/** Returns true if this filter matches the given host properties */ public boolean matches(String hostname, String flavor, Optional<ClusterMembership> membership) { if ( ! hostnames.isEmpty() && ! hostnames.contains(hostname)) return false; if ( ! flavors.isEmpty() && ! flavors.contains(flavor)) return false; if ( ! clusterTypes.isEmpty() && ! (membership.isPresent() && clusterTypes.contains(membership.get().cluster().type()))) return false; if ( ! clusterIds.isEmpty() && ! (membership.isPresent() && clusterIds.contains(membership.get().cluster().id()))) return false; return true; }
private List<Container> createNodesFromHosts(DeployLogger deployLogger, Map<HostResource, ClusterMembership> hosts, ContainerCluster cluster) { List<Container> nodes = new ArrayList<>(); for (Map.Entry<HostResource, ClusterMembership> entry : hosts.entrySet()) { String id = "container." + entry.getValue().index(); Container container = new Container(cluster, id, entry.getValue().retired(), entry.getValue().index(), cluster.isHostedVespa()); container.setHostResource(entry.getKey()); container.initService(deployLogger); nodes.add(container); } return nodes; }
/** Returns a copy of this which is retired */ public Allocation retire() { return new Allocation(owner, clusterMembership.retire(), restartGeneration, removable); }
private Node move(Node node, Node.State toState, Agent agent, Optional<String> reason) { if (toState == Node.State.active && ! node.allocation().isPresent()) throw new IllegalArgumentException("Could not set " + node.hostname() + " active. It has no allocation."); try (Mutex lock = lock(node)) { if (toState == Node.State.active) { for (Node currentActive : getNodes(node.allocation().get().owner(), Node.State.active)) { if (node.allocation().get().membership().cluster().equals(currentActive.allocation().get().membership().cluster()) && node.allocation().get().membership().index() == currentActive.allocation().get().membership().index()) throw new IllegalArgumentException("Could not move " + node + " to active:" + "It has the same cluster and index as an existing node"); } } return db.writeTo(toState, node, agent, reason); } }
private void toSlime(ClusterMembership membership, Cursor object) { object.setString("clustertype", membership.cluster().type().name()); object.setString("clusterid", membership.cluster().id().value()); object.setString("group", String.valueOf(membership.cluster().group().get().index())); object.setLong("index", membership.index()); object.setBool("retired", membership.retired()); }
ClusterMembership membership = offered.allocation().get().membership(); if ( ! offered.allocation().get().owner().equals(application)) continue; // wrong application if ( ! membership.cluster().equalsIgnoringGroupAndVespaVersion(cluster)) continue; // wrong cluster id/type if ((! offeredPriority.isSurplusNode || saturated()) && ! membership.cluster().group().equals(cluster.group())) continue; // wrong group and we can't or have no reason to change it if ( offered.allocation().get().isRemovable()) continue; // don't accept; causes removal if ( indexes.contains(membership.index())) continue; // duplicate index (just to be sure) ClusterMembership.from(cluster, highestIndex.add(1)), nodeRepository.clock().instant()); accepted.add(acceptNode(offeredPriority, false));
int currentRetiredCount = (int) nodes.stream().filter(node -> node.node.allocation().get().membership().retired()).count(); int deltaRetiredCount = requestedNodes.idealRetiredCount(nodes.size(), currentRetiredCount) - currentRetiredCount; if ( ! node.node.allocation().get().membership().retired() && node.node.state().equals(Node.State.active)) { node.node = node.node.retire(Agent.application, nodeRepository.clock().instant()); if ( node.node.allocation().get().membership().retired() && hasCompatibleFlavor(node.node)) { node.node = node.node.unretire(); if (++deltaRetiredCount == 0) break; .with(allocation.membership().cluster().exclusive(requestedNodes.isExclusive()))));
private void toSlime(HostSpec host, Cursor cursor) { cursor.setString(hostSpecHostName, host.hostname()); host.membership().ifPresent(membership -> { cursor.setString(hostSpecMembership, membership.stringValue()); cursor.setString(hostSpecVespaVersion, membership.cluster().vespaVersion().toFullString()); }); host.flavor().ifPresent(flavor -> cursor.setString(hostSpecFlavor, flavor.name())); host.version().ifPresent(version -> cursor.setString(hostSpecCurrentVespaVersion, version.toFullString())); }
@Override public List<HostSpec> prepare(ClusterSpec cluster, Capacity capacity, int groups, ProvisionLogger logger) { // TODO: This should fail if capacity requested is more than 1 List<HostSpec> hosts = new ArrayList<>(); hosts.add(new HostSpec(host.hostname(), host.aliases(), ClusterMembership.from(cluster, counter++))); return hosts; }
/** Move nodes from unwanted groups to wanted groups to avoid lingering groups consisting of retired nodes */ private void moveToActiveGroup(List<Node> surplusNodes, int wantedGroups, Optional<ClusterSpec.Group> targetGroup) { for (ListIterator<Node> i = surplusNodes.listIterator(); i.hasNext(); ) { Node node = i.next(); ClusterMembership membership = node.allocation().get().membership(); ClusterSpec cluster = membership.cluster(); if (cluster.group().get().index() >= wantedGroups) { ClusterSpec.Group newGroup = targetGroup.orElse(ClusterSpec.Group.from(0)); ClusterMembership newGroupMembership = membership.with(cluster.with(Optional.of(newGroup))); i.set(node.with(node.allocation().get().with(newGroupMembership))); } } }
/** Returns the subset of nodes which are not retired */ public NodeList nonretired() { return filter(node -> ! node.allocation().get().membership().retired()); }
/** * 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) { Optional<ClusterMembership> thisMembership = this.primaryClusterMembership(); Optional<ClusterMembership> otherMembership = other.primaryClusterMembership(); if (thisMembership.isPresent() && otherMembership.isPresent()) return Integer.compare(thisMembership.get().index(), otherMembership.get().index()); else return this.getHostname().compareTo(other.getHostname()); }
@Override public String toString() { return stringValue(); }
private ClusterMembership(String stringValue, Version vespaVersion) { String[] components = stringValue.split("/"); if (components.length < 4 || components.length > 7) throw new RuntimeException("Could not parse '" + stringValue + "' to a cluster membership. " + "Expected 'clusterType/clusterId/groupId/index[/retired][/exclusive][/rotationId,...]'"); boolean exclusive = false; Set<RotationName> rotations = Collections.emptySet(); if (components.length > 4) { for (int i = 4; i < components.length; i++) { String component = components[i]; switch (component) { case "exclusive": exclusive = true; break; case "retired": retired = true; break; default: rotations = rotationsFrom(component); break; } } } this.cluster = ClusterSpec.from(ClusterSpec.Type.valueOf(components[0]), ClusterSpec.Id.from(components[1]), ClusterSpec.Group.from(Integer.valueOf(components[2])), vespaVersion, exclusive, rotations); this.index = Integer.parseInt(components[3]); this.stringValue = toStringValue(); }
/** Returns a copy of this which is retired */ public ClusterMembership retire() { return new ClusterMembership(cluster, index, true); }
protected String toStringValue() { return cluster.type().name() + "/" + cluster.id().value() + (cluster.group().isPresent() ? "/" + cluster.group().get().index() : "") + "/" + index + ( cluster.isExclusive() ? "/exclusive" : "") + ( retired ? "/retired" : "") + ( !cluster.rotations().isEmpty() ? "/" + rotationsAsString(cluster.rotations()) : ""); }
/** * Returns the highest index number of all active and failed nodes in this cluster, or -1 if there are no nodes. * We include failed nodes to avoid reusing the index of the failed node in the case where the failed node is the * node with the highest index. */ private int findHighestIndex(ApplicationId application, ClusterSpec cluster) { int highestIndex = -1; for (Node node : nodeRepository.getNodes(application, Node.State.active, Node.State.inactive, Node.State.parked, Node.State.failed)) { ClusterSpec nodeCluster = node.allocation().get().membership().cluster(); if ( ! nodeCluster.id().equals(cluster.id())) continue; if ( ! nodeCluster.type().equals(cluster.type())) continue; highestIndex = Math.max(node.allocation().get().membership().index(), highestIndex); } return highestIndex; }
private void toSlime(Allocation allocation, Cursor object) { object.setString(tenantIdKey, allocation.owner().tenant().value()); object.setString(applicationIdKey, allocation.owner().application().value()); object.setString(instanceIdKey, allocation.owner().instance().value()); object.setString(serviceIdKey, allocation.membership().stringValue()); object.setLong(restartGenerationKey, allocation.restartGeneration().wanted()); object.setLong(currentRestartGenerationKey, allocation.restartGeneration().current()); object.setBool(removableKey, allocation.isRemovable()); object.setString(wantedVespaVersionKey, allocation.membership().cluster().vespaVersion().toString()); }
private ClusterMembership clusterMembershipFromSlime(Inspector object) { return ClusterMembership.from(object.field(serviceIdKey).asString(), versionFromSlime(object.field(wantedVespaVersionKey)).get()); }
/** Returns the subset of nodes which are retired */ public NodeList retired() { return filter(node -> node.allocation().get().membership().retired()); }