]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: Tiering form - Placement Target in Advanced Section
authorDnyaneshwari <dnyaneshwari@li-9c9fbecc-2d5c-11b2-a85c-e2a7cc8a424f.ibm.com>
Wed, 20 Aug 2025 04:46:21 +0000 (10:16 +0530)
committerDnyaneshwari <dnyaneshwari@li-9c9fbecc-2d5c-11b2-a85c-e2a7cc8a424f.ibm.com>
Wed, 24 Sep 2025 08:24:01 +0000 (13:54 +0530)
Fixes: https://tracker.ceph.com/issues/72545
Signed-off-by: Dnyaneshwari Talwekar <dtalweka@redhat.com>
(cherry picked from commit aa3bb8adddac675ea3c6dcd0bd4e9143743124b8)

src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-migrate/rgw-multisite-migrate.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-zone-form/rgw-multisite-zone-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-multisite-zonegroup-form/rgw-multisite-zonegroup-form.component.html
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-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.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/rgw/rgw-storage-class-list/rgw-storage-class-list.component.ts

index 1277af79362acd42c94fce74facc1580c62cafec..e9d1c7e6926686c9bbaf048e1591028da35de2e0 100644 (file)
@@ -58,7 +58,7 @@
         <div class="cd-col-form-input">
           <input class="form-control"
                  type="text"
-                 placeholder="e.g, http://ceph-node-00.com:80"
+                 placeholder="http://ceph-node-00.com:80"
                  id="zonegroup_endpoints"
                  name="zonegroup_endpoints"
                  formControlName="zonegroup_endpoints">
@@ -97,7 +97,7 @@
         <div class="cd-col-form-input">
           <input class="form-control"
                  type="text"
-                 placeholder="e.g, http://ceph-node-00.com:80"
+                 placeholder="http://ceph-node-00.com:80"
                  id="zone_endpoints"
                  name="zone_endpoints"
                  formControlName="zone_endpoints">
index c6a001be3fb004301093d2eb3d390db37469713b..f2b8cf2d5b5adcef81827121c9a68c1f51bd0acc 100644 (file)
@@ -97,7 +97,7 @@
         <div class="cd-col-form-input">
           <input class="form-control"
                  type="text"
-                 placeholder="e.g, http://ceph-node-00.com:80"
+                 placeholder="http://ceph-node-00.com:80"
                  id="zone_endpoints"
                  name="zone_endpoints"
                  formControlName="zone_endpoints">
index 196426bc5b60464a438c971f5a2e3d26c66b63f7..88969a237c6b1143d907815b0f77cf6992479046 100644 (file)
@@ -91,7 +91,7 @@
         <div class="cd-col-form-input">
           <input class="form-control"
                  type="text"
-                 placeholder="e.g, http://ceph-node-00.com:80"
+                 placeholder="http://ceph-node-00.com:80"
                  id="zonegroup_endpoints"
                  name="zonegroup_endpoints"
                  formControlName="zonegroup_endpoints">
index 701ae11f27fd87920986658326c4c82bff16d130..1a988d3407b1fff96314090d57c5f19bb66ff9df 100644 (file)
@@ -7,34 +7,7 @@
         data-testid="rgw-storage-details"
       >
         <tbody>
-          @if( isTierMatch( TIER_TYPE_DISPLAY.LOCAL )){
-          <tr>
-            <td class="bold"
-                i18n>
-              Zone Group
-              <cd-helper class="text-pre-wrap">
-                <span>
-                  {{ zoneGroupText }}
-                </span>
-              </cd-helper>
-            </td>
-            <td>{{ selection?.zonegroup_name }}</td>
-          </tr>
-          } @if(isTierMatch( TIER_TYPE_DISPLAY.LOCAL )){
-          <tr>
-            <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>
-          } @if(isTierMatch( TIER_TYPE_DISPLAY.CLOUD_TIER, TIER_TYPE_DISPLAY.GLACIER)){
+        @if(isTierMatch( TIER_TYPE_DISPLAY.CLOUD_TIER, TIER_TYPE_DISPLAY.GLACIER)){
           <tr>
             <td class="bold"
                 i18n>
               </div>
             </td>
           </tr>
-          <tr>
-            <td class="bold"
-                i18n>
-              Secret key
-              <cd-helper class="text-pre-wrap">
-                <span> {{ targetSecretKeyText }} </span>
-              </cd-helper>
-            </td>
-            <td>
-              <div cdsCol
-                   [columnNumbers]="{ md: 4 }"
-                   class="d-flex">
-                <input
-                  cdsPassword
-                  type="password"
-                  readonly
-                  id="secret"
-                  [value]="selection?.secret"
-                />
-                <button type="button"
-                        class="btn btn-light"
-                        cdPasswordButton="secret"></button>
-                <cd-copy-2-clipboard-button source="secret"> </cd-copy-2-clipboard-button>
-              </div>
-            </td>
-          </tr>
+        }
+        @if(isTierMatch( TIER_TYPE_DISPLAY.CLOUD_TIER, TIER_TYPE_DISPLAY.GLACIER)){
           <tr>
             <td class="bold"
                 i18n>
             </td>
             <td>{{ selection?.multipart_sync_threshold | dimlessBinary }}</td>
           </tr>
-          } @if(selection?.acl_mappings.length > 0) {
+          } @if(selection?.acl_mappings?.length > 0) {
           <tr>
             <td class="bold w-25"
                 i18n>ACLs</td>
             </td>
           </tr>
          }
+          <tr>
+            <td class="bold">
+              Placement Target
+              <cd-helper class="text-pre-wrap">
+                <span i18n>
+                  Placement Target defines the destination and rules for moving objects between
+                  storage tiers.
+                </span>
+              </cd-helper>
+            </td>
+            <td>{{ selection?.placement_target }}</td>
+          </tr>
         </tbody>
       </table>
     </cds-tab>
index 138bc4131ecf245aad83ddcb70406f24ed983e0e..aa65fc210d060bed3683f3df5d7195a0aedf18f9 100644 (file)
           </option>
         </cds-select>
         <ng-template #storageError>
-          <span
-            class="invalid-feedback"
-            *ngIf="storageClassForm.showError('storageClassType', formDir, 'required')"
-            i18n
-            >This field is required.</span
-          >
+          @if (storageClassForm.showError('storageClassType', formDir, 'required')) {
+          <span class="invalid-feedback"
+                i18n> This field is required. </span>
+          }
         </ng-template>
       </div>
       <div class="form-item form-item-append"
            cdsRow>
+        <!-- Storage Class Name -->
+        <div cdsCol>
+          <cds-text-label
+            labelInputID="storage_class"
+            i18n
+            [disabled]="editing"
+            [invalid]="
+              storageClassForm.controls.storage_class.invalid &&
+              storageClassForm.controls.storage_class.dirty
+            "
+            [invalidText]="storageClassError"
+          >
+            Storage Class name
+            <input
+              cdsText
+              type="text"
+              formControlName="storage_class"
+              [invalid]="storageClassForm.showError('storage_class', formDir, 'required')"
+            />
+          </cds-text-label>
+          <ng-template #storageClassError>
+            @if (storageClassForm.showError('storage_class', formDir, 'required')) {
+            <span class="invalid-feedback"
+                  i18n>This field is required.</span>
+            }
+          </ng-template>
+        </div>
+        <!-- Zone Group / Region -->
         <div cdsCol>
-          <!-- Zone Group -->
           <cds-select
-            label="Zone Group Name"
+            label="Zone Group / Region"
             i18n-label
             formControlName="zonegroup"
             id="zonegroup"
             </option>
           </cds-select>
           <ng-template #zonegroupError>
-            <span
-              class="invalid-feedback"
-              *ngIf="storageClassForm.showError('zonegroup', formDir, 'required')"
-              i18n
-              >This field is required.</span
-            >
+            @if (storageClassForm.showError('zonegroup', formDir, 'required')) {
+            <span class="invalid-feedback"
+                  i18n>This field is required.</span>
+            }
           </ng-template>
         </div>
-        <div cdsCol>
-          <!-- Placement Target -->
-          <cds-select
-            label="Placement Target"
-            i18n-label
-            formControlName="placement_target"
-            id="placement_target"
-            [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
-            >
-              {{ placementTarget }}
-            </option>
-          </cds-select>
-          <ng-template #placementError>
-            <span
-              class="invalid-feedback"
-              *ngIf="storageClassForm.showError('placement_target', formDir, 'required')"
-              i18n
-              >This field is required.</span
-            >
-          </ng-template>
-        </div>
-      </div>
-      <!-- Storage Class -->
-      <div class="form-item">
-        <cds-text-label
-          labelInputID="storage_class"
-          i18n
-          [disabled]="editing"
-          [invalid]="
-            storageClassForm.controls.storage_class.invalid &&
-            storageClassForm.controls.storage_class.dirty
-          "
-          [invalidText]="storageClassError"
-          >Name
-          <input
-            cdsText
-            type="text"
-            id="storage_class"
-            formControlName="storage_class"
-            [invalid]="storageClassForm.showError('storage_class', formDir, 'required')"
-          />
-        </cds-text-label>
-        <ng-template #storageClassError>
-          <span
-            class="invalid-feedback"
-            *ngIf="storageClassForm.showError('storage_class', formDir, 'required')"
-            i18n
-            >This field is required.</span
-          >
-        </ng-template>
       </div>
       @if( isTierMatch( TIER_TYPE.CLOUD_TIER, TIER_TYPE.GLACIER )){
       <div>
               />
             </cds-text-label>
             <ng-template #regionError>
-              <span
-                class="invalid-feedback"
-                *ngIf="storageClassForm.showError('region', formDir, 'required')"
-                i18n
-                >This field is required.</span
-              >
+              @if (storageClassForm.showError('region', formDir, 'required')) {
+              <span class="invalid-feedback"
+                    i18n>This field is required.</span>
+              }
             </ng-template>
           </div>
           <div cdsCol>
             <!-- Target Endpoint -->
             <cds-text-label
+              labelInputID="target_endpoint"
               i18n
-              [invalid]="storageClassForm.showError('target_endpoint', formDir, 'required') || storageClassForm.showError('target_endpoint', formDir, 'invalidUrl')"
-
-              [invalidText]="
-                            storageClassForm.controls['target_endpoint'].errors?.['required'] ? 'This field is required.' :
-                            storageClassForm.controls['target_endpoint'].errors?.['invalidUrl'] ? 'Please enter a valid URL.' : ''
-                          "
-              i18n-invalidText
+              [invalid]="
+                storageClassForm.showError('target_endpoint', formDir, 'invalidURL') ||
+                storageClassForm.showError('target_endpoint', formDir, 'required')
+              "
+              [invalidText]="endpointError"
+              [helperText]="helpTextLabels.targetEndpointText"
               >Target Endpoint
               <input
                 cdsText
+                type="text"
+                placeholder="http://ceph-node-00.com:80"
+                i18n-placeholder
+                id="target_endpoint"
                 formControlName="target_endpoint"
-                placeholder="e.g. 192.168.0.10, 192.168.1.0/8"
-                 [invalid]="storageClassForm.showError('target_endpoint', formDir, 'required') || storageClassForm.showError('target_endpoint', formDir, 'invalidUrl')"
+                [invalid]="
+                  storageClassForm.showError('target_endpoint', formDir, 'invalidURL') ||
+                  storageClassForm.showError('target_endpoint', formDir, 'required')
+                "
+                [invalidText]="endpointError"
               />
             </cds-text-label>
+            <ng-template #endpointError>
+              @if (storageClassForm.showError('target_endpoint', formDir, 'required')) {
+              <span class="invalid-feedback"
+                    i18n> This field is required. </span>
+              } @else if (storageClassForm.showError('target_endpoint', formDir, 'invalidURL')) {
+              <span class="invalid-feedback"
+                    i18n> Please enter a valid URL.</span>
+              }
+            </ng-template>
           </div>
         </div>
         <!-- Access Key  -->
                 [invalid]="storageClassForm.showError('access_key', formDir, 'required')"
               />
             </cds-password-label>
-            <cd-copy-2-clipboard-button class="clipboard"> </cd-copy-2-clipboard-button>
+            <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
-              >
+              @if (storageClassForm.showError('access_key', formDir, 'required')) {
+              <span class="invalid-feedback"
+                    i18n> This field is required. </span>
+              }
             </ng-template>
           </div>
         </div>
             </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
-              >
+              @if (storageClassForm.showError('secret_key', formDir, 'required')) {
+              <span class="invalid-feedback"
+                    i18n> This field is required. </span>
+              }
             </ng-template>
           </div>
         </div>
             />
           </cds-text-label>
           <ng-template #targetError>
-            <span
-              class="invalid-feedback"
-              *ngIf="storageClassForm.showError('target_path', formDir, 'required')"
-              i18n
-              >This field is required.</span
-            >
+            @if (storageClassForm.showError('target_path', formDir, 'required')) {
+            <span class="invalid-feedback"
+                  i18n> This field is required. </span>
+            }
           </ng-template>
         </div>
         <div class="form-item">
               [invalidText]="readThroughError"
             ></cds-number>
             <ng-template #readThroughError>
-              <span
-                class="invalid-feedback"
-                *ngIf="storageClassForm.showError('read_through_restore_days', formDir, 'pattern')"
-                i18n
-                >The entered value must be a positive integer.</span
-              >
-              <span
-                class="invalid-feedback"
-                *ngIf="storageClassForm.showError('read_through_restore_days', formDir, 'lockDays')"
-                i18n
-                >ReadThrough Restore Days must be positive.</span
-              >
+              @if (storageClassForm.showError('read_through_restore_days', formDir, 'pattern')) {
+              <span class="invalid-feedback"
+                    i18n>
+                ReadThrough Restore Days must be positive.
+              </span>
+              }
             </ng-template>
           </div>
           <div cdsCol>
                       i18n>Expedited</option>
             </cds-select>
             <ng-template #glacierError>
-              <span
-                class="invalid-feedback"
-                *ngIf="storageClassForm.showError('glacier_restore_tier_type', formDir, 'required')"
-                i18n
-                >This field is required.</span
-              >
+              @if (storageClassForm.showError('glacier_restore_tier_type', formDir, 'required')) {
+              <span class="invalid-feedback"
+                    i18n> This field is required. </span>
+              }
             </ng-template>
           </div>
           <div cdsCol>
             >
             </cds-number>
             <ng-template #glacierRestoreError>
-              <span
-                class="invalid-feedback"
-                *ngIf="storageClassForm.showError('glacier_restore_days', formDir, 'pattern')"
-                i18n
-                >The entered value must be a positive integer.</span
-              >
-              <span
-                class="invalid-feedback"
-                *ngIf="storageClassForm.showError('glacier_restore_days', formDir, 'lockDays')"
-                i18n
-                >Glacier Restore Days must be positive.</span
-              >
+              @if (storageClassForm.showError('glacier_restore_days', formDir, 'pattern')) {
+              <span class="invalid-feedback"
+                    i18n>
+                The entered value must be a positive integer.
+              </span>
+              }
             </ng-template>
           </div>
         </div>
       </div>
-      } @if( isTierMatch( TIER_TYPE.CLOUD_TIER, TIER_TYPE.GLACIER )){
+      }
       <fieldset>
         <div>
           <ng-template #title>
                 id="advanced-fieldset"
                 (selected)="showAdvanced = !showAdvanced"
               >
+                @if( isTierMatch( TIER_TYPE.CLOUD_TIER, TIER_TYPE.GLACIER )){
                 <!-- Multi Part Sync Threshold -->
                 <div class="form-item form-item-append"
                      cdsRow>
                     }
                   </ng-container>
                 </div>
+                }
+                <div class="form-item form-item-append"
+                     cdsRow>
+                  <div cdsCol>
+                    <!-- Placement Target -->
+                    <cds-select
+                      label="Placement Target"
+                      i18n-label
+                      formControlName="placement_target"
+                      [invalid]="
+                        storageClassForm.showError('placement_target', formDir, 'required')
+                      "
+                      [invalidText]="placementError"
+                    >
+                      <option [value]="''"
+                              i18n>--Select--</option>
+                      @for (placementTarget of placementTargets; track placementTarget) {
+                      <option
+                        [value]="placementTarget"
+                        [selected]="
+                          placementTarget === storageClassForm.getValue('placement_target')
+                        "
+                        i18n
+                      >
+                        {{ placementTarget }}
+                      </option>
+                      }
+                    </cds-select>
+                    <ng-template #placementError>
+                      @if (storageClassForm.showError('placement_target', formDir, 'required')) {
+                      <span class="invalid-feedback"
+                            i18n> This field is required. </span>
+                      }
+                    </ng-template>
+                  </div>
+                </div>
               </cds-accordion-item>
             </cds-accordion>
           </fieldset>
         </div>
       </fieldset>
-      @if( isTierMatch( TIER_TYPE.CLOUD_TIER, TIER_TYPE.GLACIER )){
+      @if( isTierMatch( TIER_TYPE.CLOUD_TIER, TIER_TYPE.GLACIER )){
       <cd-alert-panel type="warning"
                       spacingClass="mb-2">
         <span i18n>RGW service would be restarted after creating the storage class.</span>
index 4a81ef203c3f31f128a11d25a8e5a4c7976bb1bc..15a9bb7b4c2c4b4b4770d7d8f1a5d7d129c19c04 100644 (file)
@@ -184,26 +184,29 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
             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,
             restore_storage_class: this.tierTargetInfo?.val?.restore_storage_class,
             read_through_restore_days: this.tierTargetInfo?.val?.read_through_restore_days,
             acl_mappings: this.tierTargetInfo?.val?.s3?.acl_mappings || []
           });
-          this.acls?.clear();
-          if (aclMappings.length > 0) {
-            aclMappings.forEach((acl) => {
-              this.acls?.push(
-                this.formBuilder.group({
-                  source_id: [acl.val?.source_id || ''],
-                  dest_id: [acl.val?.dest_id || ''],
-                  type: [acl.val?.type || AclTypeConst.ID, Validators.required]
-                })
-              );
-            });
-          } else {
-            this.addAcls();
+          if (
+            this.storageClassForm.get('storageClassType')?.value === TIER_TYPE.CLOUD_TIER ||
+            this.storageClassForm.get('storageClassType')?.value === TIER_TYPE.GLACIER
+          ) {
+            this.acls?.clear();
+            if (aclMappings.length > 0) {
+              aclMappings.forEach((acl) => {
+                this.acls?.push(
+                  this.formBuilder.group({
+                    source_id: [acl.val?.source_id || ''],
+                    dest_id: [acl.val?.dest_id || ''],
+                    type: [acl.val?.type || AclTypeConst.ID, Validators.required]
+                  })
+                );
+              });
+            } else {
+              this.addAcls();
+            }
           }
           if (this.tierTargetInfo?.val?.tier_type == TIER_TYPE.GLACIER) {
             let glacierResponse = this.tierTargetInfo?.val['s3-glacier'];
@@ -221,7 +224,6 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
       this.onAllowReadThroughChange(value);
     });
   }
-
   createForm() {
     const self = this;
 
@@ -288,7 +290,13 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
       ),
       allow_read_through: new FormControl(false),
       storageClassType: new FormControl(TIER_TYPE.LOCAL, Validators.required),
-      acls: new FormArray([this.createAcls()])
+      acls: new FormArray([])
+    });
+    this.storageClassForm.get('storageClassType')?.valueChanges.subscribe((type: string) => {
+      if (type === TIER_TYPE.CLOUD_TIER) {
+        const aclsArray = this.storageClassForm.get('acls') as FormArray;
+        aclsArray.push(this.createAcls());
+      }
     });
   }
 
@@ -541,7 +549,6 @@ 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;
@@ -560,8 +567,8 @@ export class RgwStorageClassFormComponent extends CdForm implements OnInit {
     this.removedAclSourceIds.forEach((sourceId: string, index: number) => {
       tier_config_rm[`acls[${index}].source_id`] = sourceId;
     });
-    if (this.aclList.length > rawFormValue.acls.length) {
-      this.aclList.forEach((acl: ACL, index: number) => {
+    if (this.aclList?.length > rawFormValue.acls?.length) {
+      this.aclList?.forEach((acl: ACL, index: number) => {
         const sourceId = acl?.val?.source_id;
         const ifExist = removeAclList.find((acl: ACLVal) => acl?.source_id === sourceId);
 
index fbe7dd9d48d0a35723e430ec94b4938b57ed8f33..9cd8abf13aaedc3fe2f7c0f8a323fa747653c8fa 100644 (file)
@@ -60,7 +60,7 @@ export class RgwStorageClassListComponent extends ListWithDetails implements OnI
         isHidden: true
       },
       {
-        name: $localize`Storage Class`,
+        name: $localize`Storage Class name`,
         prop: 'storage_class',
         flexGrow: 2
       },
@@ -74,11 +74,6 @@ export class RgwStorageClassListComponent extends ListWithDetails implements OnI
         prop: 'zonegroup_name',
         flexGrow: 2
       },
-      {
-        name: $localize`Placement Target`,
-        prop: 'placement_target',
-        flexGrow: 2
-      },
       {
         name: $localize`Target Region`,
         prop: 'region',