]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mgr/dashboard: Manage PG autoscaling 31417/head
authorRicardo Marques <rimarques@suse.com>
Tue, 5 Nov 2019 14:58:00 +0000 (14:58 +0000)
committerRicardo Marques <rimarques@suse.com>
Fri, 8 Nov 2019 13:53:14 +0000 (13:53 +0000)
Fixes: https://tracker.ceph.com/issues/38227
Signed-off-by: Ricardo Marques <rimarques@suse.com>
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.html
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-form/pool-form.component.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/configuration.service.spec.ts
src/pybind/mgr/dashboard/frontend/src/app/shared/api/configuration.service.ts

index df17a756033f08c309b0bb97e365eb6901a8ae57..a43913fe68ec1ae7bfc22b0ddebf2fee2b2f7c4b 100644 (file)
         </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>
index 9032766d1458086a6b54fff68ea72d358e536677..6efb4bf0793060a8025e02aad45b80711d9844d6 100644 (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);
index 85273c4a460b3350225e8b8829ee34b42882cad3..a8f9ea82256adfd57a3b0bfec82ffe59242386da 100644 (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) {
@@ -222,6 +232,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,
@@ -531,7 +542,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' }
         : {
index 9add3a5b7745bba789dba199f988a14c67999b9a..2c9da18c7e7bae21edd33589ad9a14dce89890fc 100644 (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');
+  });
 });
index 3355b8fb7723886e98c0aaaa551a50208877f12f..01d95afcf604c9e9015db43a4842a995fdbd00ad 100644 (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/');
   }