]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/pybind/mgr/dashboard/frontend/src/testing/unit-test-helper.ts
import 15.2.5
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / testing / unit-test-helper.ts
index e7d8b6fdd5c108b0b691df4539c308c85259a7a2..81047f906377875b702311ffde261c3a505d78a8 100644 (file)
@@ -1,9 +1,10 @@
 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';
@@ -11,6 +12,8 @@ import { Icons } from '../app/shared/enum/icons.enum';
 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,
@@ -18,29 +21,9 @@ import {
   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 {
@@ -394,3 +377,168 @@ export class IscsiHelper {
     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;
+  }
+}