summaryrefslogtreecommitdiffstats
path: root/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeRepositoryImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeRepositoryImpl.java')
-rw-r--r--node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeRepositoryImpl.java172
1 files changed, 172 insertions, 0 deletions
diff --git a/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeRepositoryImpl.java b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeRepositoryImpl.java
new file mode 100644
index 00000000000..f2152dffc0c
--- /dev/null
+++ b/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/configserver/noderepository/NodeRepositoryImpl.java
@@ -0,0 +1,172 @@
+// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.node.admin.configserver.noderepository;
+
+import com.yahoo.vespa.hosted.node.admin.ContainerAclSpec;
+import com.yahoo.vespa.hosted.node.admin.ContainerNodeSpec;
+import com.yahoo.vespa.hosted.dockerapi.ContainerName;
+import com.yahoo.vespa.hosted.dockerapi.DockerImage;
+import com.yahoo.vespa.hosted.node.admin.configserver.ConfigServerApi;
+import com.yahoo.vespa.hosted.node.admin.nodeagent.NodeAttributes;
+import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings.GetAclResponse;
+import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings.GetNodesResponse;
+import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings.NodeMessageResponse;
+import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings.UpdateNodeAttributesRequestBody;
+import com.yahoo.vespa.hosted.node.admin.configserver.noderepository.bindings.UpdateNodeAttributesResponse;
+import com.yahoo.vespa.hosted.node.admin.configserver.HttpException;
+import com.yahoo.vespa.hosted.node.admin.util.PrefixLogger;
+import com.yahoo.vespa.hosted.provision.Node;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+/**
+ * @author stiankri, dybis
+ */
+public class NodeRepositoryImpl implements NodeRepository {
+ private static final PrefixLogger NODE_ADMIN_LOGGER = PrefixLogger.getNodeAdminLogger(NodeRepositoryImpl.class);
+
+ private final ConfigServerApi configServerApi;
+
+ public NodeRepositoryImpl(ConfigServerApi configServerApi) {
+ this.configServerApi = configServerApi;
+ }
+
+ @Override
+ public List<ContainerNodeSpec> getContainersToRun(String baseHostName) {
+ final GetNodesResponse nodesForHost = configServerApi.get(
+ "/nodes/v2/node/?parentHost=" + baseHostName + "&recursive=true",
+ GetNodesResponse.class);
+
+ List<ContainerNodeSpec> nodes = new ArrayList<>(nodesForHost.nodes.size());
+ for (GetNodesResponse.Node node : nodesForHost.nodes) {
+ ContainerNodeSpec nodeSpec;
+ try {
+ nodeSpec = createContainerNodeSpec(node);
+ } catch (IllegalArgumentException | NullPointerException e) {
+ NODE_ADMIN_LOGGER.warning("Bad node received from node repo when requesting children of the "
+ + baseHostName + " host: " + node, e);
+ continue;
+ }
+ nodes.add(nodeSpec);
+ }
+ return nodes;
+ }
+
+ @Override
+ public Optional<ContainerNodeSpec> getContainerNodeSpec(String hostName) {
+ try {
+ GetNodesResponse.Node nodeResponse = configServerApi.get("/nodes/v2/node/" + hostName,
+ GetNodesResponse.Node.class);
+ if (nodeResponse == null) {
+ return Optional.empty();
+ }
+ return Optional.of(createContainerNodeSpec(nodeResponse));
+ } catch (HttpException.NotFoundException e) {
+ return Optional.empty();
+ }
+ }
+
+ @Override
+ public List<ContainerAclSpec> getContainerAclSpecs(String hostName) {
+ try {
+ final String path = String.format("/nodes/v2/acl/%s?children=true", hostName);
+ final GetAclResponse response = configServerApi.get(path, GetAclResponse.class);
+ return response.trustedNodes.stream()
+ .map(node -> new ContainerAclSpec(
+ node.hostname, node.ipAddress, ContainerName.fromHostname(node.trustedBy)))
+ .collect(Collectors.toList());
+ } catch (HttpException.NotFoundException e) {
+ return Collections.emptyList();
+ }
+ }
+
+ private static ContainerNodeSpec createContainerNodeSpec(GetNodesResponse.Node node)
+ throws IllegalArgumentException, NullPointerException {
+ Objects.requireNonNull(node.nodeState, "Unknown node state");
+ Node.State nodeState = Node.State.valueOf(node.nodeState);
+ if (nodeState == Node.State.active) {
+ Objects.requireNonNull(node.wantedVespaVersion, "Unknown vespa version for active node");
+ Objects.requireNonNull(node.wantedDockerImage, "Unknown docker image for active node");
+ Objects.requireNonNull(node.wantedRestartGeneration, "Unknown wantedRestartGeneration for active node");
+ Objects.requireNonNull(node.currentRestartGeneration, "Unknown currentRestartGeneration for active node");
+ }
+
+ String hostName = Objects.requireNonNull(node.hostname, "hostname is null");
+
+ ContainerNodeSpec.Owner owner = null;
+ if (node.owner != null) {
+ owner = new ContainerNodeSpec.Owner(node.owner.tenant, node.owner.application, node.owner.instance);
+ }
+
+ ContainerNodeSpec.Membership membership = null;
+ if (node.membership != null) {
+ membership = new ContainerNodeSpec.Membership(node.membership.clusterType, node.membership.clusterId,
+ node.membership.group, node.membership.index, node.membership.retired);
+ }
+
+ return new ContainerNodeSpec(
+ hostName,
+ Optional.ofNullable(node.wantedDockerImage).map(DockerImage::new),
+ Optional.ofNullable(node.currentDockerImage).map(DockerImage::new),
+ nodeState,
+ node.nodeType,
+ node.nodeFlavor,
+ node.nodeCanonicalFlavor,
+ Optional.ofNullable(node.wantedVespaVersion),
+ Optional.ofNullable(node.vespaVersion),
+ Optional.ofNullable(owner),
+ Optional.ofNullable(membership),
+ Optional.ofNullable(node.wantedRestartGeneration),
+ Optional.ofNullable(node.currentRestartGeneration),
+ Optional.ofNullable(node.wantedRebootGeneration),
+ Optional.ofNullable(node.currentRestartGeneration),
+ node.minCpuCores,
+ node.minMainMemoryAvailableGb,
+ node.minDiskAvailableGb,
+ node.fastDisk,
+ node.ipAddresses,
+ Optional.ofNullable(node.hardwareDivergence));
+ }
+
+ @Override
+ public void updateNodeAttributes(final String hostName, final NodeAttributes nodeAttributes) {
+ UpdateNodeAttributesResponse response = configServerApi.patch(
+ "/nodes/v2/node/" + hostName,
+ new UpdateNodeAttributesRequestBody(nodeAttributes),
+ UpdateNodeAttributesResponse.class);
+
+ if (response.errorCode == null || response.errorCode.isEmpty()) {
+ return;
+ }
+ throw new RuntimeException("Unexpected message " + response.message + " " + response.errorCode);
+ }
+
+ @Override
+ public void markAsDirty(String hostName) {
+ // This will never happen once the new allocation scheme is rolled out.
+ markNodeToState(hostName, Node.State.dirty.name());
+ }
+
+ @Override
+ public void markNodeAvailableForNewAllocation(final String hostName) {
+ // TODO replace with call to delete node when everything has been migrated to dynamic docker allocation
+ markNodeToState(hostName, "availablefornewallocations");
+ }
+
+ private void markNodeToState(String hostName, String state) {
+ NodeMessageResponse response = configServerApi.put(
+ "/nodes/v2/state/" + state + "/" + hostName,
+ Optional.empty(), /* body */
+ NodeMessageResponse.class);
+ NODE_ADMIN_LOGGER.info(response.message);
+
+ if (response.errorCode == null || response.errorCode.isEmpty()) {
+ return;
+ }
+ throw new RuntimeException("Unexpected message " + response.message + " " + response.errorCode);
+ }
+}