1 import { AbstractControl } from '@angular/forms';
3 import * as _ from 'lodash';
5 import { CrushNode } from '../models/crush-node';
7 export class CrushNodeSelectionClass {
8 private nodes: CrushNode[] = [];
9 private easyNodes: { [id: number]: CrushNode } = {};
10 private allDevices: string[] = [];
12 root: AbstractControl;
13 failure: AbstractControl;
14 device: AbstractControl;
17 buckets: CrushNode[] = [];
18 failureDomains: { [type: string]: CrushNode[] } = {};
19 failureDomainKeys: string[] = [];
20 devices: string[] = [];
23 initCrushNodeSelection(
25 rootControl: AbstractControl,
26 failureControl: AbstractControl,
27 deviceControl: AbstractControl
30 nodes.forEach((node) => {
31 this.easyNodes[node.id] = node;
33 this.buckets = _.sortBy(
34 nodes.filter((n) => n.children),
39 failure: failureControl,
43 this.controls.root.valueChanges.subscribe(() => this.onRootChange());
44 this.controls.failure.valueChanges.subscribe(() => this.onFailureDomainChange());
45 this.controls.device.valueChanges.subscribe(() => this.onDeviceChange());
48 private preSelectRoot() {
49 const rootNode = this.nodes.find((node) => node.type === 'root');
50 this.silentSet(this.controls.root, rootNode);
54 private silentSet(control: AbstractControl, value: any) {
55 control.setValue(value, { emitEvent: false });
58 private onRootChange() {
59 const nodes = this.getSubNodes(this.controls.root.value);
61 nodes.forEach((node) => {
62 if (!domains[node.type]) {
63 domains[node.type] = [];
65 domains[node.type].push(node);
67 Object.keys(domains).forEach((type) => {
68 if (domains[type].length <= 1) {
72 this.failureDomains = domains;
73 this.failureDomainKeys = Object.keys(domains).sort();
74 this.updateFailureDomain();
77 private getSubNodes(node: CrushNode): CrushNode[] {
78 let subNodes = [node]; // Includes parent node
82 node.children.forEach((id) => {
83 const childNode = this.easyNodes[id];
84 subNodes = subNodes.concat(this.getSubNodes(childNode));
89 private updateFailureDomain() {
90 let failureDomain = this.getIncludedCustomValue(
91 this.controls.failure,
92 Object.keys(this.failureDomains)
94 if (failureDomain === '') {
95 failureDomain = this.setMostCommonDomain(this.controls.failure);
97 this.updateDevices(failureDomain);
100 private getIncludedCustomValue(control: AbstractControl, includedIn: string[]) {
101 return control.dirty && includedIn.includes(control.value) ? control.value : '';
104 private setMostCommonDomain(failureControl: AbstractControl): string {
105 let winner = { n: 0, type: '' };
106 Object.keys(this.failureDomains).forEach((type) => {
107 const n = this.failureDomains[type].length;
109 winner = { n, type };
112 this.silentSet(failureControl, winner.type);
116 private onFailureDomainChange() {
117 this.updateDevices();
120 private updateDevices(failureDomain: string = this.controls.failure.value) {
121 const subNodes = _.flatten(
122 this.failureDomains[failureDomain].map((node) => this.getSubNodes(node))
124 this.allDevices = subNodes.filter((n) => n.device_class).map((n) => n.device_class);
125 this.devices = _.uniq(this.allDevices).sort();
127 this.devices.length === 1
129 : this.getIncludedCustomValue(this.controls.device, this.devices);
130 this.silentSet(this.controls.device, device);
131 this.onDeviceChange(device);
134 private onDeviceChange(deviceType: string = this.controls.device.value) {
137 ? this.allDevices.length
138 : this.allDevices.filter((type) => type === deviceType).length;