From: Abhishek Desai Date: Wed, 15 Apr 2026 08:45:25 +0000 (+0530) Subject: mgr/dashboard : add stretch cluster validation for pools form X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=6fa8d64ed6ee43b439e162143fb0e33cf2e49e8c;p=ceph.git mgr/dashboard : add stretch cluster validation for pools form fixes : https://tracker.ceph.com/issues/75667 Signed-off-by: Abhishek Desai --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html index 9652cf145a08..9b44da73c00b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html @@ -15,6 +15,11 @@ class="form-header" > {{ action | titlecase }} {{ resource | upperFirst }} + @if (isStretchMode) { + + Stretch cluster is enabled. Only replicated pool type is supported in Stretch mode. + + }
Pool type + @if (isStretchMode) { + + + @for (poolType of data.poolTypes; track poolType) { + + {{ poolType | upperFirst }} + + } + + + } @else { @@ -109,6 +141,7 @@ } + } @if (form.showError('poolType', formDir, 'required')) { + @if (isStretchMode) { + + The replicated size value is predefined for stretch cluster + + } @if (form.getValue('size') === 1) { } + + @if (form.showError('crushRule', formDir, 'required')) { + + This field is required! + + } +
} @else {
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts index 99466c9fe56a..f2e9df5a5efb 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts @@ -38,6 +38,7 @@ import { Pool } from '../pool'; import { PoolFormData } from './pool-form-data'; import { PoolEditModeResponseModel } from '../../block/mirroring/pool-edit-mode-modal/pool-edit-mode-response.model'; import { RbdMirroringService } from '~/app/shared/api/rbd-mirroring.service'; +import { MonitorService } from '~/app/shared/api/monitor.service'; import { ModalCdsService } from '~/app/shared/services/modal-cds.service'; interface FormFieldDescription { @@ -49,6 +50,12 @@ interface FormFieldDescription { resetValue?: any; } +interface MonitorResponse { + mon_status?: { + stretch_mode?: boolean; + }; +} + @Component({ selector: 'cd-pool-form', templateUrl: './pool-form.component.html', @@ -95,6 +102,12 @@ export class PoolFormComponent extends CdForm implements OnInit { DEFAULT_RATIO = 0.875; isApplicationsSelected = true; msrCrush: boolean = false; + isStretchMode: boolean = false; + + readonly DEFAULT_REPLICATED_MIN_SIZE = 1; + readonly DEFAULT_REPLICATED_MAX_SIZE = 3; + readonly STRETCH_REPLICATED_MIN_SIZE = 2; + readonly STRETCH_REPLICATED_MAX_SIZE = 4; private modalSubscription: Subscription; @@ -111,6 +124,7 @@ export class PoolFormComponent extends CdForm implements OnInit { private crushRuleService: CrushRuleService, public actionLabels: ActionLabelsI18n, private rbdMirroringService: RbdMirroringService, + private monitorService: MonitorService, private cdr: ChangeDetectorRef ) { super(); @@ -205,6 +219,13 @@ export class PoolFormComponent extends CdForm implements OnInit { } ngOnInit() { + this.monitorService.getMonitor().subscribe((data: MonitorResponse) => { + this.isStretchMode = data?.mon_status?.stretch_mode || false; + if (this.isStretchMode) { + this.applyStretchModeRestrictions(); + } + this.replicatedRuleChange(); + }); this.poolService.getInfo().subscribe((info: PoolFormInfo) => { this.initInfo(info); if (this.editing) { @@ -459,9 +480,31 @@ export class PoolFormComponent extends CdForm implements OnInit { this.setListControlStatus('crushRule', rules); } this.replicatedRuleChange(); + if (this.isStretchMode) { + this.applyStretchModeRestrictions(); + } this.pgCalc(); } + private applyStretchModeRestrictions() { + if (!this.editing && this.form.getValue('poolType') === 'erasure') { + this.form.silentSet('poolType', 'replicated'); + this.poolTypeChange('replicated'); + return; + } + if (this.editing) { + return; + } + const sizeControl = this.form.get('size'); + if (this.isStretchMode) { + if (sizeControl.enabled) { + sizeControl.disable({ emitEvent: false }); + } + } else if (sizeControl.disabled) { + sizeControl.enable({ emitEvent: false }); + } + } + private setTypeBooleans(replicated: boolean, erasure: boolean) { this.isReplicated = replicated; this.isErasure = erasure; @@ -472,7 +515,9 @@ export class PoolFormComponent extends CdForm implements OnInit { return; } const control = this.form.get('size'); - let size = this.form.getValue('size') || 3; + let size = + this.form.getValue('size') || + (this.isStretchMode ? this.STRETCH_REPLICATED_MAX_SIZE : this.DEFAULT_REPLICATED_MAX_SIZE); const min = this.getMinSize(); const max = this.getMaxSize(); if (size < min) { @@ -489,7 +534,7 @@ export class PoolFormComponent extends CdForm implements OnInit { if (!this.info || this.info.osd_count < 1) { return 0; } - return 1; + return this.isStretchMode ? this.STRETCH_REPLICATED_MIN_SIZE : this.DEFAULT_REPLICATED_MIN_SIZE; } getMaxSize(): number { @@ -497,10 +542,9 @@ export class PoolFormComponent extends CdForm implements OnInit { if (!this.info) { return 0; } + if (this.isStretchMode) return this.STRETCH_REPLICATED_MAX_SIZE; if (!rule) { - const osds = this.info.osd_count; - const defaultSize = 3; - return Math.min(osds, defaultSize); + return Math.min(this.info.osd_count, this.DEFAULT_REPLICATED_MAX_SIZE); } return rule.usable_size; }