import { LOCALE_ID, TRANSLATIONS, TRANSLATIONS_FORMAT, Type } from '@angular/core';
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
import { AbstractControl } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { I18n } from '@ngx-translate/i18n-polyfill';
+import { configureTestSuite } from 'ng-bullet';
import { BsModalRef } from 'ngx-bootstrap/modal';
import { TableActionsComponent } from '../app/shared/datatable/table-actions/table-actions.component';
import { CdFormGroup } from '../app/shared/forms/cd-form-group';
import { CdTableAction } from '../app/shared/models/cd-table-action';
import { CdTableSelection } from '../app/shared/models/cd-table-selection';
+import { CrushNode } from '../app/shared/models/crush-node';
+import { CrushRule, CrushRuleConfig } from '../app/shared/models/crush-rule';
import { Permission } from '../app/shared/models/permissions';
import {
AlertmanagerAlert,
AlertmanagerNotificationAlert,
PrometheusRule
} from '../app/shared/models/prometheus-alerts';
-import { _DEV_ } from '../unit-test-configuration';
-
-export function configureTestBed(configuration: any, useOldMethod?: boolean) {
- if (_DEV_ && !useOldMethod) {
- const resetTestingModule = TestBed.resetTestingModule;
- beforeAll((done) =>
- (async () => {
- TestBed.resetTestingModule();
- TestBed.configureTestingModule(configuration);
- // prevent Angular from resetting testing module
- TestBed.resetTestingModule = () => TestBed;
- })()
- .then(done)
- .catch(done.fail)
- );
- afterAll(() => {
- TestBed.resetTestingModule = resetTestingModule;
- });
- } else {
- beforeEach(async(() => {
- TestBed.configureTestingModule(configuration);
- }));
- }
+
+export function configureTestBed(configuration: any) {
+ configureTestSuite(() => TestBed.configureTestingModule(configuration));
}
export class PermissionHelper {
formHelper.expectErrorChange(fieldName, 'thisPasswordIsWayTooBig', 'pattern');
}
}
+
+export class Mocks {
+ static getCrushNode(
+ name: string,
+ id: number,
+ type: string,
+ type_id: number,
+ children?: number[],
+ device_class?: string
+ ): CrushNode {
+ return { name, type, type_id, id, children, device_class };
+ }
+
+ /**
+ * Create the following test crush map:
+ * > default
+ * --> ssd-host
+ * ----> 3x osd with ssd
+ * --> mix-host
+ * ----> hdd-rack
+ * ------> 2x osd-rack with hdd
+ * ----> ssd-rack
+ * ------> 2x osd-rack with ssd
+ */
+ static getCrushMap(): CrushNode[] {
+ return [
+ // Root node
+ this.getCrushNode('default', -1, 'root', 11, [-2, -3]),
+ // SSD host
+ this.getCrushNode('ssd-host', -2, 'host', 1, [1, 0, 2]),
+ this.getCrushNode('osd.0', 0, 'osd', 0, undefined, 'ssd'),
+ this.getCrushNode('osd.1', 1, 'osd', 0, undefined, 'ssd'),
+ this.getCrushNode('osd.2', 2, 'osd', 0, undefined, 'ssd'),
+ // SSD and HDD mixed devices host
+ this.getCrushNode('mix-host', -3, 'host', 1, [-4, -5]),
+ // HDD rack
+ this.getCrushNode('hdd-rack', -4, 'rack', 3, [3, 4]),
+ this.getCrushNode('osd2.0', 3, 'osd-rack', 0, undefined, 'hdd'),
+ this.getCrushNode('osd2.1', 4, 'osd-rack', 0, undefined, 'hdd'),
+ // SSD rack
+ this.getCrushNode('ssd-rack', -5, 'rack', 3, [5, 6]),
+ this.getCrushNode('osd3.0', 5, 'osd-rack', 0, undefined, 'ssd'),
+ this.getCrushNode('osd3.1', 6, 'osd-rack', 0, undefined, 'ssd')
+ ];
+ }
+
+ /**
+ * Generates an simple crush map with multiple hosts that have OSDs with either ssd or hdd OSDs.
+ * Hosts with zero or even numbers at the end have SSD OSDs the other hosts have hdd OSDs.
+ *
+ * Host names follow the following naming convention:
+ * host.$index
+ * $index represents a number count started at 0 (like an index within an array) (same for OSDs)
+ *
+ * OSD names follow the following naming convention:
+ * osd.$hostIndex.$osdIndex
+ *
+ * The following crush map will be generated with the set defaults:
+ * > default
+ * --> host.0 (has only ssd OSDs)
+ * ----> osd.0.0
+ * ----> osd.0.1
+ * ----> osd.0.2
+ * ----> osd.0.3
+ * --> host.1 (has only hdd OSDs)
+ * ----> osd.1.0
+ * ----> osd.1.1
+ * ----> osd.1.2
+ * ----> osd.1.3
+ */
+ static generateSimpleCrushMap(hosts: number = 2, osds: number = 4): CrushNode[] {
+ const nodes = [];
+ const createOsdLeafs = (hostSuffix: number): number[] => {
+ let osdId = 0;
+ const osdIds = [];
+ const osdsInUse = hostSuffix * osds;
+ for (let o = 0; o < osds; o++) {
+ osdIds.push(osdId);
+ nodes.push(
+ this.getCrushNode(
+ `osd.${hostSuffix}.${osdId}`,
+ osdId + osdsInUse,
+ 'osd',
+ 0,
+ undefined,
+ hostSuffix % 2 === 0 ? 'ssd' : 'hdd'
+ )
+ );
+ osdId++;
+ }
+ return osdIds;
+ };
+ const createHostBuckets = (): number[] => {
+ let hostId = -2;
+ const hostIds = [];
+ for (let h = 0; h < hosts; h++) {
+ const hostSuffix = hostId * -1 - 2;
+ hostIds.push(hostId);
+ nodes.push(
+ this.getCrushNode(`host.${hostSuffix}`, hostId, 'host', 1, createOsdLeafs(hostSuffix))
+ );
+ hostId--;
+ }
+ return hostIds;
+ };
+ nodes.push(this.getCrushNode('default', -1, 'root', 11, createHostBuckets()));
+ return nodes;
+ }
+
+ static getCrushRuleConfig(
+ name: string,
+ root: string,
+ failure_domain: string,
+ device_class?: string
+ ): CrushRuleConfig {
+ return {
+ name,
+ root,
+ failure_domain,
+ device_class
+ };
+ }
+
+ static getCrushRule({
+ id = 0,
+ name = 'somePoolName',
+ min = 1,
+ max = 10,
+ type = 'replicated',
+ failureDomain = 'osd',
+ itemName = 'default' // This string also sets the device type - "default~ssd" <- ssd usage only
+ }: {
+ max?: number;
+ min?: number;
+ id?: number;
+ name?: string;
+ type?: string;
+ failureDomain?: string;
+ itemName?: string;
+ }): CrushRule {
+ const typeNumber = type === 'erasure' ? 3 : 1;
+ const rule = new CrushRule();
+ rule.max_size = max;
+ rule.min_size = min;
+ rule.rule_id = id;
+ rule.ruleset = typeNumber;
+ rule.rule_name = name;
+ rule.steps = [
+ {
+ item_name: itemName,
+ item: -1,
+ op: 'take'
+ },
+ {
+ num: 0,
+ type: failureDomain,
+ op: 'choose_firstn'
+ },
+ {
+ op: 'emit'
+ }
+ ];
+ return rule;
+ }
+}