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 { 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';
OsdDetailsComponent,
OsdScrubModalComponent,
OsdFlagsModalComponent,
+ OsdRecvSpeedModalComponent,
OsdReweightModalComponent
],
imports: [
ConfigurationFormComponent,
OsdReweightModalComponent,
CrushmapComponent,
- LogsComponent
+ LogsComponent,
+ OsdRecvSpeedModalComponent
]
})
export class ClusterModule {}
<i class="fa fa-fw fa-cog"
aria-hidden="true">
</i>
- <ng-container i18n>Set Cluster-wide OSD Flags</ng-container>
+ <ng-container i18n>Set Cluster-wide Flags</ng-container>
+ </button>
+
+ <button class="btn btn-sm btn-default btn-label tc_configureCluster"
+ type="button"
+ (click)="configureQosParamsAction()">
+ <i class="fa fa-fw fa-cog"
+ aria-hidden="true">
+ </i>
+ <ng-container i18n>Set Cluster-wide Recovery Priority</ng-container>
</button>
</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 { 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';
});
});
}
+
+ configureQosParamsAction() {
+ this.bsModalRef = this.modalService.show(OsdRecvSpeedModalComponent, {});
+ }
}
--- /dev/null
+<cd-modal [modalRef]="bsModalRef">
+ <ng-container class="modal-title"
+ i18n>OSD Recovery Priority</ng-container>
+
+ <ng-container class="modal-content">
+ <form class="form-horizontal"
+ #formDir="ngForm"
+ [formGroup]="osdRecvSpeedForm"
+ novalidate>
+ <div class="modal-body">
+ <!-- Priority -->
+ <div class="form-group"
+ [ngClass]="{'has-error': osdRecvSpeedForm.showError('priority', formDir)}">
+ <label class="control-label col-sm-6"
+ for="priority">
+ <ng-container i18n>Priority</ng-container>
+ <span class="required"></span>
+ </label>
+ <div class="col-sm-6">
+ <select class="form-control"
+ formControlName="priority"
+ id="priority"
+ (change)="onPriorityChange($event.target.value)">
+ <option *ngFor="let priority of priorities"
+ [value]="priority.name">
+ {{ priority.text }}
+ </option>
+ </select>
+ <span class="help-block"
+ *ngIf="osdRecvSpeedForm.showError('priority', formDir, 'required')"
+ i18n>This field is required.</span>
+ </div>
+ </div>
+
+ <!-- Customize priority -->
+ <div class="form-group">
+ <div class="col-sm-offset-6 col-sm-6">
+ <div class="checkbox checkbox-primary">
+ <input formControlName="customizePriority"
+ id="customizePriority"
+ type="checkbox"
+ (change)="onCustomizePriorityChange()">
+ <label i18n
+ for="customizePriority">Customize priority values</label>
+ </div>
+ </div>
+ </div>
+ <!-- Priority values -->
+ <div class="form-group" *ngFor="let attr of priorityAttrs"
+ [ngClass]="{'has-error': osdRecvSpeedForm.getValue('customizePriority') &&
+ osdRecvSpeedForm.showError(attr.name, formDir)}">
+ <label class="control-label col-sm-6"
+ [for]="attr.name">{{ attr.text }}
+ <span class="required" *ngIf="osdRecvSpeedForm.getValue('customizePriority')"></span>
+ </label>
+ <div class="col-sm-6">
+ <input class="form-control"
+ type="number"
+ [id]="attr.name"
+ [formControlName]="attr.name"
+ [readonly]="!osdRecvSpeedForm.getValue('customizePriority')">
+ <span class="help-block"
+ *ngIf="osdRecvSpeedForm.getValue('customizePriority') &&
+ osdRecvSpeedForm.showError(attr.name, formDir, 'required')"
+ i18n>This field is required!</span>
+ </div>
+ </div>
+ </div>
+ <div class="modal-footer">
+ <div class="button-group text-right">
+ <cd-submit-button (submitAction)="submitAction()"
+ [form]="osdRecvSpeedForm"
+ i18n>Submit</cd-submit-button>
+
+ <button class="btn btn-link btn-sm"
+ (click)="bsModalRef.hide()"
+ i18n>Cancel</button>
+ </div>
+ </div>
+ </form>
+ </ng-container>
+</cd-modal>
--- /dev/null
+import { HttpClientTestingModule } from '@angular/common/http/testing';
+import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+
+import * as _ from 'lodash';
+import { ToastModule } from 'ng2-toastr';
+import { BsModalRef, ModalModule } from 'ngx-bootstrap/modal';
+import { of } from 'rxjs';
+
+import { configureTestBed, i18nProviders } from '../../../../../testing/unit-test-helper';
+import { ConfigurationService } from '../../../../shared/api/configuration.service';
+import { SharedModule } from '../../../../shared/shared.module';
+import { OsdRecvSpeedModalComponent } from './osd-recv-speed-modal.component';
+
+describe('OsdRecvSpeedModalComponent', () => {
+ let component: OsdRecvSpeedModalComponent;
+ let fixture: ComponentFixture<OsdRecvSpeedModalComponent>;
+ let configService: ConfigurationService;
+
+ configureTestBed({
+ imports: [
+ HttpClientTestingModule,
+ ModalModule.forRoot(),
+ ReactiveFormsModule,
+ SharedModule,
+ ToastModule.forRoot()
+ ],
+ declarations: [OsdRecvSpeedModalComponent],
+ providers: [BsModalRef, i18nProviders]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(OsdRecvSpeedModalComponent);
+ component = fixture.componentInstance;
+ configService = TestBed.get(ConfigurationService);
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+
+ describe('setPriority', () => {
+ it('should prepare the form for a custom priority', () => {
+ const customPriority = {
+ name: 'custom',
+ text: 'Custom',
+ values: {
+ osd_max_backfills: 1,
+ osd_recovery_max_active: 4,
+ osd_recovery_max_single_start: 1,
+ osd_recovery_sleep: 1
+ }
+ };
+
+ component.setPriority(customPriority);
+
+ const customInPriorities = _.find(component.priorities, (p) => {
+ return p.name === 'custom';
+ });
+
+ expect(customInPriorities).not.toBeNull();
+ expect(component.osdRecvSpeedForm.getValue('priority')).toBe('custom');
+ expect(component.osdRecvSpeedForm.getValue('osd_max_backfills')).toBe(1);
+ expect(component.osdRecvSpeedForm.getValue('osd_recovery_max_active')).toBe(4);
+ expect(component.osdRecvSpeedForm.getValue('osd_recovery_max_single_start')).toBe(1);
+ expect(component.osdRecvSpeedForm.getValue('osd_recovery_sleep')).toBe(1);
+ });
+
+ it('should prepare the form for a none custom priority', () => {
+ const lowPriority = {
+ name: 'low',
+ text: 'Low',
+ values: {
+ osd_max_backfills: 1,
+ osd_recovery_max_active: 1,
+ osd_recovery_max_single_start: 1,
+ osd_recovery_sleep: 0.5
+ }
+ };
+
+ component.setPriority(lowPriority);
+
+ const customInPriorities = _.find(component.priorities, (p) => {
+ return p.name === 'custom';
+ });
+
+ expect(customInPriorities).toBeUndefined();
+ expect(component.osdRecvSpeedForm.getValue('priority')).toBe('low');
+ expect(component.osdRecvSpeedForm.getValue('osd_max_backfills')).toBe(1);
+ expect(component.osdRecvSpeedForm.getValue('osd_recovery_max_active')).toBe(1);
+ expect(component.osdRecvSpeedForm.getValue('osd_recovery_max_single_start')).toBe(1);
+ expect(component.osdRecvSpeedForm.getValue('osd_recovery_sleep')).toBe(0.5);
+ });
+ });
+
+ describe('getStoredPriority', () => {
+ const configOptionsLow = [
+ {
+ name: 'osd_max_backfills',
+ value: [
+ {
+ section: 'osd',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_max_active',
+ value: [
+ {
+ section: 'osd',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_max_single_start',
+ value: [
+ {
+ section: 'osd',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_sleep',
+ value: [
+ {
+ section: 'osd',
+ value: '0.5'
+ }
+ ]
+ }
+ ];
+
+ const configOptionsDefault = [
+ {
+ name: 'osd_max_backfills',
+ value: [
+ {
+ section: 'osd',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_max_active',
+ value: [
+ {
+ section: 'osd',
+ value: '3'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_max_single_start',
+ value: [
+ {
+ section: 'osd',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_sleep',
+ value: [
+ {
+ section: 'osd',
+ value: '0'
+ }
+ ]
+ }
+ ];
+
+ const configOptionsHigh = [
+ {
+ name: 'osd_max_backfills',
+ value: [
+ {
+ section: 'osd',
+ value: '4'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_max_active',
+ value: [
+ {
+ section: 'osd',
+ value: '4'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_max_single_start',
+ value: [
+ {
+ section: 'osd',
+ value: '4'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_sleep',
+ value: [
+ {
+ section: 'osd',
+ value: '0'
+ }
+ ]
+ }
+ ];
+
+ const configOptionsCustom = [
+ {
+ name: 'osd_max_backfills',
+ value: [
+ {
+ section: 'osd',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_max_active',
+ value: [
+ {
+ section: 'osd',
+ value: '2'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_max_single_start',
+ value: [
+ {
+ section: 'osd',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_sleep',
+ value: [
+ {
+ section: 'osd',
+ value: '0'
+ }
+ ]
+ }
+ ];
+
+ const configOptionsIncomplete = [
+ {
+ name: 'osd_max_backfills',
+ value: [
+ {
+ section: 'osd',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_max_single_start',
+ value: [
+ {
+ section: 'osd',
+ value: '1'
+ }
+ ]
+ },
+ {
+ name: 'osd_recovery_sleep',
+ value: [
+ {
+ section: 'osd',
+ value: '0'
+ }
+ ]
+ }
+ ];
+
+ it('should return priority "low" if the config option values have been set accordingly', fakeAsync(() => {
+ spyOn(configService, 'get').and.callFake((configOptionName: string) => {
+ const result = _.find(configOptionsLow, (configOption) => {
+ return configOption.name === configOptionName;
+ });
+ return of(result);
+ });
+
+ component.getStoredPriority((priority) => {
+ expect(priority.name).toBe('low');
+ });
+ tick();
+
+ expect(component.osdRecvSpeedForm.getValue('customizePriority')).toBeFalsy();
+ }));
+
+ it('should return priority "default" if the config option values have been set accordingly', fakeAsync(() => {
+ spyOn(configService, 'get').and.callFake((configOptionName: string) => {
+ const result = _.find(configOptionsDefault, (configOption) => {
+ return configOption.name === configOptionName;
+ });
+ return of(result);
+ });
+
+ component.getStoredPriority((priority) => {
+ expect(priority.name).toBe('default');
+ });
+ tick();
+
+ expect(component.osdRecvSpeedForm.getValue('customizePriority')).toBeFalsy();
+ }));
+
+ it('should return priority "high" if the config option values have been set accordingly', fakeAsync(() => {
+ spyOn(configService, 'get').and.callFake((configOptionName: string) => {
+ const result = _.find(configOptionsHigh, (configOption) => {
+ return configOption.name === configOptionName;
+ });
+ return of(result);
+ });
+
+ component.getStoredPriority((priority) => {
+ expect(priority.name).toBe('high');
+ });
+ tick();
+
+ expect(component.osdRecvSpeedForm.getValue('customizePriority')).toBeFalsy();
+ }));
+
+ it('should return priority "custom" if the config option values do not match any priority', fakeAsync(() => {
+ spyOn(configService, 'get').and.callFake((configOptionName: string) => {
+ const result = _.find(configOptionsCustom, (configOption) => {
+ return configOption.name === configOptionName;
+ });
+ return of(result);
+ });
+
+ component.getStoredPriority((priority) => {
+ expect(priority.name).toBe('custom');
+ });
+ tick();
+
+ expect(component.osdRecvSpeedForm.getValue('customizePriority')).toBeTruthy();
+ }));
+
+ it('should return no priority if the config option values are incomplete', fakeAsync(() => {
+ spyOn(configService, 'get').and.callFake((configOptionName: string) => {
+ const result = _.find(configOptionsIncomplete, (configOption) => {
+ return configOption.name === configOptionName;
+ });
+ return of(result);
+ });
+
+ component.getStoredPriority((priority) => {
+ expect(priority.name).toBeNull();
+ });
+ tick();
+
+ expect(component.osdRecvSpeedForm.getValue('customizePriority')).toBeFalsy();
+ }));
+ });
+});
--- /dev/null
+import { Component, OnInit } from '@angular/core';
+import { FormControl, Validators } from '@angular/forms';
+
+import { I18n } from '@ngx-translate/i18n-polyfill';
+import * as _ from 'lodash';
+import { BsModalRef } from 'ngx-bootstrap/modal';
+import { forkJoin as observableForkJoin, of } from 'rxjs';
+import { mergeMap } from 'rxjs/operators';
+
+import { ConfigurationService } from '../../../../shared/api/configuration.service';
+import { NotificationType } from '../../../../shared/enum/notification-type.enum';
+import { CdFormGroup } from '../../../../shared/forms/cd-form-group';
+import { NotificationService } from '../../../../shared/services/notification.service';
+import { OsdRecvSpeedModalPriorities } from './osd-recv-speed-modal.priorities';
+
+@Component({
+ selector: 'cd-osd-recv-speed-modal',
+ templateUrl: './osd-recv-speed-modal.component.html',
+ styleUrls: ['./osd-recv-speed-modal.component.scss']
+})
+export class OsdRecvSpeedModalComponent implements OnInit {
+ osdRecvSpeedForm: CdFormGroup;
+ priorities = OsdRecvSpeedModalPriorities.KNOWN_PRIORITIES;
+ priorityAttrs = [];
+
+ constructor(
+ public bsModalRef: BsModalRef,
+ private configService: ConfigurationService,
+ private notificationService: NotificationService,
+ private i18n: I18n
+ ) {
+ this.osdRecvSpeedForm = new CdFormGroup({
+ priority: new FormControl(null, { validators: [Validators.required] }),
+ customizePriority: new FormControl(false)
+ });
+ this.priorityAttrs = [
+ {
+ name: 'osd_max_backfills',
+ text: this.i18n('Max Backfills')
+ },
+ {
+ name: 'osd_recovery_max_active',
+ text: this.i18n('Recovery Max Active')
+ },
+ {
+ name: 'osd_recovery_max_single_start',
+ text: this.i18n('Recovery Max Single Start')
+ },
+ {
+ name: 'osd_recovery_sleep',
+ text: this.i18n('Recovery Sleep')
+ }
+ ];
+
+ this.priorityAttrs.forEach((attr) => {
+ this.osdRecvSpeedForm.addControl(
+ attr.name,
+ new FormControl(null, { validators: [Validators.required] })
+ );
+ });
+ }
+
+ ngOnInit() {
+ this.getStoredPriority((priority) => {
+ this.setPriority(priority);
+ });
+ }
+
+ setPriority(priority: any) {
+ const customPriority = _.find(this.priorities, (p) => {
+ return p.name === 'custom';
+ });
+
+ if (priority.name === 'custom') {
+ if (!customPriority) {
+ this.priorities.push(priority);
+ }
+ } else {
+ if (customPriority) {
+ this.priorities.splice(this.priorities.indexOf(customPriority), 1);
+ }
+ }
+
+ this.osdRecvSpeedForm.controls.priority.setValue(priority.name);
+ Object.entries(priority.values).forEach(([name, value]) => {
+ this.osdRecvSpeedForm.controls[name].setValue(value);
+ });
+ }
+
+ onCustomizePriorityChange() {
+ if (this.osdRecvSpeedForm.getValue('customizePriority')) {
+ const values = {};
+ this.priorityAttrs.forEach((attr) => {
+ values[attr.name] = this.osdRecvSpeedForm.getValue(attr.name);
+ });
+ const customPriority = {
+ name: 'custom',
+ text: this.i18n('Custom'),
+ values: values
+ };
+ this.setPriority(customPriority);
+ } else {
+ this.setPriority(this.priorities[0]);
+ }
+ }
+
+ getStoredPriority(callbackFn: Function) {
+ const observables = [];
+ this.priorityAttrs.forEach((configName) => {
+ observables.push(this.configService.get(configName.name));
+ });
+
+ observableForkJoin(observables)
+ .pipe(
+ mergeMap((configOptions) => {
+ const result = {};
+ configOptions.forEach((configOption) => {
+ if (configOption && 'value' in configOption) {
+ configOption.value.forEach((value) => {
+ if (value['section'] === 'osd') {
+ result[configOption.name] = Number(value.value);
+ }
+ });
+ }
+ });
+ return of(result);
+ })
+ )
+ .subscribe((resp) => {
+ const priority = _.find(this.priorities, (p) => {
+ return _.isEqual(p.values, resp);
+ });
+
+ this.osdRecvSpeedForm.controls.customizePriority.setValue(false);
+
+ if (priority) {
+ return callbackFn(priority);
+ }
+
+ if (Object.entries(resp).length === 4) {
+ this.osdRecvSpeedForm.controls.customizePriority.setValue(true);
+ return callbackFn(Object({ name: 'custom', text: this.i18n('Custom'), values: resp }));
+ }
+
+ return callbackFn(this.priorities[0]);
+ });
+ }
+
+ onPriorityChange(selectedPriorityName) {
+ const selectedPriority =
+ _.find(this.priorities, (p) => {
+ return p.name === selectedPriorityName;
+ }) || this.priorities[0];
+
+ this.setPriority(selectedPriority);
+ }
+
+ submitAction() {
+ const options = {};
+ this.priorityAttrs.forEach((attr) => {
+ options[attr.name] = { section: 'osd', value: this.osdRecvSpeedForm.getValue(attr.name) };
+ });
+
+ this.configService.bulkCreate({ options: options }).subscribe(
+ () => {
+ this.notificationService.show(
+ NotificationType.success,
+ this.i18n('OSD recovery speed priority "{{value}}" was set successfully.', {
+ value: this.osdRecvSpeedForm.getValue('priority')
+ }),
+ this.i18n('OSD recovery speed priority')
+ );
+ this.bsModalRef.hide();
+ },
+ () => {
+ this.bsModalRef.hide();
+ }
+ );
+ }
+}
--- /dev/null
+export class OsdRecvSpeedModalPriorities {
+ public static KNOWN_PRIORITIES = [
+ // TODO: I18n
+ {
+ name: null,
+ text: '-- Select the priority --',
+ values: {
+ osd_max_backfills: null,
+ osd_recovery_max_active: null,
+ osd_recovery_max_single_start: null,
+ osd_recovery_sleep: null
+ }
+ },
+ {
+ name: 'low',
+ text: 'Low',
+ values: {
+ osd_max_backfills: 1,
+ osd_recovery_max_active: 1,
+ osd_recovery_max_single_start: 1,
+ osd_recovery_sleep: 0.5
+ }
+ },
+ {
+ name: 'default',
+ text: 'Default',
+ values: {
+ osd_max_backfills: 1,
+ osd_recovery_max_active: 3,
+ osd_recovery_max_single_start: 1,
+ osd_recovery_sleep: 0
+ }
+ },
+ {
+ name: 'high',
+ text: 'High',
+ values: {
+ osd_max_backfills: 4,
+ osd_recovery_max_active: 4,
+ osd_recovery_max_single_start: 4,
+ osd_recovery_sleep: 0
+ }
+ }
+ ];
+}
create(configOption: ConfigFormCreateRequestModel) {
return this.http.post('api/cluster_conf/', configOption);
}
+
+ bulkCreate(configOptions: Object) {
+ return this.http.put('api/cluster_conf', configOptions);
+ }
}
<context context-type="sourcefile">app/ceph/block/rbd-form/rbd-form.component.html</context>
<context context-type="linenumber">175</context>
</context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html</context>
+ <context context-type="linenumber">31</context>
+ </context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/cluster/osd/osd-reweight-modal/osd-reweight-modal.component.html</context>
<context context-type="linenumber">18</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/cluster/osd/osd-list/osd-list.component.html</context>
- <context context-type="linenumber">50</context>
+ <context context-type="linenumber">59</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/pool/pool-list/pool-list.component.html</context>
<context context-type="sourcefile">app/ceph/cluster/osd/osd-flags-modal/osd-flags-modal.component.html</context>
<context context-type="linenumber">38</context>
</context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html</context>
+ <context context-type="linenumber">77</context>
+ </context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/cluster/osd/osd-reweight-modal/osd-reweight-modal.component.html</context>
<context context-type="linenumber">34</context>
<context context-type="sourcefile">app/ceph/cluster/osd/osd-flags-modal/osd-flags-modal.component.html</context>
<context context-type="linenumber">34</context>
</context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html</context>
+ <context context-type="linenumber">73</context>
+ </context-group>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/cluster/osd/osd-scrub-modal/osd-scrub-modal.component.html</context>
<context context-type="linenumber">21</context>
</context-group>
+ </trans-unit><trans-unit id="c35f9c5f268a514b970cc55e9a5dc4bed0988e7f" datatype="html">
+ <source>OSD Recovery Priority</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html</context>
+ <context context-type="linenumber">3</context>
+ </context-group>
+ </trans-unit><trans-unit id="b74af38005e8a8914e45af2ec412e11ceafef8b6" datatype="html">
+ <source>Priority</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html</context>
+ <context context-type="linenumber">16</context>
+ </context-group>
+ </trans-unit><trans-unit id="c2f48f04b379bfba133825747adfd238d511412e" datatype="html">
+ <source>Customize priority values</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html</context>
+ <context context-type="linenumber">44</context>
+ </context-group>
+ </trans-unit><trans-unit id="4aa19de2a2b54cbda39e9c62917b23044c087776" datatype="html">
+ <source>This field is required!</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.html</context>
+ <context context-type="linenumber">65</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
+ <context context-type="linenumber">34</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
+ <context context-type="linenumber">68</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
+ <context context-type="linenumber">92</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
+ <context context-type="linenumber">118</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
+ <context context-type="linenumber">166</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/pool-form/pool-form.component.html</context>
+ <context context-type="linenumber">40</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/pool-form/pool-form.component.html</context>
+ <context context-type="linenumber">69</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/pool/pool-form/pool-form.component.html</context>
+ <context context-type="linenumber">95</context>
+ </context-group>
</trans-unit><trans-unit id="272696ff8acdbed0af6dc13f09729e70be435b82" datatype="html">
<source>Reweight OSD</source>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/cluster/osd/osd-scrub-modal/osd-scrub-modal.component.html</context>
<context context-type="linenumber">13</context>
</context-group>
- </trans-unit><trans-unit id="5e5d841e1b1db3f5d3cf9a7549dea652e08856ed" datatype="html">
- <source>Set Cluster-wide OSD Flags</source>
+ </trans-unit><trans-unit id="4beacb61e2236200b5627677b5c0054d2b482ed4" datatype="html">
+ <source>Set Cluster-wide Flags</source>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/cluster/osd/osd-list/osd-list.component.html</context>
<context context-type="linenumber">23</context>
</context-group>
+ </trans-unit><trans-unit id="9617df8e0504d997d0ff45b6c206a12becd13c37" datatype="html">
+ <source>Set Cluster-wide Recovery Priority</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">app/ceph/cluster/osd/osd-list/osd-list.component.html</context>
+ <context context-type="linenumber">32</context>
+ </context-group>
</trans-unit><trans-unit id="b49d7877d24112d4bdfce9256edf61a007fae888" datatype="html">
<source>OSDs List</source>
<context-group purpose="location">
<x id="START_TAG_STRONG" ctype="x-strong" equiv-text="<strong>"/><x id="INTERPOLATION_1" equiv-text="{{ markActionDescription }}"/><x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="</strong>"/> if you proceed.</source>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/cluster/osd/osd-list/osd-list.component.html</context>
- <context context-type="linenumber">65</context>
+ <context context-type="linenumber">74</context>
</context-group>
</trans-unit><trans-unit id="2d3a73f6440a7d896d74356fe0a725d731e71cbb" datatype="html">
<source>The OSD is not safe to destroy!</source>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/cluster/osd/osd-list/osd-list.component.html</context>
- <context context-type="linenumber">74</context>
+ <context context-type="linenumber">83</context>
</context-group>
</trans-unit><trans-unit id="9d08116242443953ebbfe10bc2092e0a694b4adf" datatype="html">
<source><x id="START_TAG_STRONG" ctype="x-strong" equiv-text="<strong>"/>OSD <x id="INTERPOLATION" equiv-text="{{ selection.first().id }}"/><x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="</strong>"/> will be
<x id="START_TAG_STRONG" ctype="x-strong" equiv-text="<strong>"/><x id="INTERPOLATION_1" equiv-text="{{ actionDescription }}"/><x id="CLOSE_TAG_STRONG" ctype="x-strong" equiv-text="</strong>"/> if you proceed.</source>
<context-group purpose="location">
<context context-type="sourcefile">app/ceph/cluster/osd/osd-list/osd-list.component.html</context>
- <context context-type="linenumber">76</context>
+ <context context-type="linenumber">85</context>
</context-group>
</trans-unit><trans-unit id="d2bcd3296d2850de762fb943060b7e086a893181" datatype="html">
<source>Health</source>
<context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
<context context-type="linenumber">3</context>
</context-group>
- </trans-unit><trans-unit id="4aa19de2a2b54cbda39e9c62917b23044c087776" datatype="html">
- <source>This field is required!</source>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
- <context context-type="linenumber">34</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
- <context context-type="linenumber">68</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
- <context context-type="linenumber">92</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
- <context context-type="linenumber">118</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form.component.html</context>
- <context context-type="linenumber">166</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/pool/pool-form/pool-form.component.html</context>
- <context context-type="linenumber">40</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/pool/pool-form/pool-form.component.html</context>
- <context context-type="linenumber">69</context>
- </context-group>
- <context-group purpose="location">
- <context context-type="sourcefile">app/ceph/pool/pool-form/pool-form.component.html</context>
- <context context-type="linenumber">95</context>
- </context-group>
</trans-unit><trans-unit id="9edc2b494e660618af3e5225f68d40b7ca67629c" datatype="html">
<source>The chosen erasure code profile name is already in use.</source>
<context-group purpose="location">
<context context-type="linenumber">1</context>
</context-group>
</trans-unit>
+ <trans-unit id="4edc2162af0bd6cd941eaf730d20a7e3b5a83ba7" datatype="html">
+ <source>Max Backfills</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
+ <trans-unit id="eb5e6c24a48a1dfe23e0a772b762be5e949c0c8a" datatype="html">
+ <source>Recovery Max Active</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
+ <trans-unit id="4a23c6d3859e03184118620d2baeb736ac765fbf" datatype="html">
+ <source>Recovery Max Single Start</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
+ <trans-unit id="97049169ef5228fd1724de2fcdad1fec2858eaf8" datatype="html">
+ <source>Recovery Sleep</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
+ <trans-unit id="a5c05002b0ac2040f1aede5e727e0ffd06eda819" datatype="html">
+ <source>Custom</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
+ <trans-unit id="92a8cbd0b31b1a5a64fe3a3245437ecf14b6d74d" datatype="html">
+ <source>OSD recovery speed priority "<x id="INTERPOLATION" equiv-text="{{value}}"/>" was set successfully.</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
+ <trans-unit id="87241d8eb7bf0d14756f27febe8d0a84015627db" datatype="html">
+ <source>OSD recovery speed priority</source>
+ <context-group purpose="location">
+ <context context-type="sourcefile">src/app/ceph/cluster/osd/osd-recv-speed-modal/osd-recv-speed-modal.component.ts</context>
+ <context context-type="linenumber">1</context>
+ </context-group>
+ </trans-unit>
<trans-unit id="442db228d78b94d704b22bb0ffeb712af183f19a" datatype="html">
<source><x id="INTERPOLATION" equiv-text="{{operation}}"/> was initialized in the following OSD: <x id="INTERPOLATION_1" equiv-text="{{id}}"/></source>
<context-group purpose="location">