]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
mgr/dashboard: add enhancements for pool rulest
authorPedro Gonzalez Gomez <pegonzal@redhat.com>
Tue, 1 Apr 2025 20:04:16 +0000 (22:04 +0200)
committerPedro Gonzalez Gomez <pegonzal@redhat.com>
Wed, 2 Apr 2025 12:10:39 +0000 (14:10 +0200)
On EC pool:
- Use host count instead of device count for host crush-failure-domain
- Host warning for k+m+1
On replicated:
- Set 'All devices' as default

Fixes: https://tracker.ceph.com/issues/70764
Signed-off-by: Pedro Gonzalez Gomez <pegonzal@redhat.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/crush-rule-form-modal/crush-rule-form-modal.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/crush-rule-form-modal/crush-rule-form-modal.component.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form-modal.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form-modal.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/classes/crush.node.selection.class.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/models/erasure-code-profile.ts

index 2b8c9e5cfe5e76c16cdac41ab16a486463977e43..598a0bab1750eb7115b7d41429c2499d8f7457a2 100644 (file)
@@ -139,6 +139,7 @@ describe('CrushRuleFormComponent', () => {
     });
 
     it('should select all values automatically by selecting "ssd-host" as root', () => {
+      formHelper.setValue('device_class', 'ssd', true);
       assert.valuesOnRootChange('ssd-host', 'osd', 'ssd');
     });
 
@@ -149,7 +150,9 @@ describe('CrushRuleFormComponent', () => {
 
     it('should override automatic selections', () => {
       assert.formFieldValues(get.nodeByName('default'), 'osd-rack', '');
+      formHelper.setValue('device_class', 'ssd', true);
       assert.valuesOnRootChange('ssd-host', 'osd', 'ssd');
+      formHelper.setValue('device_class', '', true);
       assert.valuesOnRootChange('mix-host', 'osd-rack', '');
     });
 
@@ -160,6 +163,7 @@ describe('CrushRuleFormComponent', () => {
     });
 
     it('should preselect device by domain selection', () => {
+      formHelper.setValue('device_class', 'ssd', true);
       formHelper.setValue('failure_domain', 'osd', true);
       assert.formFieldValues(get.nodeByName('default'), 'osd', 'ssd');
     });
@@ -203,6 +207,7 @@ describe('CrushRuleFormComponent', () => {
     });
 
     it('creates a rule with all fields', () => {
+      formHelper.setValue('device_class', 'ssd', true);
       assert.valuesOnRootChange('ssd-host', 'osd', 'ssd');
       assert.creation(Mocks.getCrushRuleConfig('ssd-host-rule', 'ssd-host', 'osd', 'ssd'));
     });
index 0afb2442735cf8fe2ab5a89a91c10e8adeb277a9..f30b61ea5e5f3199a2d6bf94f8b70b74ada151b8 100644 (file)
@@ -76,7 +76,8 @@ export class CrushRuleFormModalComponent extends CrushNodeSelectionClass impleme
           nodes,
           this.form.get('root'),
           this.form.get('failure_domain'),
-          this.form.get('device_class')
+          this.form.get('device_class'),
+          false
         );
         this.names = names;
       });
index 58f46ae230450e53126e4e0aa88f8bc3794d7fda..0d74214cc514c58121651e6625c9f3140500b71f 100644 (file)
                   *ngIf="form.showError('k', frm, 'min')"
                   i18n>Must be equal to or greater than 2.</span>
             <span class="invalid-feedback"
-                  *ngIf="form.showError('k', frm, 'max')"
+                  *ngIf="form.showError('k', frm, 'max') && form.getValue('crushFailureDomain') === CrushFailureDomains.Osd"
                   i18n>Chunks (k+m) have exceeded the available OSDs of {{deviceCount}}.</span>
+            <span class="invalid-feedback"
+                  *ngIf="form.showError('k', frm, 'max') && form.getValue('crushFailureDomain') === CrushFailureDomains.Host"
+                  i18n>Chunks (k+m+1) have exceeded the available hosts of {{deviceCount}}.</span>
             <span class="invalid-feedback"
                   *ngIf="form.showError('k', frm, 'unequal')"
                   i18n>For an equal distribution k has to be a multiple of (k+m)/l.</span>
                   *ngIf="form.showError('m', frm, 'min')"
                   i18n>Must be equal to or greater than 1.</span>
             <span class="invalid-feedback"
-                  *ngIf="form.showError('m', frm, 'max')"
+                  *ngIf="form.showError('m', frm, 'max') && form.getValue('crushFailureDomain') ===  CrushFailureDomains.Osd"
                   i18n>Chunks (k+m) have exceeded the available OSDs of {{deviceCount}}.</span>
+            <span class="invalid-feedback"
+                  *ngIf="form.showError('m', frm, 'max') && form.getValue('crushFailureDomain') ===  CrushFailureDomains.Host"
+                  i18n>Chunks (k+m+1) have exceeded the available hosts of {{deviceCount}}.</span>
           </div>
         </div>
 
             <select class="form-select"
                     id="crushFailureDomain"
                     name="crushFailureDomain"
-                    formControlName="crushFailureDomain">
+                    formControlName="crushFailureDomain"
+                    (change)="onCrushFailureDomainChane()">
               <option *ngIf="!failureDomains"
                       ngValue=""
                       i18n>Loading...</option>
index b78b5d483f5207da128a1cb60ecdeec2b3761ab0..43433173191b9dbb609feaf422588a18352d33bb 100644 (file)
@@ -11,7 +11,7 @@ import { CdFormBuilder } from '~/app/shared/forms/cd-form-builder';
 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
 import { CdValidators } from '~/app/shared/forms/cd-validators';
 import { CrushNode } from '~/app/shared/models/crush-node';
-import { ErasureCodeProfile } from '~/app/shared/models/erasure-code-profile';
+import { ErasureCodeProfile, CrushFailureDomains } from '~/app/shared/models/erasure-code-profile';
 import { FinishedTask } from '~/app/shared/models/finished-task';
 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
 
@@ -47,6 +47,8 @@ export class ErasureCodeProfileFormModalComponent
   lrcGroups: number;
   lrcMultiK: number;
 
+  public CrushFailureDomains = CrushFailureDomains;
+
   constructor(
     private formBuilder: CdFormBuilder,
     public activeModal: NgbActiveModal,
@@ -144,9 +146,12 @@ export class ErasureCodeProfileFormModalComponent
 
   private baseValueValidation(dataChunk: boolean = false): boolean {
     return this.validValidation(() => {
+      const kMSum =
+        this.form.get('crushFailureDomain').value === CrushFailureDomains.Host
+          ? this.getKMSum() + 1
+          : this.getKMSum();
       return (
-        this.getKMSum() > this.deviceCount &&
-        this.form.getValue('k') > this.form.getValue('m') === dataChunk
+        kMSum > this.deviceCount && this.form.getValue('k') > this.form.getValue('m') === dataChunk
       );
     });
   }
@@ -469,4 +474,9 @@ export class ErasureCodeProfileFormModalComponent
     const value = this.form.getValue(name);
     ecp[differentApiAttributes[name] || name] = name === 'crushRoot' ? value.name : value;
   }
+
+  onCrushFailureDomainChane() {
+    this.form.get('k').updateValueAndValidity();
+    this.form.get('m').updateValueAndValidity();
+  }
 }
index ec8b05232886bbda0d2a0551de2354cb14307092..fb1fd535ec7dbe1de51758ac9ac8856609f2a76d 100644 (file)
@@ -3,6 +3,7 @@ import { AbstractControl } from '@angular/forms';
 import _ from 'lodash';
 
 import { CrushNode } from '../models/crush-node';
+import { CrushFailureDomains } from '../models/erasure-code-profile';
 
 export class CrushNodeSelectionClass {
   private nodes: CrushNode[] = [];
@@ -207,19 +208,26 @@ export class CrushNodeSelectionClass {
   }
 
   private updateDevices(failureDomain: string = this.controls.failure.value) {
-    const subNodes = _.flatten(
-      this.failureDomains[failureDomain].map((node) =>
-        CrushNodeSelectionClass.getSubNodes(node, this.idTree)
-      )
-    );
-    this.allDevices = subNodes.filter((n) => n.device_class).map((n) => n.device_class);
-    this.devices = _.uniq(this.allDevices).sort();
-    const device =
-      this.devices.length === 1
-        ? this.devices[0]
-        : this.getIncludedCustomValue(this.controls.device, this.devices);
-    if (this.autoDeviceUpdate) this.silentSet(this.controls.device, device);
-    this.onDeviceChange(device);
+    if (failureDomain === CrushFailureDomains.Host) {
+      this.allDevices = this.failureDomains[failureDomain]
+        .filter((fD) => fD.type)
+        .map((fD) => fD.type);
+      this.onDeviceChange('');
+    } else {
+      const subNodes = _.flatten(
+        this.failureDomains[failureDomain].map((node) =>
+          CrushNodeSelectionClass.getSubNodes(node, this.idTree)
+        )
+      );
+      this.allDevices = subNodes.filter((n) => n.device_class).map((n) => n.device_class);
+      this.devices = _.uniq(this.allDevices).sort();
+      const device =
+        this.devices.length === 1
+          ? this.devices[0]
+          : this.getIncludedCustomValue(this.controls.device, this.devices);
+      if (this.autoDeviceUpdate) this.silentSet(this.controls.device, device);
+      this.onDeviceChange(device);
+    }
   }
 
   private onDeviceChange(deviceType: string = this.controls.device.value) {
index c5e744632acd36aaea50cfe6e65ba07c7205266d..ee8685d1114e3d93d425bc8ff28d4afdf786a44e 100644 (file)
@@ -17,3 +17,8 @@ export class ErasureCodeProfile {
   'crush-device-class'?: string;
   'directory'?: string;
 }
+
+export enum CrushFailureDomains {
+  Osd = 'osd',
+  Host = 'host'
+}