i18n>Name</label>
<div class="cd-col-form-input">
<input id="name"
- name="name"
type="text"
class="form-control"
placeholder="Name..."
<div class="cd-col-form-input">
<select class="form-select"
id="poolType"
- formControlName="poolType"
- name="poolType">
+ formControlName="poolType">
<option ngValue=""
i18n>-- Select a pool type --</option>
<option *ngFor="let poolType of data.poolTypes"
<div class="cd-col-form-input">
<select class="form-select"
id="pgAutoscaleMode"
- name="pgAutoscaleMode"
formControlName="pgAutoscaleMode">
<option *ngFor="let mode of pgAutoscaleModes"
[value]="mode">
<div class="cd-col-form-input">
<input class="form-control"
id="pgNum"
- name="pgNum"
formControlName="pgNum"
min="1"
type="number"
id="size"
[max]="getMaxSize()"
[min]="getMinSize()"
- name="size"
type="number"
formControlName="size">
<span class="invalid-feedback"
</label>
<div class="cd-col-form-input">
<cd-select-badges id="applications"
- name="applications"
[customBadges]="true"
[customBadgeValidators]="data.applications.validators"
[messages]="data.applications.messages"
<div class="custom-control custom-checkbox">
<input class="custom-control-input"
id="rbdMirroring"
- name="rbdMirroring"
type="checkbox"
formControlName="rbdMirroring">
<label class="custom-control-label"
<div class="input-group mb-1">
<select class="form-select"
id="erasureProfile"
- name="erasureProfile"
formControlName="erasureProfile"
(change)="erasureProfileChange()">
<option *ngIf="!ecProfiles"
<div class="input-group">
<select class="form-select"
id="crushRule"
- formControlName="crushRule"
- name="crushSet">
+ formControlName="crushRule">
<option [ngValue]="null"
i18n>-- Select a crush rule --</option>
<option *ngFor="let rule of current.rules"
<div class="cd-col-form-input">
<select class="form-select"
id="mode"
- name="mode"
formControlName="mode">
<option *ngFor="let mode of info.compression_modes"
[value]="mode">
<div class="cd-col-form-input">
<select class="form-select"
id="algorithm"
- name="algorithm"
formControlName="algorithm">
<option *ngIf="!info.compression_algorithms"
ngValue=""
<ng-container i18n>Minimum blob size</ng-container>
</label>
<div class="cd-col-form-input">
- <input id="minBlobSize"
- name="minBlobSize"
- formControlName="minBlobSize"
- type="text"
- min="0"
- class="form-control"
- i18n-placeholder
- placeholder="e.g., 128KiB"
- defaultUnit="KiB"
- cdDimlessBinary>
+ <div class="input-group mb-1">
+ <input id="minBlobSize"
+ formControlName="minBlobSize"
+ type="text"
+ min="0"
+ class="form-control"
+ i18n-placeholder
+ placeholder="e.g., 128">
+ <select id="minUnit"
+ class="form-input form-select"
+ formControlName="minBlobSizeUnit">
+ <option *ngFor="let u of blobUnits"
+ [value]="u">
+ {{ u }}
+ </option>
+ </select>
+ </div>
<cd-help-text>
<span i18n>Chunks smaller than Minimum blob size are never compressed</span>
</cd-help-text>
<ng-container i18n>Maximum blob size</ng-container>
</label>
<div class="cd-col-form-input">
- <input id="maxBlobSize"
- type="text"
- min="0"
- formControlName="maxBlobSize"
- class="form-control"
- i18n-placeholder
- placeholder="e.g., 512KiB"
- defaultUnit="KiB"
- cdDimlessBinary>
+ <div class="input-group mb-1">
+ <input id="maxBlobSize"
+ type="text"
+ min="0"
+ formControlName="maxBlobSize"
+ class="form-control">
+ <select id="minUnit"
+ class="form-input form-select"
+ formControlName="maxBlobSizeUnit">
+ <option *ngFor="let u of blobUnits"
+ [value]="u">
+ {{ u }}
+ </option>
+ </select>
+ </div>
<cd-help-text>
<span i18n>Chunks larger than `Maximum Blob Size` are broken into smaller blobs of size mentioned before being compressed.</span>
</cd-help-text>
</label>
<div class="cd-col-form-input">
<input id="ratio"
- name="ratio"
formControlName="ratio"
type="number"
min="0"
<ng-container i18n>Max bytes</ng-container>
</label>
<div class="cd-col-form-input">
- <input class="form-control"
- id="max_bytes"
- name="max_bytes"
- type="text"
- formControlName="max_bytes"
- i18n-placeholder
- placeholder="e.g., 10GiB"
- defaultUnit="GiB"
- cdDimlessBinary>
+ <div class="input-group mb-1">
+ <input class="form-control"
+ id="max_bytes"
+ type="text"
+ formControlName="max_bytes">
+ <select id="unit"
+ class="form-input form-select"
+ formControlName="maxBytesUnit">
+ <option *ngFor="let u of maxBytesUnits"
+ [value]="u">
+ {{ u }}
+ </option>
+ </select>
+ </div>
<cd-help-text>
<span i18n>Leave it blank or specify 0 to disable this quota.</span>
<br>
<input class="form-control"
id="max_objects"
min="0"
- name="max_objects"
type="number"
formControlName="max_objects">
<cd-help-text>
it('validates minBlobSize to be only valid between 0 and maxBlobSize', () => {
formHelper.expectErrorChange('minBlobSize', -1, 'min');
formHelper.expectValidChange('minBlobSize', 0);
- formHelper.setValue('maxBlobSize', '2 KiB');
- formHelper.expectErrorChange('minBlobSize', '3 KiB', 'maximum');
- formHelper.expectValidChange('minBlobSize', '1.9 KiB');
+ formHelper.setValue('minBlobSizeUnit', 'KiB');
+ formHelper.setValue('maxBlobSize', '2');
+ formHelper.setValue('maxBlobSizeUnit', 'KiB');
+ formHelper.expectErrorChange('minBlobSize', '3', 'maximum');
+ formHelper.expectValidChange('minBlobSize', '1.9');
});
it('validates minBlobSize converts numbers', () => {
const control = formHelper.setValue('minBlobSize', '1');
+ const controlUnit = formHelper.setValue('minBlobSizeUnit', 'KiB');
fixture.detectChanges();
formHelper.expectValid(control);
- expect(control.value).toBe('1 KiB');
+ expect(control.value).toBe('1');
+ expect(controlUnit.value).toBe('KiB');
});
it('validates maxBlobSize to be only valid bigger than minBlobSize', () => {
formHelper.expectErrorChange('maxBlobSize', -1, 'min');
- formHelper.setValue('minBlobSize', '1 KiB');
- formHelper.expectErrorChange('maxBlobSize', '0.5 KiB', 'minimum');
- formHelper.expectValidChange('maxBlobSize', '1.5 KiB');
+ formHelper.setValue('minBlobSize', '1');
+ formHelper.setValue('minBlobSizeUnit', 'MiB');
+ formHelper.expectErrorChange('maxBlobSize', '0.5', 'minimum');
+ formHelper.expectValidChange('maxBlobSize', '1.5');
});
it('s valid to only use one blob size', () => {
- formHelper.expectValid(formHelper.setValue('minBlobSize', '1 KiB'));
+ formHelper.expectValid(formHelper.setValue('minBlobSize', '1'));
formHelper.expectValid(formHelper.setValue('maxBlobSize', ''));
formHelper.expectValid(formHelper.setValue('minBlobSize', ''));
- formHelper.expectValid(formHelper.setValue('maxBlobSize', '1 KiB'));
+ formHelper.expectValid(formHelper.setValue('maxBlobSize', '1'));
});
it('dismisses any size error if one of the blob sizes is changed into a valid state', () => {
it('validates maxBlobSize converts numbers', () => {
const control = formHelper.setValue('maxBlobSize', '2');
+ const controlUnit = formHelper.setValue('maxBlobSizeUnit', 'KiB');
fixture.detectChanges();
- expect(control.value).toBe('2 KiB');
+ expect(control.value).toBe('2');
+ expect(controlUnit.value).toBe('KiB');
});
it('validates that odd size validator works as expected', () => {
- const odd = (min: string, max: string) => component['oddBlobSize'](min, max);
- expect(odd('10', '8')).toBe(true);
- expect(odd('8', '-')).toBe(false);
- expect(odd('8', '10')).toBe(false);
- expect(odd(null, '8')).toBe(false);
- expect(odd('10', '')).toBe(false);
- expect(odd('10', null)).toBe(false);
- expect(odd(null, null)).toBe(false);
+ const odd = (min: string, minUnit: string, max: string, maxUnit: string) =>
+ component['oddBlobSize'](min, minUnit, max, maxUnit);
+ expect(odd('10', 'KiB', '8', 'KiB')).toBe(true);
+ expect(odd('8', 'KiB', '-', 'KiB')).toBe(false);
+ expect(odd('8', 'KiB', '10', 'KiB')).toBe(false);
+ expect(odd(null, 'KiB', '8', 'KiB')).toBe(false);
+ expect(odd('10', 'KiB', '', 'KiB')).toBe(false);
+ expect(odd('10', 'KiB', null, 'KiB')).toBe(false);
+ expect(odd(null, 'KiB', null, 'KiB')).toBe(false);
});
it('validates ratio to be only valid between 0 and 1', () => {
setMultipleValues({
mode: 'passive',
algorithm: 'lz4',
- minBlobSize: '4 K',
- maxBlobSize: '4 M',
+ minBlobSize: '4',
+ minBlobSizeUnit: 'KiB',
+ maxBlobSize: '4',
+ maxBlobSizeUnit: 'MiB',
ratio: 0.7
});
+ fixture.detectChanges();
expectEcSubmit({
compression_mode: 'passive',
compression_algorithm: 'lz4',
it('creates a pool with quotas', () => {
setMultipleValues({
- max_bytes: 1024 * 1024,
+ max_bytes: 1,
+ maxBytesUnit: 'MiB',
max_objects: 3000
});
component.data.applications.selected = ['cephfs', 'rgw'];
expect(form.getValue('pgNum')).toBe(pool.pg_num);
expect(form.getValue('mode')).toBe(pool.options.compression_mode);
expect(form.getValue('algorithm')).toBe(pool.options.compression_algorithm);
- expect(form.getValue('minBlobSize')).toBe('512 KiB');
- expect(form.getValue('maxBlobSize')).toBe('1 MiB');
+ expect(form.getValue('minBlobSize')).toBe('512');
+ expect(form.getValue('minBlobSizeUnit')).toBe('KiB');
+ expect(form.getValue('maxBlobSize')).toBe('1');
+ expect(form.getValue('maxBlobSizeUnit')).toBe('MiB');
expect(form.getValue('ratio')).toBe(pool.options.compression_required_ratio);
- expect(form.getValue('max_bytes')).toBe('1 GiB');
+ expect(form.getValue('max_bytes')).toBe('1');
+ expect(form.getValue('maxBytesUnit')).toBe('GiB');
expect(form.getValue('max_objects')).toBe(pool.quota_max_objects);
});
editing = false;
isReplicated = false;
isErasure = false;
+ blobUnits = ['B', 'KiB', 'MiB', 'GiB', 'TiB'];
+ maxBytesUnits = ['KiB', 'MiB', 'GiB', 'TiB'];
data = new PoolFormData();
externalPgChange = false;
current: Record<string, any> = {
minBlobSize: new UntypedFormControl('', {
updateOn: 'blur'
}),
+ minBlobSizeUnit: new UntypedFormControl(this.blobUnits[0], {
+ updateOn: 'blur'
+ }),
maxBlobSize: new UntypedFormControl('', {
updateOn: 'blur'
}),
+ maxBlobSizeUnit: new UntypedFormControl(this.blobUnits[2], {
+ updateOn: 'blur'
+ }),
ratio: new UntypedFormControl(this.DEFAULT_RATIO, {
updateOn: 'blur'
})
ecOverwrites: new UntypedFormControl(false),
compression: compressionForm,
max_bytes: new UntypedFormControl(''),
+ maxBytesUnit: new UntypedFormControl(this.maxBytesUnits[2]),
max_objects: new UntypedFormControl(0),
rbdMirroring: new UntypedFormControl(false)
},
initialData: pool.configuration,
sourceType: RbdConfigurationSourceField.pool
});
+ const maxBytesConverted = this.dimlessBinaryPipe.transform(pool.quota_max_bytes).split(' ');
+ const minBlobSizeConverted = this.dimlessBinaryPipe
+ .transform(pool.options.compression_min_blob_size)
+ .split(' ');
+ const maxBlobSizeConverted = this.dimlessBinaryPipe
+ .transform(pool.options.compression_max_blob_size)
+ .split(' ');
this.poolTypeChange(pool.type);
const rules = this.info.crush_rules_replicated.concat(this.info.crush_rules_erasure);
const dataMap = {
ecOverwrites: pool.flags_names.includes('ec_overwrites'),
mode: pool.options.compression_mode,
algorithm: pool.options.compression_algorithm,
- minBlobSize: this.dimlessBinaryPipe.transform(pool.options.compression_min_blob_size),
- maxBlobSize: this.dimlessBinaryPipe.transform(pool.options.compression_max_blob_size),
+ minBlobSize: minBlobSizeConverted[0],
+ minBlobSizeUnit: minBlobSizeConverted[1],
+ maxBlobSize: maxBlobSizeConverted[0],
+ maxBlobSizeUnit: maxBlobSizeConverted[1],
ratio: pool.options.compression_required_ratio,
- max_bytes: this.dimlessBinaryPipe.transform(pool.quota_max_bytes),
+ max_bytes: maxBytesConverted[0],
+ maxBytesUnit: maxBytesConverted[1],
max_objects: pool.quota_max_objects
};
Object.keys(dataMap).forEach((controlName: string) => {
this.form.get('minBlobSize').valueChanges.subscribe(() => {
this.form.get('maxBlobSize').updateValueAndValidity({ emitEvent: false });
});
+ this.form.get('minBlobSizeUnit').valueChanges.subscribe(() => {
+ this.form.get('maxBlobSize').updateValueAndValidity({ emitEvent: false });
+ });
this.form.get('maxBlobSize').valueChanges.subscribe(() => {
this.form.get('minBlobSize').updateValueAndValidity({ emitEvent: false });
});
+ this.form.get('maxBlobSizeUnit').valueChanges.subscribe(() => {
+ this.form.get('minBlobSize').updateValueAndValidity({ emitEvent: false });
+ });
}
private poolTypeChange(poolType: string) {
CdValidators.validateIf(this.form.get('minBlobSize'), () => this.hasCompressionEnabled(), [
Validators.min(0),
CdValidators.custom('maximum', (size: string) =>
- this.oddBlobSize(size, this.form.getValue('maxBlobSize'))
+ this.oddBlobSize(
+ size,
+ this.form.getValue('minBlobSizeUnit'),
+ this.form.getValue('maxBlobSize'),
+ this.form.getValue('maxBlobSizeUnit')
+ )
)
]);
CdValidators.validateIf(this.form.get('maxBlobSize'), () => this.hasCompressionEnabled(), [
Validators.min(0),
CdValidators.custom('minimum', (size: string) =>
- this.oddBlobSize(this.form.getValue('minBlobSize'), size)
+ this.oddBlobSize(
+ this.form.getValue('minBlobSize'),
+ this.form.getValue('minBlobSizeUnit'),
+ size,
+ this.form.getValue('maxBlobSizeUnit')
+ )
)
]);
CdValidators.validateIf(this.form.get('ratio'), () => this.hasCompressionEnabled(), [
]);
}
- private oddBlobSize(minimum: string, maximum: string) {
- const min = this.formatter.toBytes(minimum);
- const max = this.formatter.toBytes(maximum);
+ private oddBlobSize(minimum: string, minUnit: string, maximum: string, maxUnit: string) {
+ const min = this.formatter.toBytes(minimum + minUnit);
+ const max = this.formatter.toBytes(maximum + maxUnit);
return Boolean(min && max && min >= max);
}
{
externalFieldName: 'quota_max_bytes',
formControlName: 'max_bytes',
- replaceFn: this.formatter.toBytes,
+ replaceFn: (value: string) => {
+ const unit = this.form.getValue('maxBytesUnit');
+ return this.formatter.toBytes(value + unit);
+ },
editable: true,
resetValue: this.editing ? 0 : undefined
},
{
externalFieldName: 'compression_min_blob_size',
formControlName: 'minBlobSize',
- replaceFn: this.formatter.toBytes,
+ replaceFn: (value: string) => {
+ const unit = this.form.getValue('minBlobSizeUnit');
+ return this.formatter.toBytes(value + unit);
+ },
editable: true,
resetValue: 0
},
{
externalFieldName: 'compression_max_blob_size',
formControlName: 'maxBlobSize',
- replaceFn: this.formatter.toBytes,
+ replaceFn: (value: string) => {
+ const unit = this.form.getValue('maxBlobSizeUnit');
+ return this.formatter.toBytes(value + unit);
+ },
editable: true,
resetValue: 0
},