value)
self.assertEqual(result, value)
+ def test_check_existence(self):
+ """
+ This test case is intended to check the existence of all hard coded config options used by
+ the dashboard.
+
+ If you include further hard coded options in the dashboard, feel free to add them to the
+ list.
+ """
+ hard_coded_options = [
+ 'osd_max_backfills', # osd-recv-speed
+ 'osd_recovery_max_active', # osd-recv-speed
+ 'osd_recovery_max_single_start', # osd-recv-speed
+ 'osd_recovery_sleep', # osd-recv-speed
+ 'osd_scrub_during_recovery', # osd-pg-scrub
+ 'osd_scrub_begin_hour', # osd-pg-scrub
+ 'osd_scrub_end_hour', # osd-pg-scrub
+ 'osd_scrub_begin_week_day', # osd-pg-scrub
+ 'osd_scrub_end_week_day', # osd-pg-scrub
+ 'osd_scrub_min_interval', # osd-pg-scrub
+ 'osd_scrub_max_interval', # osd-pg-scrub
+ 'osd_deep_scrub_interval', # osd-pg-scrub
+ 'osd_scrub_auto_repair', # osd-pg-scrub
+ 'osd_max_scrubs', # osd-pg-scrub
+ 'osd_scrub_priority', # osd-pg-scrub
+ 'osd_scrub_sleep', # osd-pg-scrub
+ 'osd_scrub_auto_repair_num_errors', # osd-pg-scrub
+ 'osd_debug_deep_scrub_sleep', # osd-pg-scrub
+ 'osd_deep_scrub_keys', # osd-pg-scrub
+ 'osd_deep_scrub_large_omap_object_key_threshold', # osd-pg-scrub
+ 'osd_deep_scrub_large_omap_object_value_sum_threshold', # osd-pg-scrub
+ 'osd_deep_scrub_randomize_ratio', # osd-pg-scrub
+ 'osd_deep_scrub_stride', # osd-pg-scrub
+ 'osd_deep_scrub_update_digest_min_age', # osd-pg-scrub
+ 'osd_op_queue_mclock_scrub_lim', # osd-pg-scrub
+ 'osd_op_queue_mclock_scrub_res', # osd-pg-scrub
+ 'osd_op_queue_mclock_scrub_wgt', # osd-pg-scrub
+ 'osd_requested_scrub_priority', # osd-pg-scrub
+ 'osd_scrub_backoff_ratio', # osd-pg-scrub
+ 'osd_scrub_chunk_max', # osd-pg-scrub
+ 'osd_scrub_chunk_min', # osd-pg-scrub
+ 'osd_scrub_cost', # osd-pg-scrub
+ 'osd_scrub_interval_randomize_ratio', # osd-pg-scrub
+ 'osd_scrub_invalid_stats', # osd-pg-scrub
+ 'osd_scrub_load_threshold', # osd-pg-scrub
+ 'osd_scrub_max_preemptions' # osd-pg-scrub
+ ]
+
+ for config_option in hard_coded_options:
+ self._get('/api/cluster_conf/{}'.format(config_option))
+ self.assertStatus(200)
+
def _validate_single(self, data):
self.assertIn('name', data)
self.assertIn('daemon_default', data)
import { OsdFlagsModalComponent } from './osd/osd-flags-modal/osd-flags-modal.component';
import { OsdListComponent } from './osd/osd-list/osd-list.component';
import { OsdPerformanceHistogramComponent } from './osd/osd-performance-histogram/osd-performance-histogram.component';
+import { OsdPgScrubModalComponent } from './osd/osd-pg-scrub-modal/osd-pg-scrub-modal.component';
import { OsdRecvSpeedModalComponent } from './osd/osd-recv-speed-modal/osd-recv-speed-modal.component';
import { OsdReweightModalComponent } from './osd/osd-reweight-modal/osd-reweight-modal.component';
import { OsdScrubModalComponent } from './osd/osd-scrub-modal/osd-scrub-modal.component';
OsdScrubModalComponent,
OsdFlagsModalComponent,
OsdRecvSpeedModalComponent,
- OsdReweightModalComponent
+ OsdReweightModalComponent,
+ OsdPgScrubModalComponent
],
imports: [
CommonModule,
CrushmapComponent,
LogsComponent,
PrometheusListComponent,
- OsdRecvSpeedModalComponent
+ OsdRecvSpeedModalComponent,
+ OsdPgScrubModalComponent
]
})
export class ClusterModule {}
<ul *dropdownMenu
class="dropdown-menu"
role="menu">
- <li role="menuitem">
- <a class="dropdown-item"
- (click)="configureQosParamsAction()">
- <i class="fa fa-fw fa-cog"
- aria-hidden="true">
- </i>
- <ng-container i18n>Set Cluster-wide Recovery Priority</ng-container>
- </a>
- </li>
+ <ng-container *ngFor="let action of generalTableActions">
+ <li role="menuitem">
+ <a class="dropdown-item"
+ (click)="action.click()">
+ <i class="fa fa-fw {{ action.icon }}"
+ aria-hidden="true">
+ </i>
+ <ng-container>{{ action.name }}</ng-container>
+ </a>
+ </li>
+ </ng-container>
</ul>
</div>
</div>
import { DimlessBinaryPipe } from '../../../../shared/pipes/dimless-binary.pipe';
import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
import { OsdFlagsModalComponent } from '../osd-flags-modal/osd-flags-modal.component';
+import { OsdPgScrubModalComponent } from '../osd-pg-scrub-modal/osd-pg-scrub-modal.component';
import { OsdRecvSpeedModalComponent } from '../osd-recv-speed-modal/osd-recv-speed-modal.component';
import { OsdReweightModalComponent } from '../osd-reweight-modal/osd-reweight-modal.component';
import { OsdScrubModalComponent } from '../osd-scrub-modal/osd-scrub-modal.component';
tableActions: CdTableAction[];
bsModalRef: BsModalRef;
columns: CdTableColumn[];
+ generalTableActions: any[];
osds = [];
selection = new CdTableSelection();
icon: 'fa-remove'
}
];
+ this.generalTableActions = [
+ {
+ name: this.i18n('Set Cluster-wide Recovery Priority'),
+ icon: 'fa-cog',
+ click: () => this.configureQosParamsAction()
+ },
+ {
+ name: this.i18n('PG scrub configuration'),
+ icon: 'fa-stethoscope',
+ click: () => this.configurePgScrubAction()
+ }
+ ];
}
ngOnInit() {
configureQosParamsAction() {
this.bsModalRef = this.modalService.show(OsdRecvSpeedModalComponent, {});
}
+
+ configurePgScrubAction() {
+ this.bsModalRef = this.modalService.show(OsdPgScrubModalComponent, { class: 'modal-lg' });
+ }
}
--- /dev/null
+<cd-modal [modalRef]="bsModalRef">
+ <ng-container i18n="form title|Example: Create Pool@@formTitle"
+ class="modal-title">{{ action | titlecase }} {{ resource | upperFirst }}</ng-container>
+
+ <ng-container class="modal-content">
+ <form class="form-horizontal"
+ #formDir="ngForm"
+ [formGroup]="osdPgScrubForm"
+ novalidate>
+ <div class="modal-body osd-modal">
+ <!-- Basic -->
+ <cd-config-option [optionNames]="basicOptions"
+ [optionsForm]="osdPgScrubForm"
+ [optionsFormDir]="formDir"
+ [optionsFormGroupName]="'basicFormGroup'"
+ #basicOptionsValues></cd-config-option>
+ <!-- Advanced -->
+ <div class="row">
+ <div class="col-sm-12">
+ <a class="pull-right margin-right-md"
+ (click)="advancedEnabled = true"
+ *ngIf="!advancedEnabled"
+ i18n>Advanced...</a>
+ </div>
+ </div>
+ <div *ngIf="advancedEnabled">
+ <h2 class="page-header"
+ i18n>Advanced configuration options</h2>
+ <cd-config-option [optionNames]="advancedOptions"
+ [optionsForm]="osdPgScrubForm"
+ [optionsFormDir]="formDir"
+ [optionsFormGroupName]="'advancedFormGroup'"
+ #advancedOptionsValues></cd-config-option>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <div class="button-group text-right">
+ <cd-submit-button (submitAction)="submitAction()"
+ [form]="osdPgScrubForm"
+ i18n="form action button|Example: Create Pool@@formActionButton"
+ type="button">{{ action | titlecase }} {{ resource | upperFirst }}</cd-submit-button>
+ <cd-back-button [back]="bsModalRef.hide">
+ </cd-back-button>
+ </div>
+ </div>
+ </form>
+ </ng-container>
+</cd-modal>
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { Router } from '@angular/router';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { ToastModule } from 'ng2-toastr';
+import { BsModalRef, ModalModule } from 'ngx-bootstrap/modal';
+import { of as observableOf } from 'rxjs';
+
+import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
+import { ConfigurationService } from '../../../../shared/api/configuration.service';
+import { NotificationType } from '../../../../shared/enum/notification-type.enum';
+import { NotificationService } from '../../../../shared/services/notification.service';
+import { SharedModule } from '../../../../shared/shared.module';
+import { OsdPgScrubModalComponent } from './osd-pg-scrub-modal.component';
+
+describe('OsdPgScrubModalComponent', () => {
+ let component: OsdPgScrubModalComponent;
+ let fixture: ComponentFixture<OsdPgScrubModalComponent>;
+ let configurationService: ConfigurationService;
+
+ configureTestBed({
+ imports: [
+ HttpClientTestingModule,
+ ModalModule.forRoot(),
+ ReactiveFormsModule,
+ RouterTestingModule,
+ SharedModule,
+ ToastModule.forRoot()
+ ],
+ declarations: [OsdPgScrubModalComponent],
+ providers: [BsModalRef, i18nProviders]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(OsdPgScrubModalComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ configurationService = TestBed.get(ConfigurationService);
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ describe('submitAction', () => {
+ let notificationService: NotificationService;
+
+ beforeEach(() => {
+ spyOn(TestBed.get(Router), 'navigate').and.stub();
+ notificationService = TestBed.get(NotificationService);
+ spyOn(notificationService, 'show');
+ });
+
+ it('test create success notification', () => {
+ spyOn(configurationService, 'bulkCreate').and.returnValue(observableOf([]));
+ component.submitAction();
+ expect(notificationService.show).toHaveBeenCalledWith(
+ NotificationType.success,
+ 'Updated PG scrub options'
+ );
+ });
+ });
+});
--- /dev/null
+import { Component, ViewChild } from '@angular/core';
+
+import { I18n } from '@ngx-translate/i18n-polyfill';
+import { BsModalRef } from 'ngx-bootstrap/modal';
+import { forkJoin as observableForkJoin } from 'rxjs';
+
+import { ConfigOptionComponent } from '../../../../shared/components/config-option/config-option.component';
+import { ActionLabelsI18n } from '../../../../shared/constants/app.constants';
+import { NotificationType } from '../../../../shared/enum/notification-type.enum';
+import { CdFormGroup } from '../../../../shared/forms/cd-form-group';
+import { NotificationService } from '../../../../shared/services/notification.service';
+import { OsdPgScrubModalOptions } from './osd-pg-scrub-modal.options';
+
+@Component({
+ selector: 'cd-osd-pg-scrub-modal',
+ templateUrl: './osd-pg-scrub-modal.component.html',
+ styleUrls: ['./osd-pg-scrub-modal.component.scss']
+})
+export class OsdPgScrubModalComponent {
+ osdPgScrubForm: CdFormGroup;
+ action: string;
+ resource: string;
+
+ @ViewChild('basicOptionsValues')
+ basicOptionsValues: ConfigOptionComponent;
+ basicOptions: Array<string> = OsdPgScrubModalOptions.basicOptions;
+
+ @ViewChild('advancedOptionsValues')
+ advancedOptionsValues: ConfigOptionComponent;
+ advancedOptions: Array<string> = OsdPgScrubModalOptions.advancedOptions;
+
+ advancedEnabled = false;
+
+ constructor(
+ public bsModalRef: BsModalRef,
+ private notificationService: NotificationService,
+ private i18n: I18n,
+ public actionLabels: ActionLabelsI18n
+ ) {
+ this.osdPgScrubForm = new CdFormGroup({});
+ this.resource = this.i18n('PG scrub options');
+ this.action = this.actionLabels.EDIT;
+ }
+
+ submitAction() {
+ const observables = [this.basicOptionsValues.saveValues()];
+
+ if (this.advancedOptionsValues) {
+ observables.push(this.advancedOptionsValues.saveValues());
+ }
+
+ observableForkJoin(observables).subscribe(
+ () => {
+ this.notificationService.show(
+ NotificationType.success,
+ this.i18n('Updated PG scrub options')
+ );
+ this.bsModalRef.hide();
+ },
+ () => {
+ this.bsModalRef.hide();
+ }
+ );
+ }
+}
--- /dev/null
+export class OsdPgScrubModalOptions {
+ public static basicOptions: Array<string> = [
+ 'osd_scrub_during_recovery',
+ 'osd_scrub_begin_hour',
+ 'osd_scrub_end_hour',
+ 'osd_scrub_begin_week_day',
+ 'osd_scrub_end_week_day',
+ 'osd_scrub_min_interval',
+ 'osd_scrub_max_interval',
+ 'osd_deep_scrub_interval',
+ 'osd_scrub_auto_repair',
+ 'osd_max_scrubs',
+ 'osd_scrub_priority',
+ 'osd_scrub_sleep'
+ ];
+
+ public static advancedOptions: Array<string> = [
+ 'osd_scrub_auto_repair_num_errors',
+ 'osd_debug_deep_scrub_sleep',
+ 'osd_deep_scrub_keys',
+ 'osd_deep_scrub_large_omap_object_key_threshold',
+ 'osd_deep_scrub_large_omap_object_value_sum_threshold',
+ 'osd_deep_scrub_randomize_ratio',
+ 'osd_deep_scrub_stride',
+ 'osd_deep_scrub_update_digest_min_age',
+ 'osd_op_queue_mclock_scrub_lim',
+ 'osd_op_queue_mclock_scrub_res',
+ 'osd_op_queue_mclock_scrub_wgt',
+ 'osd_requested_scrub_priority',
+ 'osd_scrub_backoff_ratio',
+ 'osd_scrub_chunk_max',
+ 'osd_scrub_chunk_min',
+ 'osd_scrub_cost',
+ 'osd_scrub_interval_randomize_ratio',
+ 'osd_scrub_invalid_stats',
+ 'osd_scrub_load_threshold',
+ 'osd_scrub_max_preemptions'
+ ];
+}