mgr/dashboard: Manage PG autoscaling (#31417)

Reviewed-by: Tiago Melo <tmelo@suse.com>
This commit is contained in:
Lenz Grimmer 2019-11-12 13:16:52 +00:00 committed by GitHub
commit 72c63fe228
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 12 deletions

View File

@ -67,8 +67,27 @@
</div>
<div *ngIf="form.getValue('poolType')">
<!-- Pg number -->
<!-- PG Autoscale Mode -->
<div class="form-group row">
<label i18n
class="col-form-label col-sm-3"
for="pgAutoscaleMode">PG Autoscale</label>
<div class="col-sm-9">
<select class="form-control custom-select"
id="pgAutoscaleMode"
name="pgAutoscaleMode"
formControlName="pgAutoscaleMode">
<option *ngFor="let mode of pgAutoscaleModes"
[value]="mode">
{{ mode }}
</option>
</select>
</div>
</div>
<!-- Pg number -->
<div class="form-group row"
*ngIf="form.getValue('pgAutoscaleMode') !== 'on'">
<label class="col-form-label col-sm-3"
for="pgNum">
<ng-container i18n>Placement groups</ng-container>

View File

@ -18,6 +18,7 @@ import {
i18nProviders
} from '../../../../testing/unit-test-helper';
import { NotFoundComponent } from '../../../core/not-found/not-found.component';
import { ConfigurationService } from '../../../shared/api/configuration.service';
import { ErasureCodeProfileService } from '../../../shared/api/erasure-code-profile.service';
import { PoolService } from '../../../shared/api/pool.service';
import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
@ -39,6 +40,7 @@ describe('PoolFormComponent', () => {
let component: PoolFormComponent;
let fixture: ComponentFixture<PoolFormComponent>;
let poolService: PoolService;
let configurationService: ConfigurationService;
let form: CdFormGroup;
let router: Router;
let ecpService: ErasureCodeProfileService;
@ -162,6 +164,10 @@ describe('PoolFormComponent', () => {
beforeEach(() => {
setUpPoolComponent();
configurationService = TestBed.get(ConfigurationService);
spyOn(configurationService, 'get').and.callFake(() => [
{ default: 'off', enum_values: ['on', 'warn', 'off'], value: [] }
]);
poolService = TestBed.get(PoolService);
spyOn(poolService, 'getInfo').and.callFake(() => [component.info]);
ecpService = TestBed.get(ErasureCodeProfileService);

View File

@ -7,6 +7,7 @@ import * as _ from 'lodash';
import { BsModalService } from 'ngx-bootstrap/modal';
import { forkJoin, Subscription } from 'rxjs';
import { ConfigurationService } from '../../../shared/api/configuration.service';
import { ErasureCodeProfileService } from '../../../shared/api/erasure-code-profile.service';
import { PoolService } from '../../../shared/api/pool.service';
import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
@ -67,6 +68,7 @@ export class PoolFormComponent implements OnInit {
action: string;
resource: string;
icons = Icons;
pgAutoscaleModes: string[];
constructor(
private dimlessBinaryPipe: DimlessBinaryPipe,
@ -74,6 +76,7 @@ export class PoolFormComponent implements OnInit {
private router: Router,
private modalService: BsModalService,
private poolService: PoolService,
private configurationService: ConfigurationService,
private authStorageService: AuthStorageService,
private formatter: FormatterService,
private bsModalService: BsModalService,
@ -148,6 +151,7 @@ export class PoolFormComponent implements OnInit {
pgNum: new FormControl('', {
validators: [Validators.required, Validators.min(1)]
}),
pgAutoscaleMode: new FormControl(null),
ecOverwrites: new FormControl(false),
compression: compressionForm,
max_bytes: new FormControl(''),
@ -160,17 +164,23 @@ export class PoolFormComponent implements OnInit {
}
ngOnInit() {
forkJoin(this.poolService.getInfo(), this.ecpService.list()).subscribe(
(data: [PoolFormInfo, ErasureCodeProfile[]]) => {
this.initInfo(data[0]);
this.initEcp(data[1]);
if (this.editing) {
this.initEditMode();
}
this.listenToChanges();
this.setComplexValidators();
forkJoin(
this.configurationService.get('osd_pool_default_pg_autoscale_mode'),
this.poolService.getInfo(),
this.ecpService.list()
).subscribe((data: [any, PoolFormInfo, ErasureCodeProfile[]]) => {
const pgAutoscaleConfig = data[0];
this.pgAutoscaleModes = pgAutoscaleConfig.enum_values;
const defaultPgAutoscaleMode = this.configurationService.getValue(pgAutoscaleConfig, 'mon');
this.form.silentSet('pgAutoscaleMode', defaultPgAutoscaleMode);
this.initInfo(data[1]);
this.initEcp(data[2]);
if (this.editing) {
this.initEditMode();
}
);
this.listenToChanges();
this.setComplexValidators();
});
}
private initInfo(info: PoolFormInfo) {
@ -221,6 +231,7 @@ export class PoolFormComponent implements OnInit {
),
size: pool.size,
erasureProfile: this.ecProfiles.find((ecp) => ecp.name === pool.erasure_code_profile),
pgAutoscaleMode: pool.pg_autoscale_mode,
pgNum: pool.pg_num,
ecOverwrites: pool.flags_names.includes('ec_overwrites'),
mode: pool.options.compression_mode,
@ -530,7 +541,17 @@ export class PoolFormComponent implements OnInit {
this.assignFormFields(pool, [
{ externalFieldName: 'pool_type', formControlName: 'poolType' },
{ externalFieldName: 'pg_num', formControlName: 'pgNum', editable: true },
{
externalFieldName: 'pg_autoscale_mode',
formControlName: 'pgAutoscaleMode',
editable: true
},
{
externalFieldName: 'pg_num',
formControlName: 'pgNum',
replaceFn: (value) => (this.form.getValue('pgAutoscaleMode') === 'on' ? 1 : value),
editable: true
},
this.form.getValue('poolType') === 'replicated'
? { externalFieldName: 'size', formControlName: 'size' }
: {

View File

@ -77,4 +77,23 @@ describe('ConfigurationService', () => {
const reg = httpTesting.expectOne('api/cluster_conf/testOption?section=testSection');
expect(reg.request.method).toBe('DELETE');
});
it('should get value', () => {
const config = {
default: 'a',
value: [
{ section: 'global', value: 'b' },
{ section: 'mon', value: 'c' },
{ section: 'mon.1', value: 'd' },
{ section: 'mds', value: 'e' }
]
};
expect(service.getValue(config, 'mon.1')).toBe('d');
expect(service.getValue(config, 'mon')).toBe('c');
expect(service.getValue(config, 'mds.1')).toBe('e');
expect(service.getValue(config, 'mds')).toBe('e');
expect(service.getValue(config, 'osd')).toBe('b');
config.value = [];
expect(service.getValue(config, 'osd')).toBe('a');
});
});

View File

@ -10,6 +10,30 @@ import { ApiModule } from './api.module';
export class ConfigurationService {
constructor(private http: HttpClient) {}
private findValue(config, section: string) {
if (!config.value) {
return undefined;
}
return config.value.find((v) => v.section === section);
}
getValue(config, section: string) {
let val = this.findValue(config, section);
if (!val) {
const indexOfDot = section.indexOf('.');
if (indexOfDot !== -1) {
val = this.findValue(config, section.substring(0, indexOfDot));
}
}
if (!val) {
val = this.findValue(config, 'global');
}
if (val) {
return val.value;
}
return config.default;
}
getConfigData() {
return this.http.get('api/cluster_conf/');
}