aboutsummaryrefslogtreecommitdiffstats
path: root/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/provisioning/NodeCandidateTest.java
blob: fac1111e6164625cc1a3e053d47549e10b52c2fd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.provision.provisioning;

import com.yahoo.config.provision.Flavor;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.NodeType;
import com.yahoo.vespa.hosted.provision.Node;
import com.yahoo.vespa.hosted.provision.node.IP;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import static org.junit.Assert.assertEquals;

/**
 * @author bratseth
 */
public class NodeCandidateTest {

    @Test
    public void testOrdering() {
        List<NodeCandidate> expected = List.of(
                new NodeCandidate.ConcreteNodeCandidate(node("01", Node.State.ready), false, new NodeResources(2, 2, 2, 2), Optional.empty(), false, true, true, false, false),
                new NodeCandidate.ConcreteNodeCandidate(node("02", Node.State.active), false, new NodeResources(2, 2, 2, 2), Optional.empty(), true, true, false, false, false),
                new NodeCandidate.ConcreteNodeCandidate(node("04", Node.State.reserved), false, new NodeResources(2, 2, 2, 2), Optional.empty(), true, true, false, false, false),
                new NodeCandidate.ConcreteNodeCandidate(node("03", Node.State.inactive), false, new NodeResources(2, 2, 2, 2), Optional.empty(), true, true, false, false, false),
                new NodeCandidate.ConcreteNodeCandidate(node("05", Node.State.ready), false, new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.active)), true, true, false, true, false),
                new NodeCandidate.ConcreteNodeCandidate(node("06", Node.State.ready), false, new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.ready)), true, true, false, true, false),
                new NodeCandidate.ConcreteNodeCandidate(node("07", Node.State.ready), false, new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.provisioned)), true, true, false, true, false),
                new NodeCandidate.ConcreteNodeCandidate(node("08", Node.State.ready), false, new NodeResources(2, 2, 2, 2), Optional.of(node("host1", Node.State.failed)), true, true, false, true, false),
                new NodeCandidate.ConcreteNodeCandidate(node("09", Node.State.ready), false, new NodeResources(1, 1, 1, 1), Optional.empty(), true, true, false, true, false),
                new NodeCandidate.ConcreteNodeCandidate(node("10", Node.State.ready), false, new NodeResources(2, 2, 2, 2), Optional.empty(), true, true, false, true, false),
                new NodeCandidate.ConcreteNodeCandidate(node("11", Node.State.ready), false, new NodeResources(2, 2, 2, 2), Optional.empty(), true, true, false, true, false)
        );
        assertOrder(expected);
    }

    @Test
    public void testOrderingByAllocationSkew1() {
        List<NodeCandidate> expected = List.of(
                node("1", node(4, 4), host(20, 20), host(40, 40)),
                node("2", node(4, 4), host(21, 20), host(40, 40)),
                node("3", node(4, 4), host(22, 20), host(40, 40)),
                node("4", node(4, 4), host(21, 22), host(40, 40)),
                node("5", node(4, 4), host(21, 21), host(40, 80))
        );
        assertOrder(expected);
    }

    @Test
    public void testOrderingByAllocationSkew2() {
        // The same as testOrderingByAllocationSkew1, but deviating from mean (20) in the other direction.
        // Since we don't choose the node with the lowest skew, but with the largest skew *reduction*
        // this causes the opposite order.
        List<NodeCandidate> expected = List.of(
                node("4", node(4, 4), host(19, 18), host(40, 40)),
                node("3", node(4, 4), host(18, 20), host(40, 40)),
                node("2", node(4, 4), host(19, 20), host(40, 40)),
                node("1", node(4, 4), host(20, 20), host(40, 40)),
                node("5", node(4, 4), host(19, 19), host(40, 80))
        );
        assertOrder(expected);
    }

    @Test
    public void testOrderingByAllocationSkew3() {
        // The same as testOrderingByAllocationSkew1, but allocating skewed towards cpu
        List<NodeCandidate> expected = List.of(
                node("1", node(4, 2), host(20, 20), host(40, 40)),
                node("2", node(4, 2), host(21, 20), host(40, 40)),
                node("4", node(4, 2), host(21, 22), host(40, 40)),
                node("3", node(4, 2), host(22, 20), host(40, 40)),
                node("5", node(4, 2), host(21, 21), host(40, 80))
        );
        assertOrder(expected);
    }

    @Test
    public void testOrderingByAllocationSkew4() {
        // The same as testOrderingByAllocationSkew1, but allocating skewed towards memory
        List<NodeCandidate> expected = List.of(
                node("5", node(2, 10), host(21, 21), host(40, 80)),
                node("3", node(2, 10), host(22, 20), host(40, 40)),
                node("2", node(2, 10), host(21, 20), host(40, 40)),
                node("1", node(2, 10), host(20, 20), host(40, 40)),
                node("4", node(2, 10), host(21, 22), host(40, 40))
        );
        assertOrder(expected);
    }

    @Test
    public void testOrderingByAllocationSkew5() {
        // node1 is skewed towards cpu (without this allocation), allocation is skewed towards memory, therefore
        // node 1 is preferred (even though it is still most skewed)
        List<NodeCandidate> expected = List.of(
                node("1", node(1, 5), host(21, 10), host(40, 40)),
                node("2", node(1, 5), host(21, 20), host(40, 40)),
                node("3", node(1, 5), host(20, 20), host(40, 40)),
                node("4", node(1, 5), host(20, 22), host(40, 40))
        );
        assertOrder(expected);
    }

    @Test
    public void testOrderingByExclusiveSwitch() {
        List<NodeCandidate> expected = List.of(
                node("1", true),
                node("2", true),
                node("3", false),
                node("4", false),
                node("5", false)
        );
        assertOrder(expected);
    }

    private void assertOrder(List<NodeCandidate> expected) {
        List<NodeCandidate> copy = new ArrayList<>(expected);
        Collections.shuffle(copy);
        Collections.sort(copy);
        assertEquals(expected, copy);
    }

    private static NodeResources node(double vcpu, double mem) {
        return new NodeResources(vcpu, mem, 0, 0);
    }

    private static NodeResources host(double vcpu, double mem) {
        return new NodeResources(vcpu, mem, 10, 10);
    }

    private static Node node(String hostname, Node.State state) {
        return Node.create(hostname, hostname, new Flavor(new NodeResources(2, 2, 2, 2)), state, NodeType.tenant)
                .ipConfigWithEmptyPool(List.of("::1")).build();
    }

    private static NodeCandidate node(String hostname,
                                      NodeResources nodeResources,
                                      NodeResources allocatedHostResources, // allocated before adding nodeResources
                                      NodeResources totalHostResources,
                                      boolean exclusiveSwitch) {
        Node node = Node.create(hostname, hostname, new Flavor(nodeResources), Node.State.ready, NodeType.tenant)
                .parentHostname(hostname + "parent")
                .ipConfigWithEmptyPool(List.of("::1")).build();
        Node parent = Node.create(hostname + "parent", hostname, new Flavor(totalHostResources), Node.State.ready, NodeType.host)
                          .ipConfig(IP.Config.of(List.of("::1"), List.of("::2")))
                          .build();
        return new NodeCandidate.ConcreteNodeCandidate(node, false, totalHostResources.subtract(allocatedHostResources), Optional.of(parent),
                                                       false, exclusiveSwitch, false, true, false);
    }

    private static NodeCandidate node(String hostname, NodeResources nodeResources,
                                      NodeResources allocatedHostResources, NodeResources totalHostResources) {
        return node(hostname, nodeResources, allocatedHostResources, totalHostResources, false);
    }

    private static NodeCandidate node(String hostname, boolean exclusiveSwitch) {
        return node(hostname,
                    node(2, 10),
                    host(20, 20),
                    host(40, 40),
                    exclusiveSwitch);
    }

}