use of com.yahoo.vespa.hosted.provision.Node in project vespa by vespa-engine.
the class RetiredExpirer method canRemove.
/**
* Checks if the node can be removed:
* if the node is a docker host, it will only be removed if it has no children,
* or all its children are parked or failed
* Otherwise, a removal is allowed if either of these are true:
* - The node has been in state {@link History.Event.Type#retired} for longer than {@link #retiredExpiry}
* - Orchestrator allows it
*/
private boolean canRemove(Node node) {
if (node.type().isDockerHost()) {
return nodeRepository().getChildNodes(node.hostname()).stream().allMatch(child -> child.state() == Node.State.parked || child.state() == Node.State.failed);
}
Optional<Instant> timeOfRetiredEvent = node.history().event(History.Event.Type.retired).map(History.Event::at);
Optional<Instant> retireAfter = timeOfRetiredEvent.map(retiredEvent -> retiredEvent.plus(retiredExpiry));
boolean shouldRetireNowBecauseExpried = retireAfter.map(time -> time.isBefore(clock.instant())).orElse(false);
if (shouldRetireNowBecauseExpried) {
return true;
}
try {
orchestrator.acquirePermissionToRemove(new HostName(node.hostname()));
return true;
} catch (OrchestrationException e) {
log.info("Did not get permission to remove retired " + node + ": " + e.getMessage());
return false;
}
}
use of com.yahoo.vespa.hosted.provision.Node in project vespa by vespa-engine.
the class CuratorDatabaseClient method writeTo.
/**
* Writes the given nodes to the given state (whether or not they are already in this state or another),
* and returns a copy of the incoming nodes in their persisted state.
*
* @param toState the state to write the nodes to
* @param nodes the list of nodes to write
* @param agent the agent causing this change
* @return the nodes in their persisted state
*/
public List<Node> writeTo(Node.State toState, List<Node> nodes, Agent agent, Optional<String> reason) {
try (NestedTransaction nestedTransaction = new NestedTransaction()) {
List<Node> writtenNodes = writeTo(toState, nodes, agent, reason, nestedTransaction);
nestedTransaction.commit();
return writtenNodes;
}
}
use of com.yahoo.vespa.hosted.provision.Node in project vespa by vespa-engine.
the class Activator method updateFrom.
/**
* Returns the input nodes with the changes resulting from applying the settings in hosts to the given list of nodes.
*/
private List<Node> updateFrom(Collection<HostSpec> hosts, List<Node> nodes) {
List<Node> updated = new ArrayList<>();
for (Node node : nodes) {
HostSpec hostSpec = getHost(node.hostname(), hosts);
node = hostSpec.membership().get().retired() ? node.retire(nodeRepository.clock().instant()) : node.unretire();
node = node.with(node.allocation().get().with(hostSpec.membership().get()));
if (// Docker nodes may change flavor
hostSpec.flavor().isPresent())
node = node.with(hostSpec.flavor().get());
updated.add(node);
}
return updated;
}
use of com.yahoo.vespa.hosted.provision.Node in project vespa by vespa-engine.
the class NodePrioritizer method toNodePriority.
/**
* Convert a list of nodes to a list of node priorities. This includes finding, calculating
* parameters to the priority sorting procedure.
*/
private PrioritizableNode toNodePriority(Node node, boolean isSurplusNode, boolean isNewNode) {
PrioritizableNode pri = new PrioritizableNode();
pri.node = node;
pri.isSurplusNode = isSurplusNode;
pri.isNewNode = isNewNode;
pri.preferredOnFlavor = requestedNodes.specifiesNonStockFlavor() && node.flavor().equals(getFlavor(requestedNodes));
pri.parent = findParentNode(node);
if (pri.parent.isPresent()) {
Node parent = pri.parent.get();
pri.freeParentCapacity = capacity.freeCapacityOf(parent, false);
if (spareHosts.contains(parent)) {
pri.violatesSpares = true;
}
if (headroomHosts.containsKey(parent) && isPreferredNodeToBeReloacted(allNodes, node, parent)) {
ResourceCapacity neededCapacity = headroomHosts.get(parent);
// If the node is new then we need to check the headroom requirement after it has been added
if (isNewNode) {
neededCapacity = ResourceCapacity.composite(neededCapacity, new ResourceCapacity(node));
}
pri.violatesHeadroom = !capacity.hasCapacity(parent, neededCapacity);
}
}
return pri;
}
use of com.yahoo.vespa.hosted.provision.Node in project vespa by vespa-engine.
the class NodePrioritizer method addNewDockerNodes.
/**
* Add a node on each docker host with enough capacity for the requested flavor
*/
void addNewDockerNodes() {
if (!isDocker)
return;
DockerHostCapacity capacity = new DockerHostCapacity(allNodes);
ResourceCapacity wantedResourceCapacity = ResourceCapacity.of(getFlavor(requestedNodes));
NodeList list = new NodeList(allNodes);
for (Node node : allNodes) {
if (node.type() != NodeType.host)
continue;
if (node.state() != Node.State.active)
continue;
if (node.status().wantToRetire())
continue;
boolean hostHasCapacityForWantedFlavor = capacity.hasCapacity(node, wantedResourceCapacity);
boolean conflictingCluster = list.childrenOf(node).owner(appId).asList().stream().anyMatch(child -> child.allocation().get().membership().cluster().id().equals(clusterSpec.id()));
if (!hostHasCapacityForWantedFlavor || conflictingCluster)
continue;
log.log(LogLevel.DEBUG, "Trying to add new Docker node on " + node);
Set<String> ipAddresses = DockerHostCapacity.findFreeIps(node, allNodes);
if (ipAddresses.isEmpty())
continue;
String ipAddress = ipAddresses.stream().findFirst().get();
Optional<String> hostname = nameResolver.getHostname(ipAddress);
if (!hostname.isPresent()) {
log.log(LogLevel.DEBUG, "Could not find hostname for " + ipAddress + ", skipping it");
continue;
}
Node newNode = Node.createDockerNode("fake-" + hostname.get(), Collections.singleton(ipAddress), Collections.emptySet(), hostname.get(), Optional.of(node.hostname()), getFlavor(requestedNodes), NodeType.tenant);
PrioritizableNode nodePri = toNodePriority(newNode, false, true);
if (!nodePri.violatesSpares || isAllocatingForReplacement) {
log.log(LogLevel.DEBUG, "Adding new Docker node " + newNode);
nodes.put(newNode, nodePri);
}
}
}
Aggregations