aboutsummaryrefslogtreecommitdiffstats
path: root/config-model/src/main/java/com/yahoo/vespa/model/HostResource.java
blob: 7aa8cb25a382b01037c42a7ff62e727ff294f690 (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
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.model;

import com.yahoo.config.application.api.DeployLogger;
import com.yahoo.config.model.api.HostInfo;
import com.yahoo.config.provision.HostSpec;
import com.yahoo.config.provision.NodeResources;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * A host representation. The identity of this is the identity of its Host.
 * TODO: Merge with {@link Host}
 * Host resources are ordered by their host order.
 *
 * @author Ulf Lilleengen
 */
public class HostResource implements Comparable<HostResource> {

    private final HostSpec spec;

    private final HostPorts hostPorts;

    private final Host host;

    /** Map from "sentinel name" to service */
    private final Map<String, Service> services = new LinkedHashMap<>();

    /**
     * Create a new {@link HostResource} bound to a specific {@link com.yahoo.vespa.model.Host}.
     *
     * @param host {@link com.yahoo.vespa.model.Host} object to bind to.
     */
    public HostResource(Host host) {
        this(host, new HostSpec(host.getHostname(), Optional.empty()));
    }

    public HostResource(Host host, HostSpec spec) {
        this.host = host;
        this.spec = spec;
        this.hostPorts = new HostPorts(host.getHostname());
    }

    /**
     * Return the currently bound {@link com.yahoo.vespa.model.Host}.
     *
     * @return the {@link com.yahoo.vespa.model.Host} if bound, null if not.
     */
    public Host getHost() { return host; }

    public HostPorts ports() { return hostPorts; }

    public HostSpec spec() { return spec.withPorts(hostPorts.networkPorts()); }

    /**
     * Adds service and allocates resources for it.
     *
     * @param service the Service to allocate resources for
     * @param wantedPort the wanted port for this service
     * @return the allocated ports for the Service.
     */
    List<Integer> allocateService(DeployLogger deployLogger, AbstractService service, int wantedPort) {
        ports().useLogger(deployLogger);
        List<Integer> ports = hostPorts.allocatePorts(service, wantedPort);
        if (getService(service.getServiceName()) != null)
            throw new IllegalStateException("There is already a service with name '" + service.getServiceName() +
                                            "' registered on " + this + ". " +
                                            "Most likely a programming error - " +
                                            "all service classes must have unique names, even in different packages!");

        services.put(service.getServiceName(), service);
        return ports;
    }

    void deallocateService(AbstractService service) {
        hostPorts.deallocatePorts(service);
        services.remove(service.getServiceName());
    }

    /**
     * Returns the service with the given "sentinel name" on this Host,
     * or null if the name does not match any service.
     *
     * @param sentinelName the sentinel name of the service we want to return
     * @return the service with the given sentinel name
     */
    public Service getService(String sentinelName) {
        return services.get(sentinelName);
    }

    /** Returns a List of all services running on this Host. */
    public List<Service> getServices() {
        return new ArrayList<>(services.values());
    }

    public HostInfo getHostInfo() {
        return new HostInfo(getHostname(), services.values().stream()
                .map(Service::getServiceInfo)
                .collect(Collectors.toCollection(() -> new LinkedHashSet<>())));
    }

    /** The real resources available for Vespa processes on this node, after subtracting infrastructure overhead. */
    public NodeResources realResources() { return spec.realResources(); }

    /** The total advertised resources of this node, typically matching what's requested. */
    public NodeResources advertisedResources() { return spec.advertisedResources(); }

    @Override
    public String toString() {
        return "host '" + host.getHostname() + "'";
    }

    public String getHostname() {
        return host.getHostname();
    }

    @Override
    public int hashCode() { return host.hashCode(); }

    @Override
    public boolean equals(Object other) {
        if (other == this) return true;
        if ( ! (other instanceof HostResource)) return false;
        return ((HostResource)other).host.equals(this.host);
    }

    @Override
    public int compareTo(HostResource other) {
        return this.host.compareTo(other.host);
    }

    /**
     * Compares by the index of the primary membership, if both hosts are members in at least one cluster at this time.
     * Compare by hostname otherwise.
     */
    public int comparePrimarilyByIndexTo(HostResource other) {
        if (this.spec.membership().isPresent() && other.spec.membership().isPresent())
            return Integer.compare(this.spec.membership().get().index(), other.spec.membership().get().index());
        else
            return this.getHostname().compareTo(other.getHostname());
    }

}