]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Local Storage Class - create and list 64061/head
authorDnyaneshwari <dnyaneshwari@li-9c9fbecc-2d5c-11b2-a85c-e2a7cc8a424f.ibm.com>
Thu, 22 May 2025 07:08:25 +0000 (12:38 +0530)
committerDnyaneshwari <dnyaneshwari@li-9c9fbecc-2d5c-11b2-a85c-e2a7cc8a424f.ibm.com>
Fri, 4 Jul 2025 08:16:17 +0000 (13:46 +0530)
Fixes: https://tracker.ceph.com/issues/71460
Signed-off-by: Dnyaneshwari Talwekar <dtalwekar@redhat.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/models/rgw-storage-class.model.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-details/rgw-storage-class-details.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-details/rgw-storage-class-details.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-form/rgw-storage-class-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-list/rgw-storage-class-list.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-list/rgw-storage-class-list.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/utils/rgw-bucket-tiering.ts
src/pybind/mgr/dashboard/services/rgw_client.py

index 3f5523dcb96df75dbeb1952e53caed1de595bd12..cc7f24fcb888b75846008d6d27baebc6b077a085 100644 (file)
@@ -5,10 +5,10 @@ export interface ZoneGroupDetails {
 }
 
 export interface StorageClass {
-  storage_class: string;
-  endpoint: string;
-  region: string;
   placement_target: string;
+  storage_class?: string;
+  endpoint?: string;
+  region?: string;
   zonegroup_name?: string;
 }
 
@@ -24,18 +24,31 @@ export interface StorageClassDetails {
 }
 
 export interface TierTarget {
+  key: string;
   val: {
     storage_class: string;
     tier_type: string;
     retain_head_object: boolean;
     allow_read_through: boolean;
-    s3: S3Details;
+    s3?: S3Details;
   };
 }
 
 export interface Target {
   name: string;
   tier_targets: TierTarget[];
+  storage_classes?: string[];
+}
+
+export interface StorageClassDetails {
+  target_path: string;
+  access_key: string;
+  secret: string;
+  multipart_min_part_size: number;
+  multipart_sync_threshold: number;
+  host_style: string;
+  zonegroup_name?: string;
+  placement_targets?: string;
 }
 
 export interface ZoneGroup {
@@ -66,8 +79,8 @@ export interface RequestModel {
 export interface PlacementTarget {
   tags: string[];
   placement_id: string;
-  tier_type: typeof CLOUD_TIER;
-  tier_config: {
+  tier_type?: TIER_TYPE;
+  tier_config?: {
     endpoint: string;
     access_key: string;
     secret: string;
@@ -83,7 +96,11 @@ export interface PlacementTarget {
   tier_targets?: TierTarget[];
 }
 
-export const CLOUD_TIER = 'cloud-s3';
+export const TIER_TYPE = {
+  LOCAL: 'local',
+  CLOUD_TIER: 'cloud-s3',
+  GLACIER: 'cloud-s3-glacier'
+} as const;
 
 export const DEFAULT_PLACEMENT = 'default-placement';
 
@@ -116,3 +133,15 @@ export const RETAIN_HEAD_OBJECT_TEXT = 'Retain object metadata after transition
 export const HOST_STYLE = `The URL format for accessing the remote S3 endpoint:
   - 'Path': Use for a path-based URL
   - 'Virtual': Use for a domain-based URL`;
+
+export const LOCAL_STORAGE_CLASS_TEXT = $localize`Local storage uses on-premises or directly attached devices for data storage.`;
+
+export const CLOUDS3_STORAGE_CLASS_TEXT = $localize`Cloud S3 storage uses Amazon S3-compatible cloud services for tiering.`;
+
+export type TIER_TYPE = typeof TIER_TYPE[keyof typeof TIER_TYPE];
+
+export const TIER_TYPE_DISPLAY = {
+  LOCAL: 'Local',
+  CLOUD_TIER: 'Cloud S3',
+  GLACIER: 'Cloud S3 Glacier'
+};
index 998867d3b6f29e9ad084458e3219caec2ff061ae..13b90e2735f2ef0314fca57b0084b75abb162fdd 100644 (file)
@@ -7,22 +7,51 @@
         data-testid="rgw-storage-details"
       >
         <tbody>
-          <tr>
-            <td class="bold">
+          <tr *ngIf="selection.tier_type === TIER_TYPE_DISPLAY.LOCAL">
+            <td class="bold"
+                i18n>
+              Zone Group
+              <cd-helper class="text-pre-wrap">
+                <span>
+                  A Zone Group is a logical grouping of one or more zones that share the same data
+                  and metadata, allowing for multi-site replication and geographic distribution of
+                  data.
+                </span>
+              </cd-helper>
+            </td>
+            <td>{{ selection?.zonegroup_name }}</td>
+          </tr>
+          <tr *ngIf="selection.tier_type === TIER_TYPE_DISPLAY.LOCAL">
+            <td class="bold"
+                i18n>
+              Placement Target
+              <cd-helper class="text-pre-wrap">
+                <span>
+                  Placement Target defines the destination and rules for moving objects between
+                  storage tiers.
+                </span>
+              </cd-helper>
+            </td>
+            <td>{{ selection?.placement_target }}</td>
+          </tr>
+          <tr *ngIf="selection.tier_type === TIER_TYPE_DISPLAY.CLOUD_TIER">
+            <td class="bold"
+                i18n>
               Target Path
               <cd-helper class="text-pre-wrap">
-                <span i18n>
+                <span>
                  {{ targetPathText }}
                 </span>
               </cd-helper>
             </td>
             <td>{{ selection?.target_path }}</td>
           </tr>
-          <tr>
-            <td class="bold">
+          <tr *ngIf="selection.tier_type === TIER_TYPE_DISPLAY.CLOUD_TIER">
+            <td class="bold"
+                i18n>
               Access key
               <cd-helper class="text-pre-wrap">
-                <span i18n>
+                <span>
                   {{ targetAccessKeyText }}
                 </span>
               </cd-helper>
               </div>
             </td>
           </tr>
-          <tr>
-            <td class="bold">
+          <tr *ngIf="selection.tier_type === TIER_TYPE_DISPLAY.CLOUD_TIER">
+            <td class="bold"
+                i18n>
               Secret key
               <cd-helper class="text-pre-wrap">
-                <span i18n> {{ targetSecretKeyText }} </span>
+                <span> {{ targetSecretKeyText }} </span>
               </cd-helper>
             </td>
             <td>
               </div>
             </td>
           </tr>
-          <tr>
-            <td class="bold">
+          <tr *ngIf="selection.tier_type === TIER_TYPE_DISPLAY.CLOUD_TIER">
+            <td class="bold"
+                i18n>
               Host Style
               <cd-helper class="text-pre-wrap">
-                <span i18n>{{ hostStyleText }}</span
-                >
+                <span>{{ hostStyleText }}</span>
               </cd-helper>
             </td>
             <td>{{ selection?.host_style }}</td>
           </tr>
-          <tr>
-            <td class="bold">
+          <tr *ngIf="selection.tier_type === TIER_TYPE_DISPLAY.CLOUD_TIER">
+            <td class="bold"
+                i18n>
               Multipart Minimum Part Size
               <cd-helper class="text-pre-wrap">
-                <span i18n>
+                <span>
                   {{ multipartMinPartText }}
                 </span>
               </cd-helper>
             </td>
             <td>{{ selection?.multipart_min_part_size }}</td>
           </tr>
-          <tr>
-            <td class="bold">
+          <tr *ngIf="selection.tier_type === TIER_TYPE_DISPLAY.CLOUD_TIER">
+            <td class="bold"
+                i18n>
               Multipart Sync Threshold
               <cd-helper class="text-pre-wrap">
-                <span i18n>
-                 {{ multipartSyncThreholdText }}
+                <span>
+                  {{ multipartSyncThreholdText }}
                 </span>
               </cd-helper>
             </td>
             <td>{{ selection?.multipart_sync_threshold }}</td>
           </tr>
-          <tr>
-            <td class="bold">
-              <span i18n>Allow Read Through </span>
+          <tr *ngIf="selection.tier_type === TIER_TYPE_DISPLAY.CLOUD_TIER">
+            <td class="bold"
+                i18n>
+              Retain Head Object
               <cd-helper class="text-pre-wrap">
-                <span i18n>
-                  {{ allowReadThroughText }}
+                <span>
+                  Retain object metadata after transition to the cloud (default: false).
                 </span>
               </cd-helper>
             </td>
-            <td>{{ selection?.allow_read_through }}</td>
+            <td>{{ selection?.retain_head_object }}</td>
           </tr>
-          <tr>
-            <td class="bold">
-              Head Object (Stub File)
+          <tr *ngIf="selection.tier_type === TIER_TYPE_DISPLAY.CLOUD_TIER">
+            <td class="bold"
+                i18n>
+              Allow Read Through
               <cd-helper class="text-pre-wrap">
-                <span i18n>
-                  {{ retainHeadObjectText }}
+                <span>
+                  {{ allowReadThroughText }}
                 </span>
               </cd-helper>
             </td>
-            <td>{{ selection?.retain_head_object }}</td>
+            <td>{{ selection?.allow_read_through }}</td>
           </tr>
         </tbody>
       </table>
index 14b01a140428ea0f3696799e0469053be6a86763..3a536f98f1c9dcc30fadd2838d18ac0bf12b0d1a 100644 (file)
@@ -9,9 +9,9 @@ import {
   StorageClassDetails,
   TARGET_ACCESS_KEY_TEXT,
   TARGET_PATH_TEXT,
-  TARGET_SECRET_KEY_TEXT
+  TARGET_SECRET_KEY_TEXT,
+  TIER_TYPE_DISPLAY
 } from '../models/rgw-storage-class.model';
-
 @Component({
   selector: 'cd-rgw-storage-class-details',
   templateUrl: './rgw-storage-class-details.component.html',
@@ -30,10 +30,13 @@ export class RgwStorageClassDetailsComponent implements OnChanges {
   targetSecretKeyText = TARGET_SECRET_KEY_TEXT;
   targetPathText = TARGET_PATH_TEXT;
   hostStyleText = HOST_STYLE;
+  TIER_TYPE_DISPLAY = TIER_TYPE_DISPLAY;
 
   ngOnChanges() {
     if (this.selection) {
       this.storageDetails = {
+        zonegroup_name: this.selection.zonegroup_name,
+        placement_targets: this.selection.placement_targets,
         access_key: this.selection.access_key,
         secret: this.selection.secret,
         target_path: this.selection.target_path,
index ce50c68d116320daee9c46f83721f68fe8122668..b34b48de67b5e51e8253f1128833ff9a847b2506 100644 (file)
 
         <cd-help-text [formAllFieldsRequired]="true"></cd-help-text>
       </div>
+      <div class="form-item">
+        <cds-select
+          label="Type"
+          i18n-label
+          for="storageClassType"
+          formControlName="storageClassType"
+          [helperText]="storageClassText"
+          id="storageClassType"
+          [invalid]="storageClassForm.showError('storageClassType', formDir, 'required')"
+          [invalidText]="storageError"
+        >
+          <option value=""
+                  i18n>-- Select Storage Class --</option>
+          <option value="local"
+                  i18n>Local</option>
+          <option value="cloud-s3"
+                  i18n>Cloud S3</option>
+        </cds-select>
+        <ng-template #storageError>
+          <span
+            class="invalid-feedback"
+            *ngIf="storageClassForm.showError('storageClassType', formDir, 'required')"
+            i18n
+            >This field is required.</span
+          >
+        </ng-template>
+      </div>
       <div class="form-item form-item-append"
            cdsRow>
         <div cdsCol>
             i18n-label
             formControlName="zonegroup"
             id="zonegroup"
-            [invalid]="
-              storageClassForm.showError('zonegroup', formDir, 'required')
-            "
+            [invalid]="storageClassForm.showError('zonegroup', formDir, 'required')"
             (change)="onZonegroupChange()"
             [invalidText]="zonegroupError"
           >
-            <option *ngFor="let zonegrp of zonegroupNames"
-                    [value]="zonegrp.name"
-                    [selected]="zonegrp.name === storageClassForm.getValue('zonegroup')"
-                    i18n>
+            <option
+              *ngFor="let zonegrp of zonegroupNames"
+              [value]="zonegrp.name"
+              [selected]="zonegrp.name === storageClassForm.getValue('zonegroup')"
+              i18n
+            >
               {{ zonegrp.name }}
             </option>
           </cds-select>
             i18n-label
             formControlName="placement_target"
             id="placement_target"
-            [invalid]="
-              storageClassForm.showError('placement_target', formDir, 'required')
-            "
+            [invalid]="storageClassForm.showError('placement_target', formDir, 'required')"
             [invalidText]="placementError"
           >
-          <option [value]=""
-                  i18n> --Select-- </option>
-          <option *ngFor="let placementTarget of placementTargets"
-                  [value]="placementTarget"
-                  [selected]="placementTarget === storageClassForm.getValue('placement_target')"
-                  i18n>
+            <option [value]=""
+                    i18n>--Select--</option>
+            <option
+              *ngFor="let placementTarget of placementTargets"
+              [value]="placementTarget"
+              [selected]="placementTarget === storageClassForm.getValue('placement_target')"
+              i18n
+            >
               {{ placementTarget }}
-          </option>
+            </option>
           </cds-select>
           <ng-template #placementError>
             <span
             storageClassForm.controls.storage_class.invalid &&
             storageClassForm.controls.storage_class.dirty
           "
-          [invalidText]="storageError"
-          >Storage Class Name
+          [invalidText]="storageClassError"
+          >Name
           <input
             cdsText
             type="type"
             id="storage_class"
             formControlName="storage_class"
-            [invalid]="
-            storageClassForm.showError('storage_class', formDir, 'required')
-            "
+            [invalid]="storageClassForm.showError('storage_class', formDir, 'required')"
           />
         </cds-text-label>
-        <ng-template #storageError>
+        <ng-template #storageClassError>
           <span
             class="invalid-feedback"
             *ngIf="storageClassForm.showError('storage_class', formDir, 'required')"
           >
         </ng-template>
       </div>
-      <div class="form-item form-item-append"
-           cdsRow>
-        <div cdsCol>
-          <!-- Target Region -->
-          <cds-text-label
-            labelInputID="region"
-            i18n
-            [invalid]="
-            storageClassForm.showError('region', formDir, 'required')
-            "
-            [invalidText]="regionError"
-            [helperText]="targetRegionText"
-            >Target Region
-            <input
-              cdsText
-              type="text"
-              id="region"
-              formControlName="region"
-              placeholder="e.g, us-east-1"
-              i18n-placeholder
-              [invalid]="
-              storageClassForm.showError('region', formDir, 'required')"/>
-          </cds-text-label>
-          <ng-template #regionError>
-            <span
-              class="invalid-feedback"
-              *ngIf="storageClassForm.showError('region', formDir, 'required')"
+      <div *ngIf="storageClassForm.getValue('storageClassType') === TIER_TYPE.CLOUD_TIER">
+        <div class="form-item form-item-append"
+             cdsRow>
+          <div cdsCol>
+            <!-- Target Region -->
+            <cds-text-label
+              labelInputID="region"
               i18n
-              >This field is required.</span
-            >
-          </ng-template>
+              [invalid]="storageClassForm.showError('region', formDir, 'required')"
+              [invalidText]="regionError"
+              [helperText]="targetRegionText"
+              >Target Region
+              <input
+                cdsText
+                type="text"
+                id="region"
+                formControlName="region"
+                placeholder="e.g, us-east-1"
+                i18n-placeholder
+                [invalid]="storageClassForm.showError('region', formDir, 'required')"
+              />
+            </cds-text-label>
+            <ng-template #regionError>
+              <span
+                class="invalid-feedback"
+                *ngIf="storageClassForm.showError('region', formDir, 'required')"
+                i18n
+                >This field is required.</span
+              >
+            </ng-template>
+          </div>
+          <div cdsCol>
+            <!-- Target Endpoint -->
+            <cds-text-label
+              labelInputID="endpoint"
+              i18n
+              [invalid]="storageClassForm.showError('endpoint', formDir, 'required')"
+              [invalidText]="endpointError"
+              [helperText]="targetEndpointText"
+              >Target Endpoint
+              <input
+                cdsText
+                type="text"
+                placeholder="e.g, http://ceph-node-00.com:80"
+                i18n-placeholder
+                id="endpoint"
+                formControlName="endpoint"
+                [invalid]="storageClassForm.showError('endpoint', formDir, 'required')"
+              />
+            </cds-text-label>
+            <ng-template #endpointError>
+              <span
+                class="invalid-feedback"
+                *ngIf="storageClassForm.showError('endpoint', formDir, 'required')"
+                i18n
+                >This field is required.</span
+              >
+            </ng-template>
+          </div>
         </div>
-        <div cdsCol>
-          <!-- Target Endpoint -->
-          <cds-text-label
-            labelInputID="endpoint"
-            i18n
-            [invalid]="
-            storageClassForm.showError('endpoint', formDir, 'required')
-            "
-            [invalidText]="endpointError"
-            [helperText]="targetEndpointText"
-            >Target Endpoint
-            <input
-              cdsText
-              type="text"
-              placeholder="e.g, http://ceph-node-00.com:80"
-              i18n-placeholder
-              id="endpoint"
-              formControlName="endpoint"
-              [invalid]="
-              storageClassForm.showError('endpoint', formDir, 'required')
-              "
-            />
-          </cds-text-label>
-          <ng-template #endpointError>
-            <span
-              class="invalid-feedback"
-              *ngIf="storageClassForm.showError('endpoint', formDir, 'required')"
+
+        <!-- Access Key  -->
+        <div class="form-item">
+          <div cdsCol
+               [columnNumbers]="{ md: 12 }"
+               class="d-flex">
+            <cds-password-label
+              labelInputID="access_key"
+              [invalid]="storageClassForm.showError('access_key', formDir, 'required')"
+              [invalidText]="accessError"
+              [helperText]="targetAccessKeyText"
               i18n
-              >This field is required.</span
-            >
-          </ng-template>
+              >Target Access Key
+              <input
+                cdsPassword
+                type="password"
+                id="access_key"
+                formControlName="access_key"
+                [invalid]="storageClassForm.showError('access_key', formDir, 'required')"
+              />
+            </cds-password-label>
+            <cd-copy-2-clipboard-button class="clipboard"> </cd-copy-2-clipboard-button>
+            <ng-template #accessError>
+              <span
+                class="invalid-feedback"
+                *ngIf="storageClassForm.showError('access_key', formDir, 'required')"
+                i18n
+                >This field is required.</span
+              >
+            </ng-template>
+          </div>
         </div>
-      </div>
 
-      <!-- Access Key  -->
-      <div class="form-item">
-        <div cdsCol
-             [columnNumbers]="{ md: 12 }"
-             class="d-flex">
-          <cds-password-label
-            labelInputID="access_key"
-            [invalid]="
-            storageClassForm.showError('access_key', formDir, 'required')
-            "
-            [invalidText]="accessError"
-            [helperText]="targetAccessKeyText"
-            i18n
-            >Target Access Key
-            <input
-              cdsPassword
-              type="password"
-              id="access_key"
-              formControlName="access_key"
-              [invalid]="
-              storageClassForm.showError('access_key', formDir, 'required')
-              "
-            />
-          </cds-password-label>
-          <cd-copy-2-clipboard-button class="clipboard"> </cd-copy-2-clipboard-button>
-          <ng-template #accessError>
-            <span
-              class="invalid-feedback"
-              *ngIf="storageClassForm.showError('access_key', formDir, 'required')"
+        <!-- Secret Key  -->
+        <div class="form-item">
+          <div cdsCol
+               [columnNumbers]="{ md: 12 }"
+               class="d-flex">
+            <cds-password-label
+              labelInputID="secret_key"
+              [helperText]="targetSecretKeyText"
+              [invalid]="storageClassForm.showError('secret_key', formDir, 'required')"
+              [invalidText]="secretError"
               i18n
-              >This field is required.</span
-            >
-          </ng-template>
+              >Target Secret Key
+              <input
+                cdsPassword
+                type="password"
+                id="secret_key"
+                formControlName="secret_key"
+                [invalid]="storageClassForm.showError('secret_key', formDir, 'required')"
+              />
+            </cds-password-label>
+            <cd-copy-2-clipboard-button class="clipboard"> </cd-copy-2-clipboard-button>
+            <ng-template #secretError>
+              <span
+                class="invalid-feedback"
+                *ngIf="storageClassForm.showError('secret_key', formDir, 'required')"
+                i18n
+                >This field is required.</span
+              >
+            </ng-template>
+          </div>
         </div>
-      </div>
 
-      <!-- Secret Key  -->
-      <div class="form-item">
-        <div cdsCol
-             [columnNumbers]="{ md: 12 }"
-             class="d-flex">
-          <cds-password-label
-            labelInputID="secret_key"
-            [helperText]="targetSecretKeyText"
-            [invalid]="
-            storageClassForm.showError('secret_key', formDir, 'required')
-            "
-            [invalidText]="secretError"
+        <!-- Target Path -->
+        <div class="form-item">
+          <cds-text-label
+            labelInputID="target_path"
             i18n
-            >Target Secret Key
+            [invalid]="storageClassForm.showError('target_path', formDir, 'required')"
+            [invalidText]="targetError"
+            [helperText]="targetPathText"
+            >Target Path
             <input
-              cdsPassword
-              type="password"
-              id="secret_key"
-              formControlName="secret_key"
-              [invalid]="
-              storageClassForm.showError('secret_key', formDir, 'required')
-              "
+              cdsText
+              type="text"
+              id="target_path"
+              formControlName="target_path"
+              [invalid]="storageClassForm.showError('target_path', formDir, 'required')"
             />
-          </cds-password-label>
-          <cd-copy-2-clipboard-button class="clipboard"> </cd-copy-2-clipboard-button>
-          <ng-template #secretError>
+          </cds-text-label>
+          <ng-template #targetError>
             <span
               class="invalid-feedback"
-              *ngIf="storageClassForm.showError('secret_key', formDir, 'required')"
+              *ngIf="storageClassForm.showError('target_path', formDir, 'required')"
               i18n
               >This field is required.</span
             >
           </ng-template>
         </div>
       </div>
-
-      <!-- Target Path -->
-      <div class="form-item">
-        <cds-text-label
-          labelInputID="target_path"
-          i18n
-          [invalid]="
-          storageClassForm.showError('target_path', formDir, 'required')
-          "
-          [invalidText]="targetError"
-          [helperText]="targetPathText"
-          >Target Path
-          <input
-            cdsText
-            type="text"
-            id="target_path"
-            formControlName="target_path"
-            [invalid]="
-            storageClassForm.showError('target_path', formDir, 'required')
-            "
-          />
-        </cds-text-label>
-        <ng-template #targetError>
-          <span
-            class="invalid-feedback"
-            *ngIf="storageClassForm.showError('target_path', formDir, 'required')"
-            i18n
-            >This field is required.</span
-          >
+      <div *ngIf="storageClassForm.getValue('storageClassType') === TIER_TYPE.CLOUD_TIER">
+        <ng-template #title>
+          <h5 class="cds--accordion__title cd-header">Advanced</h5>
         </ng-template>
-      </div>
-      <div class="form-item">
-        <cds-checkbox
-          id="allow_read_through"
-          formControlName="allow_read_through"
-          cdOptionalField="Allow Read Through"
-          i18n
-          (change)="onAllowReadThroughChange($event)"
-          >Allow Read Through
-          <cd-help-text>{{ allowReadThroughText }}</cd-help-text>
-        </cds-checkbox>
-      </div>
-      <div class="form-item">
-        <cds-checkbox
-          id="retain_head_object"
-          formControlName="retain_head_object"
-          cdOptionalField="Head Object (Stub File)"
-          i18n
-          >Head Object (Stub File)
-          <cd-help-text>{{ retainHeadObjectText }}</cd-help-text>
-        </cds-checkbox>
-      </div>
-
-      <fieldset>
-        <cds-accordion size="lg"
-                       class="form-item">
-          <cds-accordion-item
-            [title]="title"
-            id="advanced-fieldset"
-            (selected)="showAdvanced = !showAdvanced"
-          >
-            <!-- Multi Part Sync Threshold -->
-            <div class="form-item form-item-append"
-                 cdsRow>
-              <div cdsCol>
-                <cds-text-label
-                  labelInputID="multipart_sync_threshold"
+        <fieldset>
+          <cds-accordion size="lg"
+                         class="form-item">
+            <cds-accordion-item
+              [title]="title"
+              id="advanced-fieldset"
+              (selected)="showAdvanced = !showAdvanced"
+            >
+              <!-- Multi Part Sync Threshold -->
+              <div class="form-item form-item-append"
+                   cdsRow>
+                <div cdsCol>
+                  <cds-text-label
+                    labelInputID="multipart_sync_threshold"
+                    i18n
+                    [helperText]="multipartSyncThreholdText"
+                    cdOptionalField="Multipart Sync Threshold"
+                    >Multipart Sync Threshold
+                    <input
+                      cdsText
+                      type="text"
+                      id="multipart_sync_threshold"
+                      formControlName="multipart_sync_threshold"
+                    />
+                  </cds-text-label>
+                </div>
+                <div cdsCol>
+                  <cds-text-label
+                    labelInputID="multipart_min_part_size"
+                    i18n
+                    [helperText]="multipartMinPartText"
+                    cdOptionalField="Multipart Minimum Part Size"
+                    >Multipart Minimum Part Size
+                    <input
+                      cdsText
+                      type="text"
+                      id="multipart_min_part_size"
+                      formControlName="multipart_min_part_size"
+                    />
+                  </cds-text-label>
+                </div>
+              </div>
+              <div class="form-item">
+                <cds-checkbox
+                  id="allow_read_through"
+                  formControlName="allow_read_through"
+                  cdOptionalField="Allow Read Through"
                   i18n
-                  [helperText]="multipartSyncThreholdText"
-                  cdOptionalField="Multipart Sync Threshold"
-                  >Multipart Sync Threshold
-                  <input
-                    cdsText
-                    type="text"
-                    id="multipart_sync_threshold"
-                    formControlName="multipart_sync_threshold"
-                  />
-                </cds-text-label>
+                  (change)="onAllowReadThroughChange($event)"
+                  >Allow Read Through
+                  <cd-help-text>{{ allowReadThroughText }}</cd-help-text>
+                </cds-checkbox>
               </div>
-              <div cdsCol>
-                <cds-text-label
-                  labelInputID="multipart_min_part_size"
+              <div class="form-item">
+                <cds-checkbox
+                  id="retain_head_object"
+                  formControlName="retain_head_object"
+                  cdOptionalField="Head Object (Stub File)"
                   i18n
-                  [helperText]="multipartMinPartText"
-                  cdOptionalField="Multipart Minimum Part Size"
-                  >Multipart Minimum Part Size
-                  <input
-                    cdsText
-                    type="text"
-                    id="multipart_min_part_size"
-                    formControlName="multipart_min_part_size"
-                  />
-                </cds-text-label>
+                  >Head Object (Stub File)
+                  <cd-help-text>{{ retainHeadObjectText }}</cd-help-text>
+                </cds-checkbox>
               </div>
-            </div>
-          </cds-accordion-item>
-        </cds-accordion>
-        <ng-template #title>
-          <h5 class="cds--accordion__title cd-header">Advanced</h5>
-        </ng-template>
-      </fieldset>
-      <cd-alert-panel type="warning"
-                      spacingClass="mb-2">
+            </cds-accordion-item>
+          </cds-accordion>
+        </fieldset>
+      </div>
+      <cd-alert-panel
+        type="warning"
+        spacingClass="mb-2"
+        *ngIf="storageClassForm.getValue('storageClassType') === TIER_TYPE.CLOUD_TIER"
+      >
         <span i18n>RGW service would be restarted after creating the storage class.</span>
       </cd-alert-panel>
       <cd-form-button-panel
index 5a8d5c3d4bb2a09070e71e5ded469a523fa42cde..7417529f7b65edbbc5946038691adeb97d94e5c3 100644 (file)
@@ -68,15 +68,16 @@ describe('RgwStorageClassFormComponent', () => {
               name: 'default-placement',
               tier_targets: [
                 {
+                  key: 'test',
                   val: {
-                    storage_class: 'CLOUDIBM',
                     tier_type: 'cloud-s3',
                     retain_head_object: true,
+                    storage_class: 'CLOUDIBM',
                     allow_read_through: true,
                     s3: {
+                      storage_class: 'CLOUDIBM',
                       endpoint: 'https://s3.amazonaws.com',
                       access_key: 'ACCESSKEY',
-                      storage_class: 'STANDARD',
                       target_path: '/path/to/storage',
                       target_storage_class: 'STANDARD',
                       region: 'useastr1',
@@ -88,31 +89,6 @@ describe('RgwStorageClassFormComponent', () => {
                   }
                 }
               ]
-            },
-            {
-              name: 'placement1',
-              tier_targets: [
-                {
-                  val: {
-                    storage_class: 'CloudIBM',
-                    tier_type: 'cloud-s3',
-                    retain_head_object: true,
-                    allow_read_through: true,
-                    s3: {
-                      endpoint: 'https://s3.amazonaws.com',
-                      access_key: 'ACCESSKEY',
-                      storage_class: 'GLACIER',
-                      target_path: '/pathStorage',
-                      target_storage_class: 'CloudIBM',
-                      region: 'useast1',
-                      secret: 'SECRETKEY',
-                      multipart_min_part_size: 187988787,
-                      multipart_sync_threshold: 878787878,
-                      host_style: false
-                    }
-                  }
-                }
-              ]
             }
           ]
         }
@@ -120,7 +96,7 @@ describe('RgwStorageClassFormComponent', () => {
     };
     component.storageClassForm.get('zonegroup').setValue('zonegroup1');
     component.onZonegroupChange();
-    expect(component.placementTargets).toEqual(['default-placement', 'placement1']);
+    expect(component.placementTargets).toEqual(['default-placement']);
     expect(component.storageClassForm.get('placement_target').value).toBe('default-placement');
   });
 
index b2b8862109fc46a2db8c8c35b24e055069cfd872..04b0d4a2b03dede61786f972079ab5f063bd0d35 100644 (file)
@@ -10,7 +10,6 @@ import { RgwStorageClassService } from '~/app/shared/api/rgw-storage-class.servi
 import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
 import {
   ALLOW_READ_THROUGH_TEXT,
-  CLOUD_TIER,
   DEFAULT_PLACEMENT,
   MULTIPART_MIN_PART_TEXT,
   MULTIPART_SYNC_THRESHOLD_TEXT,
@@ -25,11 +24,15 @@ import {
   TARGET_REGION_TEXT,
   TARGET_SECRET_KEY_TEXT,
   TierTarget,
+  TIER_TYPE,
   ZoneGroup,
-  ZoneGroupDetails
+  ZoneGroupDetails,
+  CLOUDS3_STORAGE_CLASS_TEXT,
+  LOCAL_STORAGE_CLASS_TEXT
 } from '../models/rgw-storage-class.model';
 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
 import { NotificationService } from '~/app/shared/services/notification.service';
+import { CdValidators } from '~/app/shared/forms/cd-validators';
 
 @Component({
   selector: 'cd-rgw-storage-class-form',
@@ -49,6 +52,7 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
   zonegroupNames: ZoneGroup[];
   placementTargets: string[] = [];
   multipartMinPartText: string;
+  storageClassText: string;
   multipartSyncThreholdText: string;
   selectedZoneGroup: string;
   defaultZonegroup: ZoneGroup;
@@ -60,6 +64,7 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
   tierTargetInfo: TierTarget;
   allowReadThroughText: string;
   allowReadThrough: boolean = false;
+  TIER_TYPE = TIER_TYPE;
 
   constructor(
     public actionLabels: ActionLabelsI18n,
@@ -86,6 +91,8 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
     this.targetSecretKeyText = TARGET_SECRET_KEY_TEXT;
     this.retainHeadObjectText = RETAIN_HEAD_OBJECT_TEXT;
     this.allowReadThroughText = ALLOW_READ_THROUGH_TEXT;
+    this.storageClassText = LOCAL_STORAGE_CLASS_TEXT;
+    this.storageClassTypeText();
     this.createForm();
     this.loadingReady();
     this.loadZoneGroup();
@@ -100,39 +107,60 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
             placementTargetInfo,
             this.storageClassInfo.storage_class
           );
-          let response = this.tierTargetInfo.val.s3;
+          let response = this.tierTargetInfo?.val?.s3;
           this.storageClassForm.get('zonegroup').disable();
           this.storageClassForm.get('placement_target').disable();
           this.storageClassForm.get('storage_class').disable();
-          this.storageClassForm.get('zonegroup').setValue(this.storageClassInfo.zonegroup_name);
-          this.storageClassForm.get('region').setValue(response.region);
-          this.storageClassForm
-            .get('placement_target')
-            .setValue(this.storageClassInfo.placement_target);
-          this.storageClassForm.get('endpoint').setValue(response.endpoint);
-          this.storageClassForm.get('storage_class').setValue(this.storageClassInfo.storage_class);
-          this.storageClassForm.get('access_key').setValue(response.access_key);
-          this.storageClassForm.get('secret_key').setValue(response.secret);
-          this.storageClassForm.get('target_path').setValue(response.target_path);
-          this.storageClassForm
-            .get('retain_head_object')
-            .setValue(this.tierTargetInfo?.val?.retain_head_object || false);
-          this.storageClassForm
-            .get('multipart_sync_threshold')
-            .setValue(response.multipart_sync_threshold || '');
-          this.storageClassForm
-            .get('multipart_min_part_size')
-            .setValue(response.multipart_min_part_size || '');
-          this.storageClassForm
-            .get('allow_read_through')
-            .setValue(this.tierTargetInfo?.val?.allow_read_through || false);
+          this.storageClassForm.patchValue({
+            zonegroup: this.storageClassInfo?.zonegroup_name,
+            region: response?.region,
+            placement_target: this.storageClassInfo?.placement_target,
+            storageClassType: this.tierTargetInfo?.val?.tier_type ?? TIER_TYPE.LOCAL,
+            endpoint: response?.endpoint,
+            storage_class: this.storageClassInfo?.storage_class,
+            access_key: response?.access_key,
+            secret_key: response?.secret,
+            target_path: response?.target_path,
+            retain_head_object: this.tierTargetInfo?.val?.retain_head_object || false,
+            multipart_sync_threshold: response?.multipart_sync_threshold || '',
+            multipart_min_part_size: response?.multipart_min_part_size || '',
+            allow_read_through: this.tierTargetInfo?.val?.allow_read_through || false
+          });
         });
     }
+    this.storageClassForm?.get('storageClassType')?.valueChanges.subscribe((value) => {
+      const controlsToUpdate = ['region', 'endpoint', 'access_key', 'secret_key', 'target_path'];
+      controlsToUpdate.forEach((field) => {
+        const control = this.storageClassForm.get(field);
+        if (
+          value === TIER_TYPE.CLOUD_TIER &&
+          ['region', 'endpoint', 'access_key', 'secret_key', 'target_path'].includes(field)
+        ) {
+          control.setValidators([Validators.required]);
+        } else {
+          control.clearValidators();
+        }
+
+        control.updateValueAndValidity();
+      });
+    });
     this.storageClassForm.get('allow_read_through').valueChanges.subscribe((value) => {
       this.onAllowReadThroughChange(value);
     });
   }
 
+  storageClassTypeText() {
+    this.storageClassForm?.get('storageClassType')?.valueChanges.subscribe((value) => {
+      if (value === TIER_TYPE.LOCAL) {
+        this.storageClassText = LOCAL_STORAGE_CLASS_TEXT;
+      } else if (value === TIER_TYPE.CLOUD_TIER) {
+        this.storageClassText = CLOUDS3_STORAGE_CLASS_TEXT;
+      } else {
+        this.storageClassText = LOCAL_STORAGE_CLASS_TEXT;
+      }
+    });
+  }
+
   createForm() {
     this.storageClassForm = this.formBuilder.group({
       storage_class: new FormControl('', {
@@ -141,24 +169,29 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
       zonegroup: new FormControl(this.selectedZoneGroup, {
         validators: [Validators.required]
       }),
-      region: new FormControl('', {
-        validators: [Validators.required]
-      }),
+      region: new FormControl('', [
+        CdValidators.composeIf({ storageClassType: TIER_TYPE.CLOUD_TIER }, [Validators.required])
+      ]),
       placement_target: new FormControl('', {
         validators: [Validators.required]
       }),
-      endpoint: new FormControl(null, {
-        validators: [Validators.required]
-      }),
-      access_key: new FormControl(null, Validators.required),
-      secret_key: new FormControl(null, Validators.required),
-      target_path: new FormControl('', {
-        validators: [Validators.required]
-      }),
+      endpoint: new FormControl(null, [
+        CdValidators.composeIf({ storageClassType: TIER_TYPE.CLOUD_TIER }, [Validators.required])
+      ]),
+      access_key: new FormControl(null, [
+        CdValidators.composeIf({ storageClassType: TIER_TYPE.CLOUD_TIER }, [Validators.required])
+      ]),
+      secret_key: new FormControl(null, [
+        CdValidators.composeIf({ storageClassType: TIER_TYPE.CLOUD_TIER }, [Validators.required])
+      ]),
+      target_path: new FormControl('', [
+        CdValidators.composeIf({ storageClassType: TIER_TYPE.CLOUD_TIER }, [Validators.required])
+      ]),
       retain_head_object: new FormControl(true),
       multipart_sync_threshold: new FormControl(33554432),
       multipart_min_part_size: new FormControl(33554432),
-      allow_read_through: new FormControl(false)
+      allow_read_through: new FormControl(false),
+      storageClassType: new FormControl(TIER_TYPE.LOCAL, Validators.required)
     });
   }
 
@@ -268,33 +301,71 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
   }
 
   buildRequest() {
+    if (this.storageClassForm.errors) return null;
+
     const rawFormValue = _.cloneDeep(this.storageClassForm.value);
     const zoneGroup = this.storageClassForm.get('zonegroup').value;
     const storageClass = this.storageClassForm.get('storage_class').value;
     const placementId = this.storageClassForm.get('placement_target').value;
-    const headObject = this.storageClassForm.get('retain_head_object').value;
-    const requestModel: RequestModel = {
-      zone_group: zoneGroup,
-      placement_targets: [
-        {
-          tags: [],
-          placement_id: placementId,
-          storage_class: storageClass,
-          tier_type: CLOUD_TIER,
-          tier_config: {
-            endpoint: rawFormValue.endpoint,
-            access_key: rawFormValue.access_key,
-            secret: rawFormValue.secret_key,
-            target_path: rawFormValue.target_path,
-            retain_head_object: headObject,
-            allow_read_through: rawFormValue.allow_read_through,
-            region: rawFormValue.region,
-            multipart_sync_threshold: rawFormValue.multipart_sync_threshold,
-            multipart_min_part_size: rawFormValue.multipart_min_part_size
-          }
-        }
-      ]
-    };
-    return requestModel;
+    const storageClassType = this.storageClassForm.get('storageClassType').value;
+    const retain_head_object = this.storageClassForm.get('retain_head_object').value;
+
+    return this.buildPlacementTargets(
+      storageClassType,
+      zoneGroup,
+      placementId,
+      storageClass,
+      retain_head_object,
+      rawFormValue
+    );
+  }
+
+  private buildPlacementTargets(
+    storageClassType: string,
+    zoneGroup: string,
+    placementId: string,
+    storageClass: string,
+    retain_head_object: boolean,
+    rawFormValue: any
+  ): RequestModel {
+    switch (storageClassType) {
+      case TIER_TYPE.LOCAL:
+        return {
+          zone_group: zoneGroup,
+          placement_targets: [
+            {
+              tags: [],
+              placement_id: placementId,
+              storage_class: storageClass
+            }
+          ]
+        };
+
+      case TIER_TYPE.CLOUD_TIER:
+        return {
+          zone_group: zoneGroup,
+          placement_targets: [
+            {
+              tags: [],
+              placement_id: placementId,
+              storage_class: storageClass,
+              tier_type: TIER_TYPE.CLOUD_TIER,
+              tier_config: {
+                endpoint: rawFormValue.endpoint,
+                access_key: rawFormValue.access_key,
+                secret: rawFormValue.secret_key,
+                target_path: rawFormValue.target_path,
+                retain_head_object: retain_head_object,
+                allow_read_through: rawFormValue.allow_read_through,
+                region: rawFormValue.region,
+                multipart_sync_threshold: rawFormValue.multipart_sync_threshold,
+                multipart_min_part_size: rawFormValue.multipart_min_part_size
+              }
+            }
+          ]
+        };
+      default:
+        return null;
+    }
   }
 }
index cdea55a32e560fd10e00dc077c32dd9d56e49e49..696be083a660ceb1d5462062032113cc26b88357 100644 (file)
@@ -8,6 +8,8 @@
   columnMode="flex"
   [columns]="columns"
   (fetchData)="loadStorageClass()"
+  identifier="uniqueId"
+  [forceIdentifier]="true"
   selectionType="single"
   [hasDetails]="true"
   (setExpandedRow)="setExpandedRow($event)"
index e36b8acae19609b12f4168127f88efa9ce125908..714d0234314541898ecf9a83c16bcc3ba3f27cc6 100644 (file)
@@ -4,7 +4,12 @@ import { CdTableColumn } from '~/app/shared/models/cd-table-column';
 import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
 
 import { ListWithDetails } from '~/app/shared/classes/list-with-details.class';
-import { StorageClass, ZoneGroupDetails } from '../models/rgw-storage-class.model';
+import {
+  StorageClass,
+  TIER_TYPE,
+  TIER_TYPE_DISPLAY,
+  ZoneGroupDetails
+} from '../models/rgw-storage-class.model';
 import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
 import { FinishedTask } from '~/app/shared/models/finished-task';
 import { Icons } from '~/app/shared/enum/icons.enum';
@@ -17,7 +22,6 @@ import { AuthStorageService } from '~/app/shared/services/auth-storage.service';
 import { URLBuilderService } from '~/app/shared/services/url-builder.service';
 import { Permission } from '~/app/shared/models/permissions';
 import { BucketTieringUtils } from '../utils/rgw-bucket-tiering';
-
 import { Router } from '@angular/router';
 
 const BASE_URL = 'rgw/tiering';
@@ -50,11 +54,21 @@ export class RgwStorageClassListComponent extends ListWithDetails implements OnI
 
   ngOnInit() {
     this.columns = [
+      {
+        prop: 'uniqueId',
+        isInvisible: true,
+        isHidden: true
+      },
       {
         name: $localize`Storage Class`,
         prop: 'storage_class',
         flexGrow: 2
       },
+      {
+        name: $localize`Type`,
+        prop: 'tier_type',
+        flexGrow: 2
+      },
       {
         name: $localize`Zone Group`,
         prop: 'zonegroup_name',
@@ -110,7 +124,17 @@ export class RgwStorageClassListComponent extends ListWithDetails implements OnI
         (data: ZoneGroupDetails) => {
           this.storageClassList = [];
           const tierObj = BucketTieringUtils.filterAndMapTierTargets(data);
-          this.storageClassList.push(...tierObj);
+          const tierConfig = tierObj.map((item) => ({
+            ...item,
+            tier_type:
+              item.tier_type?.toLowerCase() === TIER_TYPE.CLOUD_TIER
+                ? TIER_TYPE_DISPLAY.CLOUD_TIER
+                : item.tier_type?.toLowerCase() === TIER_TYPE.LOCAL
+                ? TIER_TYPE_DISPLAY.LOCAL
+                : item.tier_type
+          }));
+          this.transformTierData(tierConfig);
+          this.storageClassList.push(...tierConfig);
           resolve();
         },
         (error) => {
@@ -120,6 +144,16 @@ export class RgwStorageClassListComponent extends ListWithDetails implements OnI
     });
   }
 
+  transformTierData(tierConfig: any[]) {
+    tierConfig.forEach((item, index) => {
+      const zone_group = item?.zone_group;
+      const storageClass = item?.storage_class;
+      const uniqueId = `${zone_group}-${storageClass}-${index}`;
+      item.uniqueId = uniqueId;
+    });
+    return tierConfig;
+  }
+
   removeStorageClassModal() {
     const storage_class = this.selection.first().storage_class;
     const placement_target = this.selection.first().placement_target;
index 9cb19b680dbce2cc503cb66e0c44efdd5a3fbe38..8b8e2076850f2116f38de111c1d588899e283c9f 100644 (file)
@@ -1,7 +1,7 @@
 import {
-  CLOUD_TIER,
   Target,
   TierTarget,
+  TIER_TYPE,
   ZoneGroup,
   ZoneGroupDetails
 } from '../models/rgw-storage-class.model';
@@ -9,27 +9,46 @@ import {
 export class BucketTieringUtils {
   static filterAndMapTierTargets(zonegroupData: ZoneGroupDetails) {
     return zonegroupData.zonegroups.flatMap((zoneGroup: ZoneGroup) =>
-      zoneGroup.placement_targets
-        .filter((target: Target) => target.tier_targets)
-        .flatMap((target: Target) =>
-          target.tier_targets
-            .filter((tierTarget: TierTarget) => tierTarget.val.tier_type === CLOUD_TIER)
-            .map((tierTarget: TierTarget) => {
-              return this.getTierTargets(tierTarget, zoneGroup.name, target.name);
-            })
-        )
+      zoneGroup.placement_targets.flatMap((target: Target) => {
+        const storage_class = new Set<string>(
+          (target.tier_targets || []).map((tier_target: TierTarget) => tier_target.key)
+        );
+        const tierTargetDetails = (target.tier_targets || []).map((tierTarget: TierTarget) =>
+          this.getTierTargets(tierTarget, zoneGroup.name, target.name)
+        );
+        const localStorageClasses = (target.storage_classes || [])
+          .filter((storageClass) => storageClass !== 'STANDARD' && !storage_class.has(storageClass))
+          .map((storageClass) => ({
+            zonegroup_name: zoneGroup.name,
+            placement_target: target.name,
+            storage_class: storageClass,
+            tier_type: TIER_TYPE.LOCAL
+          }));
+
+        return [...tierTargetDetails, ...localStorageClasses];
+      })
     );
   }
 
   private static getTierTargets(tierTarget: TierTarget, zoneGroup: string, targetName: string) {
-    if (tierTarget.val.tier_type !== CLOUD_TIER) return null;
-    return {
-      zonegroup_name: zoneGroup,
-      placement_target: targetName,
-      storage_class: tierTarget.val.storage_class,
-      retain_head_object: tierTarget.val.retain_head_object,
-      allow_read_through: tierTarget.val.allow_read_through,
-      ...tierTarget.val.s3
-    };
+    const val = tierTarget.val;
+    if (val.tier_type === TIER_TYPE.CLOUD_TIER) {
+      return {
+        zonegroup_name: zoneGroup,
+        placement_target: targetName,
+        storage_class: val.storage_class,
+        retain_head_object: val.retain_head_object,
+        allow_read_through: val.allow_read_through,
+        tier_type: val.tier_type,
+        ...val.s3
+      };
+    } else {
+      return {
+        zonegroup_name: zoneGroup,
+        placement_target: targetName,
+        storage_class: val.storage_class,
+        tier_type: TIER_TYPE.LOCAL
+      };
+    }
   }
 }
index 081c89ba23ae0f93bbd4dfb5a6574eedc3322312..0cbc0f9f8a543b48a1f81abccdc1ab534b87ccb3 100755 (executable)
@@ -2030,6 +2030,7 @@ class RgwMultisite:
                 '--placement-id', placement_target['placement_id']
             ]
             storage_class_name = placement_target.get('storage_class', None)
+            tier_type = placement_target.get('tier_type', None)
 
             if (
                 placement_target.get('tier_type') == CLOUD_S3_TIER_TYPE
@@ -2067,7 +2068,8 @@ class RgwMultisite:
                         )
                 except SubprocessError as error:
                     raise DashboardException(error, http_status_code=500, component='rgw')
-                self.ensure_realm_and_sync_period()
+                if tier_type == CLOUD_S3_TIER_TYPE:
+                    self.ensure_realm_and_sync_period()
 
             if storage_classes:
                 for sc in storage_classes:
@@ -2090,7 +2092,8 @@ class RgwMultisite:
                                 )
                         except SubprocessError as error:
                             raise DashboardException(error, http_status_code=500, component='rgw')
-                        self.ensure_realm_and_sync_period()
+                        if tier_type == CLOUD_S3_TIER_TYPE:
+                            self.ensure_realm_and_sync_period()
 
     def modify_placement_targets(self, zonegroup_name: str, placement_targets: List[Dict]):
         rgw_add_placement_cmd = ['zonegroup', 'placement', 'modify']