diff options
62 files changed, 1660 insertions, 820 deletions
diff --git a/config-model-api/abi-spec.json b/config-model-api/abi-spec.json index 28c04f38da5..9f86fe4dea2 100644 --- a/config-model-api/abi-spec.json +++ b/config-model-api/abi-spec.json @@ -326,7 +326,8 @@ "public java.lang.String containerId()", "public java.util.Set regions()", "public boolean equals(java.lang.Object)", - "public int hashCode()" + "public int hashCode()", + "public com.yahoo.config.application.api.Endpoint withRegions(java.util.Set)" ], "fields": [] }, diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java index ada5ee23a7c..5b5f89fd8d1 100644 --- a/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/DeploymentSpec.java @@ -74,11 +74,36 @@ public class DeploymentSpec { this.athenzDomain = athenzDomain; this.athenzService = athenzService; this.notifications = notifications; - this.endpoints = List.copyOf(Objects.requireNonNull(endpoints, "Missing endpoints parameter")); + this.endpoints = ImmutableList.copyOf(validateEndpoints(endpoints, this.steps)); validateZones(this.steps); validateAthenz(); validateEndpoints(this.steps, globalServiceId, this.endpoints); } + + /** Validates the endpoints and makes sure default values are respected */ + private List<Endpoint> validateEndpoints(List<Endpoint> endpoints, List<Step> steps) { + Objects.requireNonNull(endpoints, "Missing endpoints parameter"); + + var productionRegions = steps.stream() + .filter(step -> step.deploysTo(Environment.prod)) + .flatMap(step -> step.zones().stream()) + .flatMap(zone -> zone.region().stream()) + .map(RegionName::value) + .collect(Collectors.toSet()); + + var rebuiltEndpointsList = new ArrayList<Endpoint>(); + + for (var endpoint : endpoints) { + if (endpoint.regions().isEmpty()) { + var rebuiltEndpoint = endpoint.withRegions(productionRegions); + rebuiltEndpointsList.add(rebuiltEndpoint); + } else { + rebuiltEndpointsList.add(endpoint); + } + } + + return ImmutableList.copyOf(rebuiltEndpointsList); + } /** Throw an IllegalArgumentException if the total delay exceeds 24 hours */ private void validateTotalDelay(List<Step> steps) { diff --git a/config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java b/config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java index 158fbfb175f..e47dcd78219 100644 --- a/config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java +++ b/config-model-api/src/main/java/com/yahoo/config/application/api/Endpoint.java @@ -75,4 +75,7 @@ public class Endpoint { return Objects.hash(endpointId, containerId, regions); } + public Endpoint withRegions(Set<String> regions) { + return new Endpoint(endpointId, containerId, regions); + } } diff --git a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java b/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java index 8120c82e8f4..47eaf7a515a 100644 --- a/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java +++ b/config-model-api/src/test/java/com/yahoo/config/application/api/DeploymentSpecTest.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.Stream; import static com.yahoo.config.application.api.Notifications.Role.author; import static com.yahoo.config.application.api.Notifications.When.failing; @@ -516,6 +517,28 @@ public class DeploymentSpecTest { assertEquals(List.of("fooooooooooo"), endpointIds("<endpoint id='fooooooooooo' container-id='qrs'/>")); } + @Test + public void endpointDefaultRegions() { + var spec = DeploymentSpec.fromXml("" + + "<deployment>" + + " <prod>" + + " <region active=\"true\">us-east</region>" + + " <region active=\"true\">us-west</region>" + + " </prod>" + + " <endpoints>" + + " <endpoint id=\"foo\" container-id=\"bar\">" + + " <region>us-east</region>" + + " </endpoint>" + + " <endpoint id=\"nalle\" container-id=\"frosk\" />" + + " <endpoint container-id=\"quux\" />" + + " </endpoints>" + + "</deployment>"); + + assertEquals(Set.of("us-east"), endpointRegions("foo", spec)); + assertEquals(Set.of("us-east", "us-west"), endpointRegions("nalle", spec)); + assertEquals(Set.of("us-east", "us-west"), endpointRegions("default", spec)); + } + private static void assertInvalid(String endpointTag) { try { endpointIds(endpointTag); @@ -523,6 +546,14 @@ public class DeploymentSpecTest { } catch (IllegalArgumentException ignored) {} } + private static Set<String> endpointRegions(String endpointId, DeploymentSpec spec) { + return spec.endpoints().stream() + .filter(endpoint -> endpoint.endpointId().equals(endpointId)) + .flatMap(endpoint -> endpoint.regions().stream()) + .map(RegionName::value) + .collect(Collectors.toSet()); + } + private static List<String> endpointIds(String endpointTag) { var xml = "<deployment>" + " <prod>" + diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java index f290911c6bd..34a7d3c16cf 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/metricsproxy/MetricsProxyContainerCluster.java @@ -9,7 +9,7 @@ import ai.vespa.metricsproxy.core.MetricsConsumers; import ai.vespa.metricsproxy.core.MetricsManager; import ai.vespa.metricsproxy.core.MonitoringConfig; import ai.vespa.metricsproxy.core.VespaMetrics; -import ai.vespa.metricsproxy.http.GenericMetricsHandler; +import ai.vespa.metricsproxy.http.MetricsHandler; import ai.vespa.metricsproxy.metric.ExternalMetrics; import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensions; import ai.vespa.metricsproxy.metric.dimensions.ApplicationDimensionsConfig; @@ -49,8 +49,8 @@ import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerClus import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames.TENANT; import static com.yahoo.vespa.model.admin.metricsproxy.MetricsProxyContainerCluster.AppDimensionNames.ZONE; import static com.yahoo.vespa.model.admin.monitoring.DefaultPublicConsumer.getDefaultPublicConsumer; -import static com.yahoo.vespa.model.admin.monitoring.VespaMetricsConsumer.getVespaMetricsConsumer; import static com.yahoo.vespa.model.admin.monitoring.MetricSet.emptyMetricSet; +import static com.yahoo.vespa.model.admin.monitoring.VespaMetricsConsumer.getVespaMetricsConsumer; import static com.yahoo.vespa.model.container.xml.BundleMapper.JarSuffix.JAR_WITH_DEPS; import static com.yahoo.vespa.model.container.xml.BundleMapper.absoluteBundlePath; @@ -71,7 +71,7 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC static final Path METRICS_PROXY_BUNDLE_FILE = absoluteBundlePath((Paths.get(METRICS_PROXY_NAME + JAR_WITH_DEPS.suffix))); static final String METRICS_PROXY_BUNDLE_NAME = "com.yahoo.vespa." + METRICS_PROXY_NAME; - private static final String METRICS_HANDLER_BINDING = "/metrics/v1/values"; + private static final String METRICS_HANDLER_BINDING = "/metrics/v1"; static final class AppDimensionNames { static final String ZONE = "zone"; @@ -113,7 +113,7 @@ public class MetricsProxyContainerCluster extends ContainerCluster<MetricsProxyC private void addGenericMetricsHandler() { Handler<AbstractConfigProducer<?>> metricsHandler = new Handler<>( - new ComponentModel(GenericMetricsHandler.class.getName(), null, METRICS_PROXY_BUNDLE_NAME, null)); + new ComponentModel(MetricsHandler.class.getName(), null, METRICS_PROXY_BUNDLE_NAME, null)); metricsHandler.addServerBindings("http://*" + METRICS_HANDLER_BINDING, "http://*" + METRICS_HANDLER_BINDING + "/*"); addComponent(metricsHandler); diff --git a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicConsumer.java b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicConsumer.java index fbd1c7455dd..9c75b80c2f4 100644 --- a/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicConsumer.java +++ b/config-model/src/main/java/com/yahoo/vespa/model/admin/monitoring/DefaultPublicConsumer.java @@ -4,7 +4,7 @@ package com.yahoo.vespa.model.admin.monitoring; -import ai.vespa.metricsproxy.http.GenericMetricsHandler; +import ai.vespa.metricsproxy.http.ValuesFetcher; import com.google.common.collect.ImmutableList; import static com.yahoo.vespa.model.admin.monitoring.DefaultPublicMetrics.defaultPublicMetricSet; @@ -17,7 +17,7 @@ import static java.util.Collections.emptyList; */ public class DefaultPublicConsumer { - public static final String DEFAULT_PUBLIC_CONSUMER_ID = GenericMetricsHandler.DEFAULT_PUBLIC_CONSUMER_ID.id; + public static final String DEFAULT_PUBLIC_CONSUMER_ID = ValuesFetcher.DEFAULT_PUBLIC_CONSUMER_ID.id; private static final MetricSet publicConsumerMetrics = new MetricSet("public-consumer-metrics", emptyList(), diff --git a/config-model/src/main/javacc/SDParser.jj b/config-model/src/main/javacc/SDParser.jj index 6dde12f0fac..3141f7f7164 100644 --- a/config-model/src/main/javacc/SDParser.jj +++ b/config-model/src/main/javacc/SDParser.jj @@ -297,7 +297,7 @@ TOKEN : | < MUTABLE: "mutable" > | < FASTSEARCH: "fast-search" > | < HUGE: "huge" > -| < TENSOR_TYPE: "tensor(" (~["(",")"])+ ")" > +| < TENSOR_TYPE: "tensor" ("<" (~["<",">"])+ ">")? "(" (~["(",")"])+ ")" > | < TENSOR_VALUE_SL: "value" (" ")* ":" (" ")* ("{"<BRACE_SL_LEVEL_1>) ("\n")? > | < TENSOR_VALUE_ML: "value" (<SEARCHLIB_SKIP>)? "{" (["\n"," "])* ("{"<BRACE_ML_LEVEL_1>) (["\n"," "])* "}" ("\n")? > | < COMPRESSION: "compression" > diff --git a/config-model/src/test/derived/tensor/attributes.cfg b/config-model/src/test/derived/tensor/attributes.cfg index 4b54e67f8b8..f1c95da7084 100644 --- a/config-model/src/test/derived/tensor/attributes.cfg +++ b/config-model/src/test/derived/tensor/attributes.cfg @@ -61,3 +61,24 @@ attribute[].upperbound 9223372036854775807 attribute[].densepostinglistthreshold 0.4 attribute[].tensortype "tensor(x[10],y[20])" attribute[].imported false +attribute[].name "f5" +attribute[].datatype TENSOR +attribute[].collectiontype SINGLE +attribute[].removeifzero false +attribute[].createifnonexistent false +attribute[].fastsearch false +attribute[].huge false +attribute[].ismutable false +attribute[].sortascending true +attribute[].sortfunction UCA +attribute[].sortstrength PRIMARY +attribute[].sortlocale "" +attribute[].enablebitvectors false +attribute[].enableonlybitvector false +attribute[].fastaccess false +attribute[].arity 8 +attribute[].lowerbound -9223372036854775808 +attribute[].upperbound 9223372036854775807 +attribute[].densepostinglistthreshold 0.4 +attribute[].tensortype "tensor<float>(x[10])" +attribute[].imported false diff --git a/config-model/src/test/derived/tensor/documenttypes.cfg b/config-model/src/test/derived/tensor/documenttypes.cfg index 3b85e9b3a58..0b75644f4ae 100644 --- a/config-model/src/test/derived/tensor/documenttypes.cfg +++ b/config-model/src/test/derived/tensor/documenttypes.cfg @@ -40,6 +40,11 @@ documenttype[].datatype[].sstruct.field[].id 1224191509 documenttype[].datatype[].sstruct.field[].id_v6 1039544782 documenttype[].datatype[].sstruct.field[].datatype 21 documenttype[].datatype[].sstruct.field[].detailedtype "tensor(x[10],y[20])" +documenttype[].datatype[].sstruct.field[].name "f5" +documenttype[].datatype[].sstruct.field[].id 329055840 +documenttype[].datatype[].sstruct.field[].id_v6 105711688 +documenttype[].datatype[].sstruct.field[].datatype 21 +documenttype[].datatype[].sstruct.field[].detailedtype "tensor<float>(x[10])" documenttype[].datatype[].id -1903234535 documenttype[].datatype[].type STRUCT documenttype[].datatype[].array.element.id 0 @@ -59,3 +64,4 @@ documenttype[].fieldsets{[document]}.fields[] "f1" documenttype[].fieldsets{[document]}.fields[] "f2" documenttype[].fieldsets{[document]}.fields[] "f3" documenttype[].fieldsets{[document]}.fields[] "f4" +documenttype[].fieldsets{[document]}.fields[] "f5" diff --git a/config-model/src/test/derived/tensor/rank-profiles.cfg b/config-model/src/test/derived/tensor/rank-profiles.cfg index 471343da63c..87c0b6fab42 100644 --- a/config-model/src/test/derived/tensor/rank-profiles.cfg +++ b/config-model/src/test/derived/tensor/rank-profiles.cfg @@ -5,6 +5,8 @@ rankprofile[].fef.property[].name "vespa.type.attribute.f3" rankprofile[].fef.property[].value "tensor(x{})" rankprofile[].fef.property[].name "vespa.type.attribute.f4" rankprofile[].fef.property[].value "tensor(x[10],y[20])" +rankprofile[].fef.property[].name "vespa.type.attribute.f5" +rankprofile[].fef.property[].value "tensor<float>(x[10])" rankprofile[].name "unranked" rankprofile[].fef.property[].name "vespa.rank.firstphase" rankprofile[].fef.property[].value "value(0)" @@ -20,6 +22,8 @@ rankprofile[].fef.property[].name "vespa.type.attribute.f3" rankprofile[].fef.property[].value "tensor(x{})" rankprofile[].fef.property[].name "vespa.type.attribute.f4" rankprofile[].fef.property[].value "tensor(x[10],y[20])" +rankprofile[].fef.property[].name "vespa.type.attribute.f5" +rankprofile[].fef.property[].value "tensor<float>(x[10])" rankprofile[].name "profile1" rankprofile[].fef.property[].name "vespa.rank.firstphase" rankprofile[].fef.property[].value "rankingExpression(firstphase)" @@ -31,6 +35,8 @@ rankprofile[].fef.property[].name "vespa.type.attribute.f3" rankprofile[].fef.property[].value "tensor(x{})" rankprofile[].fef.property[].name "vespa.type.attribute.f4" rankprofile[].fef.property[].value "tensor(x[10],y[20])" +rankprofile[].fef.property[].name "vespa.type.attribute.f5" +rankprofile[].fef.property[].value "tensor<float>(x[10])" rankprofile[].name "profile2" rankprofile[].fef.property[].name "vespa.rank.firstphase" rankprofile[].fef.property[].value "rankingExpression(firstphase)" @@ -42,6 +48,8 @@ rankprofile[].fef.property[].name "vespa.type.attribute.f3" rankprofile[].fef.property[].value "tensor(x{})" rankprofile[].fef.property[].name "vespa.type.attribute.f4" rankprofile[].fef.property[].value "tensor(x[10],y[20])" +rankprofile[].fef.property[].name "vespa.type.attribute.f5" +rankprofile[].fef.property[].value "tensor<float>(x[10])" rankprofile[].name "profile3" rankprofile[].fef.property[].name "rankingExpression(joinedtensors).rankingScript" rankprofile[].fef.property[].value "tensor(i[10])(i) * attribute(f4)" @@ -57,3 +65,18 @@ rankprofile[].fef.property[].name "vespa.type.attribute.f3" rankprofile[].fef.property[].value "tensor(x{})" rankprofile[].fef.property[].name "vespa.type.attribute.f4" rankprofile[].fef.property[].value "tensor(x[10],y[20])" +rankprofile[].fef.property[].name "vespa.type.attribute.f5" +rankprofile[].fef.property[].value "tensor<float>(x[10])" +rankprofile[].name "profile4" +rankprofile[].fef.property[].name "vespa.rank.firstphase" +rankprofile[].fef.property[].value "rankingExpression(firstphase)" +rankprofile[].fef.property[].name "rankingExpression(firstphase).rankingScript" +rankprofile[].fef.property[].value "reduce(attribute(f5), sum)" +rankprofile[].fef.property[].name "vespa.type.attribute.f2" +rankprofile[].fef.property[].value "tensor(x[2],y[])" +rankprofile[].fef.property[].name "vespa.type.attribute.f3" +rankprofile[].fef.property[].value "tensor(x{})" +rankprofile[].fef.property[].name "vespa.type.attribute.f4" +rankprofile[].fef.property[].value "tensor(x[10],y[20])" +rankprofile[].fef.property[].name "vespa.type.attribute.f5" +rankprofile[].fef.property[].value "tensor<float>(x[10])" diff --git a/config-model/src/test/derived/tensor/summary.cfg b/config-model/src/test/derived/tensor/summary.cfg index 5e5507836b0..903b6033297 100644 --- a/config-model/src/test/derived/tensor/summary.cfg +++ b/config-model/src/test/derived/tensor/summary.cfg @@ -1,5 +1,5 @@ -defaultsummaryid 289405525 -classes[].id 289405525 +defaultsummaryid 898020074 +classes[].id 898020074 classes[].name "default" classes[].fields[].name "f1" classes[].fields[].type "tensor" @@ -7,13 +7,15 @@ classes[].fields[].name "f3" classes[].fields[].type "tensor" classes[].fields[].name "f4" classes[].fields[].type "tensor" +classes[].fields[].name "f5" +classes[].fields[].type "tensor" classes[].fields[].name "rankfeatures" classes[].fields[].type "featuredata" classes[].fields[].name "summaryfeatures" classes[].fields[].type "featuredata" classes[].fields[].name "documentid" classes[].fields[].type "longstring" -classes[].id 1860420340 +classes[].id 193983608 classes[].name "attributeprefetch" classes[].fields[].name "f2" classes[].fields[].type "tensor" @@ -21,6 +23,8 @@ classes[].fields[].name "f3" classes[].fields[].type "tensor" classes[].fields[].name "f4" classes[].fields[].type "tensor" +classes[].fields[].name "f5" +classes[].fields[].type "tensor" classes[].fields[].name "rankfeatures" classes[].fields[].type "featuredata" classes[].fields[].name "summaryfeatures" diff --git a/config-model/src/test/derived/tensor/tensor.sd b/config-model/src/test/derived/tensor/tensor.sd index e3e8cf43347..622be033229 100644 --- a/config-model/src/test/derived/tensor/tensor.sd +++ b/config-model/src/test/derived/tensor/tensor.sd @@ -8,12 +8,15 @@ search tensor { field f2 type tensor(x[2],y[]) { indexing: attribute } - field f3 type tensor(x{}) { + field f3 type tensor<double>(x{}) { indexing: attribute | summary } field f4 type tensor(x[10],y[20]) { indexing: attribute | summary } + field f5 type tensor<float>(x[10]) { + indexing: attribute | summary + } } rank-profile profile1 { @@ -44,4 +47,12 @@ search tensor { } + rank-profile profile4 { + + first-phase { + expression: sum(attribute(f5)) + } + + } + } diff --git a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java index c8640a6e074..192488c11ab 100644 --- a/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java +++ b/configserver/src/main/java/com/yahoo/vespa/config/server/ApplicationRepository.java @@ -462,6 +462,9 @@ public class ApplicationRepository implements com.yahoo.config.provision.Deploye long sessionId = getSessionIdForApplication(tenant, applicationId); RemoteSession session = tenant.getRemoteSessionRepo().getSession(sessionId, 0); return session.ensureApplicationLoaded().getForVersionOrLatest(version, clock.instant()); + } catch (NotFoundException e) { + log.log(LogLevel.WARNING, "Failed getting application for '" + applicationId + "': " + e.getMessage()); + throw e; } catch (Exception e) { log.log(LogLevel.WARNING, "Failed getting application for '" + applicationId + "'", e); throw e; diff --git a/configserver/src/main/sh/vespa-configserver-remove-state b/configserver/src/main/sh/vespa-configserver-remove-state index faac37d48d4..c5cee479388 100755 --- a/configserver/src/main/sh/vespa-configserver-remove-state +++ b/configserver/src/main/sh/vespa-configserver-remove-state @@ -80,6 +80,9 @@ usage() { echo "The following options are recognized:" echo "" + echo "-h|-help) print this help text" + echo "-nosudo do not use sudo when running command" + echo "-sudo use sudo when running command" echo "-force do not ask for confirmation before removal" ) >&2 } @@ -101,7 +104,7 @@ while [ $# -gt 0 ]; do -h|-help) usage; exit 0;; -nosudo) shift; sudo="" ;; -sudo) shift; sudo="sudo" ;; - -force) shift; ask=false ;; + -force) shift; ask=false ;; *) echo "Unrecognized option '$1'" >&2; usage; exit 1;; esac done diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java index 95544c23db5..916a388692e 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java @@ -5,8 +5,13 @@ import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.NodeType; import com.yahoo.config.provision.zone.ZoneId; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeList; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeState; +import java.util.Collection; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -17,8 +22,38 @@ import java.util.stream.Collectors; */ public interface NodeRepository { + void addNodes(ZoneId zone, Collection<NodeRepositoryNode> nodes); + + void deleteNode(ZoneId zone, String hostname); + + void setState(ZoneId zone, NodeState nodeState, String nodename); + + NodeRepositoryNode getNode(ZoneId zone, String hostname); + + NodeList listNodes(ZoneId zone); + + NodeList listNodes(ZoneId zone, ApplicationId application); + /** List all nodes in zone owned by given application */ - List<Node> list(ZoneId zone, ApplicationId application); + default List<Node> list(ZoneId zone, ApplicationId application) { + return listNodes(zone, application).nodes().stream() + .map(n -> new Node(com.yahoo.config.provision.HostName.from(n.getHostname()), + fromJacksonState(n.getState()), + fromJacksonType(n.getType()), Optional.of(application), + Version.fromString(n.getVespaVersion()), + Version.fromString(n.getWantedVespaVersion()), + Version.fromString(n.getCurrentOsVersion()), + Version.fromString(n.getWantedOsVersion()), + fromBoolean(n.getAllowedToBeDown()), + n.getCurrentRestartGeneration(), + n.getRestartGeneration(), + n.getCurrentRebootGeneration(), + n.getRebootGeneration(), + n.getCanonicalFlavor(), + n.getMembership().clusterid, + clusterTypeOf(n.getMembership().clustertype))) + .collect(Collectors.toUnmodifiableList()); + } /** List all nodes in states, in zone owned by given application */ default List<Node> list(ZoneId zone, ApplicationId application, Set<Node.State> states) { @@ -39,4 +74,47 @@ public interface NodeRepository { /** Cancels firmware checks on all hosts in the given zone. */ void cancelFirmwareCheck(ZoneId zone); + + + private Node.ClusterType clusterTypeOf(String type) { + switch (type) { + case "admin": return Node.ClusterType.admin; + case "content": return Node.ClusterType.content; + case "container": return Node.ClusterType.container; + default: throw new IllegalArgumentException("Unknown cluster type '" + type + "'."); + } + } + + // Convert Jackson type to config.provision type + private static NodeType fromJacksonType(com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeType nodeType) { + switch (nodeType) { + case tenant: return NodeType.tenant; + case host: return NodeType.host; + case proxy: return NodeType.proxy; + case proxyhost: return NodeType.proxyhost; + case config: return NodeType.config; + case confighost: return NodeType.confighost; + default: throw new IllegalArgumentException("Unknown type: " + nodeType); + } + } + + private static com.yahoo.vespa.hosted.controller.api.integration.configserver.Node.State fromJacksonState(NodeState state) { + switch (state) { + case provisioned: return Node.State.provisioned; + case ready: return Node.State.ready; + case reserved: return Node.State.reserved; + case active: return Node.State.active; + case inactive: return Node.State.inactive; + case dirty: return Node.State.dirty; + case failed: return Node.State.failed; + case parked: return Node.State.parked; + default: throw new IllegalArgumentException("Unknown state: " + state); + } + } + + private static Node.ServiceState fromBoolean(Boolean allowedDown) { + return (allowedDown == null) + ? Node.ServiceState.unorchestrated + : allowedDown ? Node.ServiceState.allowedDown : Node.ServiceState.expectedUp; + } } diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryClientInterface.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryClientInterface.java deleted file mode 100644 index 4b495ebf331..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepositoryClientInterface.java +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.vespa.hosted.controller.api.integration.noderepository; - -import com.yahoo.config.provision.zone.ZoneId; - -import java.io.IOException; -import java.util.Collection; - -/** - * A complete client for the node repository REST API. - * - * @author smorgrav - * @author bjorncs - */ -// TODO: Get rid of all the checked exceptions -// TODO: Replace remaining controller-server usages of this with -// com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository and move this package back to internal -// repo -public interface NodeRepositoryClientInterface { - - enum WantTo { - Retire, - Deprovision - } - - void addNodes(ZoneId zone, Collection<NodeRepositoryNode> nodes) throws IOException; - - NodeRepositoryNode getNode(ZoneId zone, String hostname) throws IOException; - - void deleteNode(ZoneId zone, String hostname) throws IOException; - - NodeList listNodes(ZoneId zone, boolean recursive) throws IOException; - - NodeList listNodes(ZoneId zone, String tenant, String applicationId, String instance) throws IOException; - - String resetFailureInformation(ZoneId zone, String nodename) throws IOException; - - String restart(ZoneId zone, String nodename) throws IOException; - - String reboot(ZoneId zone, String nodename) throws IOException; - - String cancelReboot(ZoneId zone, String nodename) throws IOException; - - String wantTo(ZoneId zone, String nodename, WantTo... actions) throws IOException; - - String cancelRestart(ZoneId zone, String nodename) throws IOException; - - String setHardwareFailureDescription(ZoneId zone, String nodename, String hardwareFailureDescription) throws IOException; - - void setState(ZoneId zone, NodeState nodeState, String nodename) throws IOException; - - String enableMaintenanceJob(ZoneId zone, String jobName) throws IOException; - - String disableMaintenanceJob(ZoneId zone, String jobName) throws IOException; - - MaintenanceJobList listMaintenanceJobs(ZoneId zone) throws IOException; - -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java index 1ec75e0c998..08702027264 100644 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java +++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/role/PathGroup.java @@ -76,7 +76,8 @@ enum PathGroup { "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/logs", "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/suspended", "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/service/{*}", - "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/global-rotation/{*}"), + "/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/global-rotation/{*}", + "/application/v4/tenant/{tenant}/application/{application}/metering"), /** Path used to restart development nodes. */ developmentRestart(Matcher.tenant, diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v1/ZoneApi.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v1/ZoneApi.java deleted file mode 100644 index 7bb4bfc6467..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v1/ZoneApi.java +++ /dev/null @@ -1,35 +0,0 @@ -// 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.controller.api.zone.v1; - -import javax.ws.rs.Consumes; -import javax.ws.rs.GET; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.core.MediaType; -import java.util.List; - -/** - * Used by build system and command-line tool. - * - * @author smorgrav - */ -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -@Path(ZoneApi.API_VERSION) -public interface ZoneApi { - - String API_VERSION = "v1"; - - @GET - @Path("") - List<ZoneReference.Environment> listEnvironments(); - - @GET - @Path("/environment/{environment}") - List<ZoneReference.Region> listRegions(@PathParam("environment") String env); - - @GET - @Path("/environment/{environment}/default") - ZoneReference.Region defaultRegion(@PathParam("environment") String env); -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v1/ZoneReference.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v1/ZoneReference.java deleted file mode 100644 index 82d03d72acd..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v1/ZoneReference.java +++ /dev/null @@ -1,64 +0,0 @@ -// 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.controller.api.zone.v1; - -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.net.URI; - -/** - * @author smorgrav - */ -public class ZoneReference { - - public static class Environment { - @JsonProperty("name") - private String name; - - @JsonProperty("url") - private URI url; - - public String getName() { - return name; - } - - public Environment setName(String name) { - this.name = name; - return this; - } - - public URI getUrl() { - return url; - } - - public Environment setUrl(URI url) { - this.url = url; - return this; - } - } - - public static class Region { - @JsonProperty("name") - private String name; - - @JsonProperty("url") - private URI url; - - public String getName() { - return name; - } - - public Region setName(String name) { - this.name = name; - return this; - } - - public URI getUrl() { - return url; - } - - public Region setUrl(URI url) { - this.url = url; - return this; - } - } -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v1/package-info.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v1/package-info.java deleted file mode 100644 index e3275ff35fa..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v1/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage -package com.yahoo.vespa.hosted.controller.api.zone.v1; - -import com.yahoo.osgi.annotation.ExportPackage; diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/ZoneApiV2.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/ZoneApiV2.java deleted file mode 100644 index a6d7614e862..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/ZoneApiV2.java +++ /dev/null @@ -1,302 +0,0 @@ -// 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.controller.api.zone.v2; - -import com.fasterxml.jackson.databind.JsonNode; -import com.yahoo.vespa.hosted.controller.api.identifiers.ApplicationId; -import com.yahoo.vespa.hosted.controller.api.identifiers.EnvironmentId; -import com.yahoo.vespa.hosted.controller.api.identifiers.InstanceId; -import com.yahoo.vespa.hosted.controller.api.identifiers.RegionId; -import com.yahoo.vespa.hosted.controller.api.identifiers.TenantId; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.MaintenanceJobList; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeList; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeState; -import com.yahoo.vespa.hosted.controller.api.integration.routing.RotationStatus; -import com.yahoo.vespa.hosted.controller.api.integration.routing.status.StatusReply; -import com.yahoo.vespa.hosted.controller.api.integration.routing.status.ZoneStatusReply; - -import javax.ws.rs.Consumes; -import javax.ws.rs.DELETE; -import javax.ws.rs.GET; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -import javax.ws.rs.PUT; -import javax.ws.rs.Path; -import javax.ws.rs.PathParam; -import javax.ws.rs.Produces; -import javax.ws.rs.QueryParam; -import javax.ws.rs.core.MediaType; -import javax.ws.rs.core.Response; -import java.util.Collection; -import java.util.List; -import java.util.Set; - -/** - * Aka the controller proxy service. - * - * Proxies calls to correct config server with the additional feature of - * retry and fail detection (ping). - */ -@Path(ZoneApiV2.API_VERSION) -@Produces(MediaType.APPLICATION_JSON) -@Consumes(MediaType.APPLICATION_JSON) -public interface ZoneApiV2 { - - String API_VERSION = "v2"; - - @GET - @Path("/") - ZoneReferences listZones(); - - @GET - @Path("/{environment}/{region}/{proxy_request: .+}") - Response proxyGet( - @PathParam("environment") String env, - @PathParam("region") String region, - @PathParam("proxy_request") String proxyRequest); - - @POST - @Path("/{environment}/{region}/{proxy_request: .+}") - Response proxyPost( - @PathParam("environment") String env, - @PathParam("region") String region, - @PathParam("proxy_request") String proxyRequest); - - @PUT - @Path("/{environment}/{region}/{proxy_request: .+}") - Response proxyPut( - @PathParam("environment") String env, - @PathParam("region") String region, - @PathParam("proxy_request") String proxyRequest); - - @DELETE - @Path("/{environment}/{region}/{proxy_request: .+}") - Response proxyDelete( - @PathParam("environment") String env, - @PathParam("region") String region, - @PathParam("proxy_request") String proxyRequest); - - // Explicit mappings of some proxy requests (to enable creation of proxy clients with javax.ws.rs) - - @GET - @Path("/{environmentId}/{regionId}/application/v2/tenant/{tenantId}/application/{applicationId}/environment/{environmentId}/region/{regionId}/instance/{instanceId}/serviceconverge") - Response waitForConfigConvergeV2(@PathParam("tenantId") TenantId tenantId, - @PathParam("applicationId") ApplicationId applicationId, - @PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("instanceId") InstanceId instanceId, - @QueryParam("timeout") long timeoutInSeconds); - @GET - @Path("/{environmentId}/{regionId}/application/v2/tenant/{tenantId}/application/{applicationId}/environment/{environmentId}/region/{regionId}/instance/{instanceId}/serviceconverge/{host}") - Response waitForConfigConvergeV2(@PathParam("tenantId") TenantId tenantId, - @PathParam("applicationId") ApplicationId applicationId, - @PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("instanceId") InstanceId instanceId, - @PathParam("host") String host, - @QueryParam("timeout") long timeoutInSeconds); - - @GET - @Path("/{environmentId}/{regionId}/config/v2/tenant/{tenantId}/application/{applicationId}/prelude.fastsearch.documentdb-info/{clusterid}/search/cluster.{clusterid}") - JsonNode getConfigWithDocumentTypes(@PathParam("tenantId") TenantId tenantId, - @PathParam("applicationId") ApplicationId applicationId, - @PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("clusterid") String clusterid, - @QueryParam("timeout") long timeoutInSeconds); - - @GET - @Path("/{environmentId}/{regionId}/config/v2/tenant/{tenantId}/application/{applicationId}/environment/{environmentId}/region/{regionId}/instance/{instanceId}/cloud.config.cluster-list") - JsonNode getVespaConfigClusterList(@PathParam("tenantId") TenantId tenantId, - @PathParam("applicationId") ApplicationId applicationId, - @PathParam("instanceId") InstanceId instanceId, - @PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @QueryParam("timeout") long timeoutInSeconds); - - - @POST - @Path("/{environmentId}/{regionId}/nodes/v2/node") - String addNodes(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - Collection<NodeRepositoryNode> node); - - @DELETE - @Path("/{environmentId}/{regionId}/nodes/v2/node/{hostname}") - String deleteNode(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("hostname") String hostname); - - @GET - @Path("/{environmentId}/{regionId}/nodes/v2/node/{hostname}") - NodeRepositoryNode getNode(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("hostname") String hostname); - - @POST - @Path("/{environmentId}/{regionId}/nodes/v2/node/{hostname}") - String patchNode(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("hostname") String hostname, - NodeRepositoryNode patchValues, - @HeaderParam("X-HTTP-Method-Override") String patchOverride); - - @GET - @Path("/{environmentId}/{regionId}/nodes/v2/node/") - NodeList listNodes(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @QueryParam("recursive") boolean recursive); - - @GET - @Path("/{environmentId}/{regionId}/nodes/v2/node/") - NodeList listNodes(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @QueryParam("application") String applicationString, - @QueryParam("recursive") boolean recursive); - - @PUT - @Path("/{environmentId}/{regionId}/nodes/v2/state/{state}/{hostname}") - String setNodeState(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("state") NodeState state, - @PathParam("hostname") String hostname); - - @POST - @Path("/{environmentId}/{regionId}/nodes/v2/command/reboot") - String rebootNode(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @QueryParam("hostname") String hostname); - - @POST - @Path("/{environmentId}/{regionId}/nodes/v2/command/restart") - String restartNode(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @QueryParam("hostname") String hostname); - - @GET - @Path("/{environmentId}/{regionId}/nodes/v2/maintenance/") - MaintenanceJobList listMaintenanceJobs(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId); - - @POST - @Path("/{environmentId}/{regionId}/nodes/v2/maintenance/inactive/{jobname}") - String disableMaintenanceJob(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("jobname") String jobname); - - @DELETE - @Path("/{environmentId}/{regionId}/nodes/v2/maintenance/inactive/{jobname}") - String enableMaintenanceJob(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("jobname") String jobname); - - - @GET - @Path("/{environmentId}/{regionId}/orchestrator/v1/suspensions/applications") - Set<String> getApplications(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId); - - @GET - @Path("/{environmentId}/{regionId}/orchestrator/v1/suspensions/applications/{application}") - void getApplication(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("application") String applicationIdString); - - @POST - @Path("/{environmentId}/{regionId}/orchestrator/v1/suspensions/applications") - void suspendApplication(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - String applicationIdString); - - @DELETE - @Path("/{environmentId}/{regionId}/orchestrator/v1/suspensions/applications/{application}") - void resumeApplication(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("application") String applicationIdString); - - /** - * Get names of all rotations with the status OUT - * - * @return List of rotation names. - */ - @GET - @Produces(MediaType.APPLICATION_JSON) - @Path("/{environmentId}/{regionId}/routing/v1/status") - List<String> listRotations(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId); - - /** - * Get the status of a rotation - * - * @param rotation The name of a rotation. - * @return The current status of the rotation, wrapped into a {@link StatusReply} object. The value is {@link - * RotationStatus#IN} for any rotation that has not been explicitly set to {@link RotationStatus#OUT} using {@link - * #setRotationStatus(EnvironmentId, RegionId, String, StatusReply)}. - */ - @GET - @Path("/{environmentId}/{regionId}/routing/v1/status/{rotation}") - @Produces(MediaType.APPLICATION_JSON) - StatusReply getRotationStatus(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("rotation") String rotation); - - /** - * Set or modify rotation status according to payload - * - * @param rotation The rotation (endpoint) to modify - * @param payload The name/status/agent/reason to set - * @return The updated status of the rotation wrapped into a {@link StatusReply} object. - */ - @PUT - @Path("/{environmentId}/{regionId}/routing/v1/status/{rotation}") - @Produces(MediaType.APPLICATION_JSON) - @Consumes(MediaType.APPLICATION_JSON) - StatusReply setRotationStatus(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("rotation") String rotation, - StatusReply payload); - - /** - * Set the status of a rotation to IN - * - * @param rotation The name of a rotation. - * @return The updated status of the rotation wrapped into a {@link StatusReply} object. - */ - @DELETE - @Path("/{environmentId}/{regionId}/routing/v1/status/{rotation}") - @Produces(MediaType.APPLICATION_JSON) - @Deprecated - StatusReply unsetRotation(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId, - @PathParam("rotation") String rotation); - - /** - * Set the status of the zone to OUT - * @return The updated status of the zone wrapped into a {@link ZoneStatusReply} object. - */ - @PUT - @Path("/{environmentId}/{regionId}/routing/v1/status/zone") - @Produces(MediaType.APPLICATION_JSON) - ZoneStatusReply setZoneRotationInactive(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId); - - /** - * Clears the status of the zone. The routing check will fall back to check individual rotations. - * @return The updated status of the zone wrapped into a {@link ZoneStatusReply} object. - */ - @DELETE - @Path("/{environmentId}/{regionId}/routing/v1/status/zone") - @Produces(MediaType.APPLICATION_JSON) - ZoneStatusReply unsetZoneRotationInactive(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId); - - /** - * Get the status of the zone - * @return The status of the zone wrapped into a {@link ZoneStatusReply} object. - */ - @GET - @Path("/{environmentId}/{regionId}/routing/v1/status/zone") - @Produces(MediaType.APPLICATION_JSON) - ZoneStatusReply getZoneRotationStatus(@PathParam("environmentId") EnvironmentId environmentId, - @PathParam("regionId") RegionId regionId); -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/ZoneReference.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/ZoneReference.java deleted file mode 100644 index 95dc1c2ee7c..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/ZoneReference.java +++ /dev/null @@ -1,27 +0,0 @@ -// 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.controller.api.zone.v2; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.yahoo.vespa.hosted.controller.api.configserver.Environment; -import com.yahoo.vespa.hosted.controller.api.configserver.Region; - -/** - * @author mpolden - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ZoneReference { - - @JsonProperty("environment") - public final Environment environment; - @JsonProperty("region") - public final Region region; - - @JsonCreator - public ZoneReference(@JsonProperty("environment") Environment environment, @JsonProperty("region") Region region) { - this.environment = environment; - this.region = region; - } - -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/ZoneReferences.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/ZoneReferences.java deleted file mode 100644 index 3a219afa0a6..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/ZoneReferences.java +++ /dev/null @@ -1,31 +0,0 @@ -// 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.controller.api.zone.v2; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; - -import java.util.Collections; -import java.util.List; - -/** - * Wire format for listing the controller URIs for all the available zones - * - * @author smorgrav - */ -@JsonIgnoreProperties(ignoreUnknown = true) -public class ZoneReferences { - - @JsonProperty("uris") - public final List<String> uris; - - @JsonProperty("zones") - public final List<ZoneReference> zones; - - @JsonCreator - public ZoneReferences(@JsonProperty("uris") List<String> uris, @JsonProperty("zones") List<ZoneReference> zones) { - this.uris = Collections.unmodifiableList(uris); - this.zones = Collections.unmodifiableList(zones); - } - -} diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/package-info.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/package-info.java deleted file mode 100644 index 5d4b1310981..00000000000 --- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/zone/v2/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -@ExportPackage -package com.yahoo.vespa.hosted.controller.api.zone.v2; - -import com.yahoo.osgi.annotation.ExportPackage; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainer.java index c99e4f5951b..d9aaef6ef3b 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ClusterInfoMaintainer.java @@ -5,8 +5,8 @@ import com.yahoo.config.provision.ClusterSpec; import com.yahoo.vespa.hosted.controller.Application; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeList; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; import com.yahoo.vespa.hosted.controller.application.ClusterInfo; import com.yahoo.vespa.hosted.controller.application.Deployment; @@ -31,13 +31,13 @@ public class ClusterInfoMaintainer extends Maintainer { private static final Logger log = Logger.getLogger(ClusterInfoMaintainer.class.getName()); private final Controller controller; - private final NodeRepositoryClientInterface nodeRepositoryClient; + private final NodeRepository nodeRepository; ClusterInfoMaintainer(Controller controller, Duration duration, JobControl jobControl, - NodeRepositoryClientInterface nodeRepositoryClient) { + NodeRepository nodeRepository) { super(controller, duration, jobControl); this.controller = controller; - this.nodeRepositoryClient = nodeRepositoryClient; + this.nodeRepository = nodeRepository; } private static String clusterId(NodeRepositoryNode node) { @@ -81,10 +81,7 @@ public class ClusterInfoMaintainer extends Maintainer { for (Deployment deployment : application.deployments().values()) { DeploymentId deploymentId = new DeploymentId(application.id(), deployment.zone()); try { - NodeList nodes = nodeRepositoryClient.listNodes(deploymentId.zoneId(), - deploymentId.applicationId().tenant().value(), - deploymentId.applicationId().application().value(), - deploymentId.applicationId().instance().value()); + NodeList nodes = nodeRepository.listNodes(deploymentId.zoneId(), deploymentId.applicationId()); Map<ClusterSpec.Id, ClusterInfo> clusterInfo = getClusterInfo(nodes); controller().applications().lockIfPresent(application.id(), lockedApplication -> controller.applications().store(lockedApplication.withClusterInfo(deployment.zone(), clusterInfo))); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java index f34c24c497a..116ea532a11 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ControllerMaintenance.java @@ -5,8 +5,8 @@ import com.yahoo.component.AbstractComponent; import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.jdisc.Metric; import com.yahoo.vespa.hosted.controller.Controller; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository; import com.yahoo.vespa.hosted.controller.api.integration.dns.NameService; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; import com.yahoo.vespa.hosted.controller.api.integration.organization.Billing; import com.yahoo.vespa.hosted.controller.api.integration.organization.ContactRetriever; import com.yahoo.vespa.hosted.controller.api.integration.organization.DeploymentIssues; @@ -60,7 +60,7 @@ public class ControllerMaintenance extends AbstractComponent { public ControllerMaintenance(MaintainerConfig maintainerConfig, ApiAuthorityConfig apiAuthorityConfig, Controller controller, CuratorDb curator, JobControl jobControl, Metric metric, DeploymentIssues deploymentIssues, OwnershipIssues ownershipIssues, - NameService nameService, NodeRepositoryClientInterface nodeRepositoryClient, + NameService nameService, NodeRepository nodeRepository, ContactRetriever contactRetriever, CostReportConsumer reportConsumer, ResourceSnapshotConsumer resourceSnapshotConsumer, @@ -75,7 +75,7 @@ public class ControllerMaintenance extends AbstractComponent { versionStatusUpdater = new VersionStatusUpdater(controller, Duration.ofMinutes(1), jobControl); upgrader = new Upgrader(controller, maintenanceInterval, jobControl, curator); readyJobsTrigger = new ReadyJobsTrigger(controller, Duration.ofMinutes(1), jobControl); - clusterInfoMaintainer = new ClusterInfoMaintainer(controller, Duration.ofHours(2), jobControl, nodeRepositoryClient); + clusterInfoMaintainer = new ClusterInfoMaintainer(controller, Duration.ofHours(2), jobControl, nodeRepository); clusterUtilizationMaintainer = new ClusterUtilizationMaintainer(controller, Duration.ofHours(2), jobControl); deploymentMetricsMaintainer = new DeploymentMetricsMaintainer(controller, Duration.ofMinutes(5), jobControl); applicationOwnershipConfirmer = new ApplicationOwnershipConfirmer(controller, Duration.ofHours(12), jobControl, ownershipIssues); @@ -84,8 +84,8 @@ public class ControllerMaintenance extends AbstractComponent { osUpgraders = osUpgraders(controller, jobControl); osVersionStatusUpdater = new OsVersionStatusUpdater(controller, maintenanceInterval, jobControl); contactInformationMaintainer = new ContactInformationMaintainer(controller, Duration.ofHours(12), jobControl, contactRetriever); - costReportMaintainer = new CostReportMaintainer(controller, Duration.ofHours(2), reportConsumer, jobControl, nodeRepositoryClient, Clock.systemUTC(), selfHostedCostConfig); - resourceMeterMaintainer = new ResourceMeterMaintainer(controller, Duration.ofMinutes(60), jobControl, nodeRepositoryClient, Clock.systemUTC(), metric, resourceSnapshotConsumer); + costReportMaintainer = new CostReportMaintainer(controller, Duration.ofHours(2), reportConsumer, jobControl, nodeRepository, Clock.systemUTC(), selfHostedCostConfig); + resourceMeterMaintainer = new ResourceMeterMaintainer(controller, Duration.ofMinutes(60), jobControl, nodeRepository, Clock.systemUTC(), metric, resourceSnapshotConsumer); nameServiceDispatcher = new NameServiceDispatcher(controller, Duration.ofSeconds(10), jobControl, nameService); billingMaintainer = new BillingMaintainer(controller, Duration.ofDays(3), jobControl, billing); } diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java index 30bca180c0f..f3dac2be22e 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/CostReportMaintainer.java @@ -5,7 +5,7 @@ import com.google.inject.Inject; import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.SystemName; import com.yahoo.vespa.hosted.controller.Controller; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository; import com.yahoo.vespa.hosted.controller.restapi.cost.CostCalculator; import com.yahoo.vespa.hosted.controller.restapi.cost.CostReportConsumer; import com.yahoo.vespa.hosted.controller.restapi.cost.config.SelfHostedCostConfig; @@ -27,7 +27,7 @@ public class CostReportMaintainer extends Maintainer { private static final Logger log = Logger.getLogger(CostReportMaintainer.class.getName()); private final CostReportConsumer consumer; - private final NodeRepositoryClientInterface nodeRepository; + private final NodeRepository nodeRepository; private final Clock clock; private final SelfHostedCostConfig selfHostedCostConfig; @@ -36,7 +36,7 @@ public class CostReportMaintainer extends Maintainer { public CostReportMaintainer(Controller controller, Duration interval, CostReportConsumer consumer, JobControl jobControl, - NodeRepositoryClientInterface nodeRepository, + NodeRepository nodeRepository, Clock clock, SelfHostedCostConfig selfHostedCostConfig) { super(controller, interval, jobControl, "CostReportMaintainer", EnumSet.of(SystemName.main)); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java index c4f0597572b..5e2ba48da3f 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java @@ -1,29 +1,28 @@ // Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.maintenance; +import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.SystemName; -import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.jdisc.Metric; import com.yahoo.vespa.hosted.controller.Controller; -import com.yahoo.config.provision.ApplicationId; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeOwner; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeState; +import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshotConsumer; -import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation; import java.time.Clock; import java.time.Duration; import java.time.Instant; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Map; import java.util.function.Predicate; import java.util.stream.Collectors; -import static com.yahoo.yolean.Exceptions.uncheck; - /** * Creates a ResourceSnapshot per application, which is then passed on to a ResourceSnapshotConsumer * TODO: Write JSON blob of node repo somewhere @@ -34,7 +33,7 @@ public class ResourceMeterMaintainer extends Maintainer { private final Clock clock; private final Metric metric; - private final NodeRepositoryClientInterface nodeRepository; + private final NodeRepository nodeRepository; private final ResourceSnapshotConsumer resourceSnapshotConsumer; private static final String metering_last_reported = "metering_last_reported"; @@ -44,7 +43,7 @@ public class ResourceMeterMaintainer extends Maintainer { public ResourceMeterMaintainer(Controller controller, Duration interval, JobControl jobControl, - NodeRepositoryClientInterface nodeRepository, + NodeRepository nodeRepository, Clock clock, Metric metric, ResourceSnapshotConsumer resourceSnapshotConsumer) { @@ -83,24 +82,17 @@ public class ResourceMeterMaintainer extends Maintainer { return controller().zoneRegistry().zones() .ofCloud(CloudName.from("aws")) .reachable().zones().stream() - .flatMap(zone -> uncheck(() -> nodeRepository.listNodes(zone.getId(), true).nodes().stream())) + .flatMap(zone -> nodeRepository.listNodes(zone.getId()).nodes().stream()) .filter(node -> node.getOwner() != null && !node.getOwner().getTenant().equals("hosted-vespa")) .filter(node -> node.getState() == NodeState.active) .collect(Collectors.toList()); } private Map<ApplicationId, ResourceAllocation> getResourceAllocationByApplication(List<NodeRepositoryNode> nodes) { - Map<ApplicationId, List<NodeRepositoryNode>> applicationNodes = new HashMap<>(); - - nodes.stream().forEach(node -> applicationNodes.computeIfAbsent(applicationIdFromNodeOwner(node.getOwner()), n -> new ArrayList<>()).add(node)); - - return applicationNodes.entrySet().stream() - .collect( - Collectors.toMap( - entry -> entry.getKey(), - entry -> ResourceAllocation.from(entry.getValue()) - ) - ); + return nodes.stream() + .collect(Collectors.groupingBy( + node -> applicationIdFromNodeOwner(node.getOwner()), + Collectors.collectingAndThen(Collectors.toList(), ResourceAllocation::from))); } private ApplicationId applicationIdFromNodeOwner(NodeOwner owner) { diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java index 9c320df2f6c..868272e5051 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiHandler.java @@ -176,6 +176,7 @@ public class ApplicationApiHandler extends LoggingRequestHandler { if (path.matches("/application/v4/tenant/{tenant}/application/{application}")) return application(path.get("tenant"), path.get("application"), "default", request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/deploying")) return deploying(path.get("tenant"), path.get("application"), "default", request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/deploying/pin")) return deploying(path.get("tenant"), path.get("application"), "default", request); + if (path.matches("/application/v4/tenant/{tenant}/application/{application}/metering")) return metering(path.get("tenant"), path.get("application"), request); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/nodes")) return nodes(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region")); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/environment/{environment}/region/{region}/instance/{instance}/logs")) return logs(path.get("tenant"), path.get("application"), path.get("instance"), path.get("environment"), path.get("region"), request.propertyMap()); if (path.matches("/application/v4/tenant/{tenant}/application/{application}/instance")) return applications(path.get("tenant"), Optional.of(path.get("application")), request); @@ -772,6 +773,45 @@ public class ApplicationApiHandler extends LoggingRequestHandler { return new SlimeJsonResponse(slime); } + private HttpResponse metering(String tenant, String application, HttpRequest request) { + Slime slime = new Slime(); + Cursor root = slime.setObject(); + + Cursor currentRate = root.setObject("currentrate"); + currentRate.setDouble("cpu", 0); + currentRate.setDouble("mem", 0); + currentRate.setDouble("disk", 0); + + Cursor thismonth = root.setObject("thismonth"); + thismonth.setDouble("cpu", 0); + thismonth.setDouble("mem", 0); + thismonth.setDouble("disk", 0); + + Cursor lastmonth = root.setObject("lastmonth"); + lastmonth.setDouble("cpu", 0); + lastmonth.setDouble("mem", 0); + lastmonth.setDouble("disk", 0); + + Cursor details = root.setObject("details"); + + Cursor detailsCpu = details.setObject("cpu"); + Cursor detailsCpuDummyApp = detailsCpu.setObject("dummy"); + Cursor detailsCpuDummyData = detailsCpuDummyApp.setArray("data"); + + // The data array should be filled with objects like: { unixms: <number>, valur: <number } + + Cursor detailsMem = details.setObject("mem"); + Cursor detailsMemDummyApp = detailsMem.setObject("dummy"); + Cursor detailsMemDummyData = detailsMemDummyApp.setArray("data"); + + Cursor detailsDisk = details.setObject("disk"); + Cursor detailsDiskDummyApp = detailsDisk.setObject("dummy"); + Cursor detailsDiskDummyData = detailsDiskDummyApp.setArray("data"); + + + return new SlimeJsonResponse(slime); + } + private HttpResponse deploying(String tenant, String application, String instance, HttpRequest request) { Application app = controller.applications().require(ApplicationId.from(tenant, application, instance)); Slime slime = new Slime(); diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java index bae790a49ad..796f786d823 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostApiHandler.java @@ -6,7 +6,7 @@ import com.yahoo.container.jdisc.HttpResponse; import com.yahoo.container.jdisc.LoggingRequestHandler; import com.yahoo.restapi.Path; import com.yahoo.vespa.hosted.controller.Controller; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository; import com.yahoo.vespa.hosted.controller.restapi.ErrorResponse; import com.yahoo.vespa.hosted.controller.restapi.StringResponse; import com.yahoo.vespa.hosted.controller.restapi.cost.config.SelfHostedCostConfig; @@ -19,10 +19,10 @@ import static com.yahoo.jdisc.http.HttpRequest.Method.GET; public class CostApiHandler extends LoggingRequestHandler { private final Controller controller; - private final NodeRepositoryClientInterface nodeRepository; + private final NodeRepository nodeRepository; private final SelfHostedCostConfig selfHostedCostConfig; - public CostApiHandler(Context ctx, Controller controller, NodeRepositoryClientInterface nodeRepository, SelfHostedCostConfig selfHostedCostConfig) { + public CostApiHandler(Context ctx, Controller controller, NodeRepository nodeRepository, SelfHostedCostConfig selfHostedCostConfig) { super(ctx); this.controller = controller; this.nodeRepository = nodeRepository; diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java index c44a80f7a20..919cade1b05 100644 --- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java +++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/cost/CostCalculator.java @@ -2,11 +2,10 @@ package com.yahoo.vespa.hosted.controller.restapi.cost; import com.yahoo.config.provision.CloudName; import com.yahoo.config.provision.Environment; -import com.yahoo.config.provision.zone.ZoneApi; import com.yahoo.vespa.hosted.controller.Controller; import com.yahoo.vespa.hosted.controller.api.identifiers.Property; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeOwner; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceAllocation; import com.yahoo.vespa.hosted.controller.restapi.cost.config.SelfHostedCostConfig; @@ -26,7 +25,7 @@ public class CostCalculator { private static final double SELF_HOSTED_DISCOUNT = .5; - public static String resourceShareByPropertyToCsv(NodeRepositoryClientInterface nodeRepository, + public static String resourceShareByPropertyToCsv(NodeRepository nodeRepository, Controller controller, Clock clock, SelfHostedCostConfig selfHostedCostConfig, @@ -36,7 +35,7 @@ public class CostCalculator { List<NodeRepositoryNode> nodes = controller.zoneRegistry().zones() .reachable().in(Environment.prod).ofCloud(cloudName).zones().stream() - .flatMap(zone -> uncheck(() -> nodeRepository.listNodes(zone.getId(), true).nodes().stream())) + .flatMap(zone -> uncheck(() -> nodeRepository.listNodes(zone.getId()).nodes().stream())) .filter(node -> node.getOwner() != null && !node.getOwner().getTenant().equals("hosted-vespa")) .collect(Collectors.toList()); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java index c26f1879f6a..0cf54db4d3a 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/ControllerTest.java @@ -43,6 +43,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Supplier; @@ -358,6 +359,7 @@ public class ControllerTest { .environment(Environment.prod) .endpoint("foobar", "qrs", "us-west-1", "us-central-1") .endpoint("default", "qrs", "us-west-1", "us-central-1") + .endpoint("all", "qrs") .region("us-west-1") .region("us-central-1") .build(); @@ -370,24 +372,31 @@ public class ControllerTest { Set.of( "rotation-id-01", "rotation-id-02", + "rotation-id-03", "app1--tenant1.global.vespa.oath.cloud", - "foobar--app1--tenant1.global.vespa.oath.cloud" + "foobar--app1--tenant1.global.vespa.oath.cloud", + "all--app1--tenant1.global.vespa.oath.cloud" ), tester.configServer().rotationNames().get(new DeploymentId(application.id(), deployment.zone()))); } tester.flushDnsRequests(); - assertEquals(2, tester.controllerTester().nameService().records().size()); + assertEquals(3, tester.controllerTester().nameService().records().size()); var record1 = tester.controllerTester().findCname("app1--tenant1.global.vespa.oath.cloud"); assertTrue(record1.isPresent()); assertEquals("app1--tenant1.global.vespa.oath.cloud", record1.get().name().asString()); - assertEquals("rotation-fqdn-02.", record1.get().data().asString()); + assertEquals("rotation-fqdn-03.", record1.get().data().asString()); var record2 = tester.controllerTester().findCname("foobar--app1--tenant1.global.vespa.oath.cloud"); assertTrue(record2.isPresent()); assertEquals("foobar--app1--tenant1.global.vespa.oath.cloud", record2.get().name().asString()); assertEquals("rotation-fqdn-01.", record2.get().data().asString()); + + var record3 = tester.controllerTester().findCname("all--app1--tenant1.global.vespa.oath.cloud"); + assertTrue(record3.isPresent()); + assertEquals("all--app1--tenant1.global.vespa.oath.cloud", record3.get().name().asString()); + assertEquals("rotation-fqdn-02.", record3.get().data().asString()); } @Test diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java index 1c7abd2489d..4f2d85adfbe 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryClientMock.java @@ -1,14 +1,16 @@ // Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.vespa.hosted.controller.integration; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.MaintenanceJobList; +import com.yahoo.component.Version; +import com.yahoo.config.provision.ApplicationId; +import com.yahoo.config.provision.NodeType; +import com.yahoo.config.provision.zone.ZoneId; +import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeList; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeMembership; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeOwner; -import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryClientInterface; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeState; -import com.yahoo.config.provision.zone.ZoneId; import java.util.Collection; import java.util.List; @@ -16,7 +18,7 @@ import java.util.List; /** * @author bjorncs */ -public class NodeRepositoryClientMock implements NodeRepositoryClientInterface { +public class NodeRepositoryClientMock implements NodeRepository { @Override public void addNodes(ZoneId zone, Collection<NodeRepositoryNode> nodes) { @@ -34,14 +36,14 @@ public class NodeRepositoryClientMock implements NodeRepositoryClientInterface { } @Override - public NodeList listNodes(ZoneId zone, boolean recursive) { + public NodeList listNodes(ZoneId zone) { NodeRepositoryNode nodeA = createNodeA(); NodeRepositoryNode nodeB = createNodeB(); return new NodeList(List.of(nodeA, nodeB)); } @Override - public NodeList listNodes(ZoneId zone, String tenant, String applicationId, String instance) { + public NodeList listNodes(ZoneId zone, ApplicationId application) { NodeRepositoryNode nodeA = createNodeA(); NodeRepositoryNode nodeB = createNodeB(); return new NodeList(List.of(nodeA, nodeB)); @@ -90,57 +92,27 @@ public class NodeRepositoryClientMock implements NodeRepositoryClientInterface { } @Override - public String resetFailureInformation(ZoneId zone, String nodename) { - throw new UnsupportedOperationException(); - } - - @Override - public String restart(ZoneId zone, String nodename) { - throw new UnsupportedOperationException(); - } - - @Override - public String reboot(ZoneId zone, String nodename) { - throw new UnsupportedOperationException(); - } - - @Override - public String cancelReboot(ZoneId zone, String nodename) { - throw new UnsupportedOperationException(); - } - - @Override - public String wantTo(ZoneId zone, String nodename, WantTo... actions) { - throw new UnsupportedOperationException(); - } - - @Override - public String cancelRestart(ZoneId zone, String nodename) { - throw new UnsupportedOperationException(); - } - - @Override - public String setHardwareFailureDescription(ZoneId zone, String nodename, String hardwareFailureDescription) { + public void setState(ZoneId zone, NodeState nodeState, String nodename) { throw new UnsupportedOperationException(); } @Override - public void setState(ZoneId zone, NodeState nodeState, String nodename) { + public void upgrade(ZoneId zone, NodeType type, Version version) { throw new UnsupportedOperationException(); } @Override - public String enableMaintenanceJob(ZoneId zone, String jobName) { + public void upgradeOs(ZoneId zone, NodeType type, Version version) { throw new UnsupportedOperationException(); } @Override - public String disableMaintenanceJob(ZoneId zone, String jobName) { + public void requestFirmwareCheck(ZoneId zone) { throw new UnsupportedOperationException(); } @Override - public MaintenanceJobList listMaintenanceJobs(ZoneId zone) { + public void cancelFirmwareCheck(ZoneId zone) { throw new UnsupportedOperationException(); } diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java index 49bc910ac33..45c2df5cb77 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java @@ -5,11 +5,15 @@ import com.yahoo.component.Version; import com.yahoo.config.provision.ApplicationId; import com.yahoo.config.provision.HostName; import com.yahoo.config.provision.NodeType; +import com.yahoo.config.provision.zone.ZoneId; import com.yahoo.vespa.hosted.controller.api.identifiers.DeploymentId; import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node; import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository; -import com.yahoo.config.provision.zone.ZoneId; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeList; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeRepositoryNode; +import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeState; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -56,6 +60,36 @@ public class NodeRepositoryMock implements NodeRepository { } @Override + public void addNodes(ZoneId zone, Collection<NodeRepositoryNode> nodes) { + throw new UnsupportedOperationException(); + } + + @Override + public void deleteNode(ZoneId zone, String hostname) { + throw new UnsupportedOperationException(); + } + + @Override + public void setState(ZoneId zone, NodeState nodeState, String nodename) { + throw new UnsupportedOperationException(); + } + + @Override + public NodeRepositoryNode getNode(ZoneId zone, String hostname) { + throw new UnsupportedOperationException(); + } + + @Override + public NodeList listNodes(ZoneId zone) { + throw new UnsupportedOperationException(); + } + + @Override + public NodeList listNodes(ZoneId zone, ApplicationId application) { + throw new UnsupportedOperationException(); + } + + @Override public List<Node> list(ZoneId zone, ApplicationId application) { return nodeRepository.getOrDefault(zone, Collections.emptyMap()).values().stream() .filter(node -> node.owner().map(application::equals).orElse(false)) diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java index 29931a1f626..577b8491bd2 100644 --- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/ApplicationApiTest.java @@ -856,7 +856,15 @@ public class ApplicationApiTest extends ControllerContainerTest { .userIdentity(USER_ID), new File("application-without-change-multiple-deployments.json")); } - + + @Test + public void testMeteringResponses() { + tester.assertResponse(request("/application/v4/tenant/doesnotexist/application/doesnotexist/metering", GET) + .userIdentity(USER_ID) + .oktaAccessToken(OKTA_AT), + new File("application1-metering.json")); + } + @Test public void testErrorResponses() throws Exception { tester.computeVersionStatus(); diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application1-metering.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application1-metering.json new file mode 100644 index 00000000000..63e1c1ebbd1 --- /dev/null +++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/application/responses/application1-metering.json @@ -0,0 +1,34 @@ +{ + "currentrate": { + "cpu": 0.0, + "mem": 0.0, + "disk": 0.0 + }, + "thismonth": { + "cpu": 0.0, + "mem": 0.0, + "disk": 0.0 + }, + "lastmonth": { + "cpu": 0.0, + "mem": 0.0, + "disk": 0.0 + }, + "details": { + "cpu": { + "dummy": { + "data": [] + } + }, + "mem": { + "dummy": { + "data": [] + } + }, + "disk": { + "dummy": { + "data": [] + } + } + } +}
\ No newline at end of file diff --git a/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h b/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h index 60f85c38659..c09202e50d0 100644 --- a/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h +++ b/eval/src/vespa/eval/tensor/dense/dense_tensor_view.h @@ -46,6 +46,7 @@ public: void accept(TensorVisitor &visitor) const override; protected: void initCellsRef(TypedCells cells_in) { + assert(_typeRef.cell_type() == cells_in.type); _cellsRef = cells_in; } private: diff --git a/metrics-proxy/pom.xml b/metrics-proxy/pom.xml index 3cfe1d2f568..26caa3f66af 100644 --- a/metrics-proxy/pom.xml +++ b/metrics-proxy/pom.xml @@ -79,6 +79,12 @@ </dependency> <dependency> <groupId>com.yahoo.vespa</groupId> + <artifactId>jdisc_http_service</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + <dependency> + <groupId>com.yahoo.vespa</groupId> <artifactId>jrt</artifactId> <version>${project.version}</version> <scope>provided</scope> @@ -110,12 +116,6 @@ <version>${project.version}</version> </dependency> <dependency> - <groupId>com.yahoo.vespa</groupId> - <artifactId>jdisc_http_service</artifactId> - <version>${project.version}</version> - <scope>test</scope> - </dependency> - <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ErrorResponse.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ErrorResponse.java new file mode 100644 index 00000000000..679bae84f8e --- /dev/null +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ErrorResponse.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + */ + +package ai.vespa.metricsproxy.http; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; + +import java.util.Map; +import java.util.logging.Logger; + +import static java.util.logging.Level.WARNING; + +/** + * @author gjoranv + */ +class ErrorResponse extends JsonResponse { + private static Logger log = Logger.getLogger(ErrorResponse.class.getName()); + + ErrorResponse(int code, String message) { + super(code, asErrorJson(message)); + } + + static String asErrorJson(String message) { + try { + return new ObjectMapper().writeValueAsString(Map.of("error", message)); + } catch (JsonProcessingException e) { + log.log(WARNING, "Could not encode error message to json:", e); + return "Could not encode error message to json, check the log for details."; + } + } +} diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/GenericMetricsHandler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/GenericMetricsHandler.java deleted file mode 100644 index f61a96917a9..00000000000 --- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/GenericMetricsHandler.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. - */ - -package ai.vespa.metricsproxy.http; - -import ai.vespa.metricsproxy.core.MetricsConsumers; -import ai.vespa.metricsproxy.core.MetricsManager; -import ai.vespa.metricsproxy.metric.model.ConsumerId; -import ai.vespa.metricsproxy.metric.model.MetricsPacket; -import ai.vespa.metricsproxy.metric.model.json.JsonRenderingException; -import ai.vespa.metricsproxy.service.VespaServices; -import com.google.inject.Inject; -import com.yahoo.container.jdisc.HttpRequest; -import com.yahoo.container.jdisc.HttpResponse; -import com.yahoo.container.jdisc.ThreadedHttpRequestHandler; - -import java.io.IOException; -import java.io.OutputStream; -import java.nio.charset.Charset; -import java.time.Instant; -import java.util.List; -import java.util.concurrent.Executor; -import java.util.logging.Logger; -import java.util.stream.Collectors; - -import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId; -import static ai.vespa.metricsproxy.metric.model.json.GenericJsonUtil.toGenericJsonModel; - -/** - * Http handler that exposes the generic metrics format. - * - * @author gjoranv - */ -public class GenericMetricsHandler extends ThreadedHttpRequestHandler { - private static final Logger log = Logger.getLogger(GenericMetricsHandler.class.getName()); - - public static final ConsumerId DEFAULT_PUBLIC_CONSUMER_ID = toConsumerId("default"); - - private final MetricsConsumers metricsConsumers; - private final MetricsManager metricsManager; - private final VespaServices vespaServices; - - @Inject - public GenericMetricsHandler(Executor executor, - MetricsManager metricsManager, - VespaServices vespaServices, - MetricsConsumers metricsConsumers) { - super(executor); - this.metricsConsumers = metricsConsumers; - this.metricsManager = metricsManager; - this.vespaServices = vespaServices; - } - - @Override - public HttpResponse handle(HttpRequest request) { - try { - ConsumerId consumer = getConsumerOrDefault(request.getProperty("consumer")); - - List<MetricsPacket> metrics = metricsManager.getMetrics(vespaServices.getVespaServices(), Instant.now()) - .stream() - .filter(metricsPacket -> metricsPacket.consumers().contains(consumer)) - .collect(Collectors.toList()); - return new Response(200, toGenericJsonModel(metrics).serialize()); - } catch (JsonRenderingException e) { - return new Response(500, e.getMessageAsJson()); - } - } - - private ConsumerId getConsumerOrDefault(String consumer) { - if (consumer == null) return DEFAULT_PUBLIC_CONSUMER_ID; - - ConsumerId consumerId = toConsumerId(consumer); - if (! metricsConsumers.getAllConsumers().contains(consumerId)) { - log.info("No consumer with id '" + consumer + "' - using the default consumer instead."); - return DEFAULT_PUBLIC_CONSUMER_ID; - } - return consumerId; - } - - private static class Response extends HttpResponse { - private final byte[] data; - - Response(int code, String data) { - super(code); - this.data = data.getBytes(Charset.forName(DEFAULT_CHARACTER_ENCODING)); - } - - @Override - public String getContentType() { - return "application/json"; - } - - @Override - public void render(OutputStream outputStream) throws IOException { - outputStream.write(data); - } - } - -} diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/JsonResponse.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/JsonResponse.java new file mode 100644 index 00000000000..758a043a823 --- /dev/null +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/JsonResponse.java @@ -0,0 +1,33 @@ +/* + * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + */ + +package ai.vespa.metricsproxy.http; + +import com.yahoo.container.jdisc.HttpResponse; + +import java.io.IOException; +import java.io.OutputStream; +import java.nio.charset.Charset; + +/** + * @author gjoranv + */ +class JsonResponse extends HttpResponse { + private final byte[] data; + + JsonResponse(int code, String data) { + super(code); + this.data = data.getBytes(Charset.forName(DEFAULT_CHARACTER_ENCODING)); + } + + @Override + public String getContentType() { + return "application/json"; + } + + @Override + public void render(OutputStream outputStream) throws IOException { + outputStream.write(data); + } +} diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/MetricsHandler.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/MetricsHandler.java new file mode 100644 index 00000000000..1d7206f177d --- /dev/null +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/MetricsHandler.java @@ -0,0 +1,99 @@ +/* + * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + */ + +package ai.vespa.metricsproxy.http; + +import ai.vespa.metricsproxy.core.MetricsConsumers; +import ai.vespa.metricsproxy.core.MetricsManager; +import ai.vespa.metricsproxy.metric.model.json.JsonRenderingException; +import ai.vespa.metricsproxy.service.VespaServices; +import com.google.inject.Inject; +import com.yahoo.container.jdisc.HttpRequest; +import com.yahoo.container.jdisc.HttpResponse; +import com.yahoo.container.jdisc.ThreadedHttpRequestHandler; +import com.yahoo.restapi.Path; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.net.URI; +import java.util.concurrent.Executor; + +import static com.yahoo.jdisc.Response.Status.INTERNAL_SERVER_ERROR; +import static com.yahoo.jdisc.Response.Status.METHOD_NOT_ALLOWED; +import static com.yahoo.jdisc.Response.Status.NOT_FOUND; +import static com.yahoo.jdisc.Response.Status.OK; +import static com.yahoo.jdisc.http.HttpRequest.Method.GET; + +/** + * Http handler for the metrics/v1 rest api. + * + * @author gjoranv + */ +public class MetricsHandler extends ThreadedHttpRequestHandler { + + static final String V1_PATH = "/metrics/v1"; + static final String VALUES_PATH = V1_PATH + "/values"; + + private final ValuesFetcher valuesFetcher; + + @Inject + public MetricsHandler(Executor executor, + MetricsManager metricsManager, + VespaServices vespaServices, + MetricsConsumers metricsConsumers) { + super(executor); + valuesFetcher = new ValuesFetcher(metricsManager, vespaServices, metricsConsumers); + } + + @Override + public HttpResponse handle(HttpRequest request) { + if (request.getMethod() != GET) return new JsonResponse(METHOD_NOT_ALLOWED, "Only GET is supported"); + + Path path = new Path(request.getUri()); + + if (path.matches(V1_PATH)) return v1Response(request.getUri()); + if (path.matches(VALUES_PATH)) return valuesResponse(request); + + return new ErrorResponse(NOT_FOUND, "No content at given path"); + } + + private JsonResponse v1Response(URI requestUri) { + try { + return new JsonResponse(OK, v1Content(requestUri)); + } catch (JSONException e) { + log.warning("Bad JSON construction in " + V1_PATH + " response: " + e.getMessage()); + return new ErrorResponse(INTERNAL_SERVER_ERROR, + "An error occurred, please try path '" + VALUES_PATH + "'"); + } + } + + private JsonResponse valuesResponse(HttpRequest request) { + try { + return new JsonResponse(OK, valuesFetcher.fetch(request.getProperty("consumer"))); + } catch (JsonRenderingException e) { + return new ErrorResponse(INTERNAL_SERVER_ERROR, e.getMessage()); + } + } + + // TODO: Use jackson with a "Resources" class instead of JSONObject + private String v1Content(URI requestUri) throws JSONException { + int port = requestUri.getPort(); + String host = requestUri.getHost(); + StringBuilder base = new StringBuilder("http://"); + base.append(host); + if (port >= 0) { + base.append(":").append(port); + } + String uriBase = base.toString(); + JSONArray linkList = new JSONArray(); + for (String api : new String[] {VALUES_PATH}) { + JSONObject resource = new JSONObject(); + resource.put("url", uriBase + api); + linkList.put(resource); + } + return new JSONObject().put("resources", linkList).toString(4); + } + +} diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ValuesFetcher.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ValuesFetcher.java new file mode 100644 index 00000000000..4686d9c1751 --- /dev/null +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/http/ValuesFetcher.java @@ -0,0 +1,65 @@ +/* + * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. + */ + +package ai.vespa.metricsproxy.http; + +import ai.vespa.metricsproxy.core.MetricsConsumers; +import ai.vespa.metricsproxy.core.MetricsManager; +import ai.vespa.metricsproxy.metric.model.ConsumerId; +import ai.vespa.metricsproxy.metric.model.MetricsPacket; +import ai.vespa.metricsproxy.metric.model.json.JsonRenderingException; +import ai.vespa.metricsproxy.service.VespaServices; + +import java.time.Instant; +import java.util.List; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import static ai.vespa.metricsproxy.metric.model.ConsumerId.toConsumerId; +import static ai.vespa.metricsproxy.metric.model.json.GenericJsonUtil.toGenericJsonModel; + +/** + * Generates metrics values in json format for the metrics/v1 rest api. + * + * @author gjoranv + */ +public class ValuesFetcher { + private static final Logger log = Logger.getLogger(ValuesFetcher.class.getName()); + + public static final ConsumerId DEFAULT_PUBLIC_CONSUMER_ID = toConsumerId("default"); + + private final MetricsManager metricsManager; + private final VespaServices vespaServices; + private final MetricsConsumers metricsConsumers; + + ValuesFetcher(MetricsManager metricsManager, + VespaServices vespaServices, + MetricsConsumers metricsConsumers) { + this.metricsManager = metricsManager; + this.vespaServices = vespaServices; + this.metricsConsumers = metricsConsumers; + } + + public String fetch(String requestedConsumer) throws JsonRenderingException { + ConsumerId consumer = getConsumerOrDefault(requestedConsumer); + + List<MetricsPacket> metrics = metricsManager.getMetrics(vespaServices.getVespaServices(), Instant.now()) + .stream() + .filter(metricsPacket -> metricsPacket.consumers().contains(consumer)) + .collect(Collectors.toList()); + return toGenericJsonModel(metrics).serialize(); + } + + private ConsumerId getConsumerOrDefault(String consumer) { + if (consumer == null) return DEFAULT_PUBLIC_CONSUMER_ID; + + ConsumerId consumerId = toConsumerId(consumer); + if (! metricsConsumers.getAllConsumers().contains(consumerId)) { + log.info("No consumer with id '" + consumer + "' - using the default consumer instead."); + return DEFAULT_PUBLIC_CONSUMER_ID; + } + return consumerId; + } + +} diff --git a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingException.java b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingException.java index 4c1d42d75ee..02292cea164 100644 --- a/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingException.java +++ b/metrics-proxy/src/main/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingException.java @@ -4,43 +4,15 @@ package ai.vespa.metricsproxy.metric.model.json; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.util.Map; -import java.util.logging.Logger; - -import static java.util.logging.Level.WARNING; - /** - * An exception to be thrown upon errors in json rendering, and that can deliver its message wrapped - * in an "error" json object. + * An unchecked exception to be thrown upon errors in json rendering. * * @author gjoranv */ public class JsonRenderingException extends RuntimeException { - private static Logger log = Logger.getLogger(JsonRenderingException.class.getName()); - JsonRenderingException(String message) { super(message); } - /** - * Returns the message wrapped in an "error" json object. In the unlikely case that json rendering of the - * error message fails, a plain text string will be returned instead. - */ - public String getMessageAsJson() { - return wrap(getMessage()); - } - - private static String wrap(String message) { - try { - return new ObjectMapper().writeValueAsString(Map.of("error", message)); - } catch (JsonProcessingException e) { - log.log(WARNING, "Could not encode error message to json:", e); - return "Could not encode error message to json, check the log for details."; - } - } - } diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingExceptionTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/ErrorResponseTest.java index 9b502d3f67a..ad2e06da19c 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/metric/model/json/JsonRenderingExceptionTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/ErrorResponseTest.java @@ -2,7 +2,7 @@ * Copyright 2019 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. */ -package ai.vespa.metricsproxy.metric.model.json; +package ai.vespa.metricsproxy.http; import org.junit.Test; @@ -11,17 +11,18 @@ import static org.junit.Assert.assertEquals; /** * @author gjoranv */ -public class JsonRenderingExceptionTest { +public class ErrorResponseTest { @Test public void error_message_is_wrapped_in_json_object() { - var exception = new JsonRenderingException("bad"); - assertEquals("{\"error\":\"bad\"}", exception.getMessageAsJson()); + var json = ErrorResponse.asErrorJson("bad"); + assertEquals("{\"error\":\"bad\"}", json); } @Test public void quotes_are_escaped() { - var exception = new JsonRenderingException("Message \" with \" embedded quotes."); - assertEquals("{\"error\":\"Message \\\" with \\\" embedded quotes.\"}", exception.getMessageAsJson()); + var json = ErrorResponse.asErrorJson("Message \" with \" embedded quotes."); + assertEquals("{\"error\":\"Message \\\" with \\\" embedded quotes.\"}", json); } + } diff --git a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/GenericMetricsHandlerTest.java b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/MetricsHandlerTest.java index dc89e5bb9f2..66220464e3e 100644 --- a/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/GenericMetricsHandlerTest.java +++ b/metrics-proxy/src/test/java/ai/vespa/metricsproxy/http/MetricsHandlerTest.java @@ -26,6 +26,8 @@ import ai.vespa.metricsproxy.service.VespaServices; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.yahoo.container.jdisc.RequestHandlerTestDriver; +import org.json.JSONArray; +import org.json.JSONObject; import org.junit.BeforeClass; import org.junit.Ignore; import org.junit.Test; @@ -36,9 +38,10 @@ import java.util.List; import java.util.concurrent.Executors; import static ai.vespa.metricsproxy.core.VespaMetrics.INSTANCE_DIMENSION_ID; -import static ai.vespa.metricsproxy.http.GenericMetricsHandler.DEFAULT_PUBLIC_CONSUMER_ID; +import static ai.vespa.metricsproxy.http.MetricsHandler.V1_PATH; +import static ai.vespa.metricsproxy.http.MetricsHandler.VALUES_PATH; +import static ai.vespa.metricsproxy.http.ValuesFetcher.DEFAULT_PUBLIC_CONSUMER_ID; import static ai.vespa.metricsproxy.metric.ExternalMetrics.VESPA_NODE_SERVICE_ID; -import static ai.vespa.metricsproxy.metric.model.ServiceId.toServiceId; import static ai.vespa.metricsproxy.metric.model.StatusCode.DOWN; import static ai.vespa.metricsproxy.metric.model.json.JacksonUtil.createObjectMapper; import static ai.vespa.metricsproxy.service.DummyService.METRIC_1; @@ -46,13 +49,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * @author gjoranv */ @SuppressWarnings("UnstableApiUsage") -public class GenericMetricsHandlerTest { +public class MetricsHandlerTest { private static final List<VespaService> testServices = ImmutableList.of( new DummyService(0, ""), @@ -66,7 +70,9 @@ public class GenericMetricsHandlerTest { private static final String CPU_METRIC = "cpu"; - private static final String URI = "http://localhost/metrics/v1/values"; + private static final String URI_BASE = "http://localhost"; + private static final String V1_URI = URI_BASE + V1_PATH; + private static final String VALUES_URI = URI_BASE + VALUES_PATH; private static RequestHandlerTestDriver testDriver; @@ -78,12 +84,12 @@ public class GenericMetricsHandlerTest { new MetricsPacket.Builder(VESPA_NODE_SERVICE_ID) .timestamp(Instant.now().getEpochSecond()) .putMetrics(ImmutableList.of(new Metric(CPU_METRIC, 12.345))))); - GenericMetricsHandler handler = new GenericMetricsHandler(Executors.newSingleThreadExecutor(), metricsManager, vespaServices, getMetricsConsumers()); + MetricsHandler handler = new MetricsHandler(Executors.newSingleThreadExecutor(), metricsManager, vespaServices, getMetricsConsumers()); testDriver = new RequestHandlerTestDriver(handler); } private GenericJsonModel getResponseAsJsonModel(String consumer) { - String response = testDriver.sendRequest(URI + "?consumer=" + consumer).readAll(); + String response = testDriver.sendRequest(VALUES_URI + "?consumer=" + consumer).readAll(); try { return createObjectMapper().readValue(response, GenericJsonModel.class); } catch (IOException e) { @@ -92,10 +98,23 @@ public class GenericMetricsHandlerTest { } } + @Test + public void v1_response_contains_values_uri() throws Exception { + String response = testDriver.sendRequest(V1_URI).readAll(); + JSONObject root = new JSONObject(response); + assertTrue(root.has("resources")); + + JSONArray resources = root.getJSONArray("resources"); + assertEquals(1, resources.length()); + + JSONObject valuesUrl = resources.getJSONObject(0); + assertEquals(VALUES_URI, valuesUrl.getString("url")); + } + @Ignore @Test - public void visually_inspect_response() throws Exception{ - String response = testDriver.sendRequest(URI+ "?consumer=default").readAll(); + public void visually_inspect_values_response() throws Exception{ + String response = testDriver.sendRequest(VALUES_URI).readAll(); ObjectMapper mapper = createObjectMapper(); var jsonModel = mapper.readValue(response, GenericJsonModel.class); System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonModel)); @@ -103,15 +122,15 @@ public class GenericMetricsHandlerTest { @Test public void no_explicit_consumer_gives_the_default_consumer() { - String responseDefaultConsumer = testDriver.sendRequest(URI + "?consumer=default").readAll(); - String responseNoConsumer = testDriver.sendRequest(URI).readAll(); + String responseDefaultConsumer = testDriver.sendRequest(VALUES_URI + "?consumer=default").readAll(); + String responseNoConsumer = testDriver.sendRequest(VALUES_URI).readAll(); assertEqualsExceptTimestamps(responseDefaultConsumer, responseNoConsumer); } @Test public void unknown_consumer_gives_the_default_consumer() { - String response = testDriver.sendRequest(URI).readAll(); - String responseUnknownConsumer = testDriver.sendRequest(URI + "?consumer=not_defined").readAll(); + String response = testDriver.sendRequest(VALUES_URI).readAll(); + String responseUnknownConsumer = testDriver.sendRequest(VALUES_URI + "?consumer=not_defined").readAll(); assertEqualsExceptTimestamps(response, responseUnknownConsumer); } diff --git a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/onnx/TypeConverter.java b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/onnx/TypeConverter.java index 29d600fa7c6..8c9fe60e1d4 100644 --- a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/onnx/TypeConverter.java +++ b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/onnx/TypeConverter.java @@ -53,17 +53,17 @@ class TypeConverter { private static TensorType.Value toValueType(Onnx.TensorProto.DataType dataType) { switch (dataType) { - case FLOAT: return TensorType.Value.FLOAT; + case FLOAT: return TensorType.Value.DOUBLE; case DOUBLE: return TensorType.Value.DOUBLE; // Imperfect conversion, for now: - case BOOL: return TensorType.Value.FLOAT; - case INT8: return TensorType.Value.FLOAT; - case INT16: return TensorType.Value.FLOAT; - case INT32: return TensorType.Value.FLOAT; + case BOOL: return TensorType.Value.DOUBLE; + case INT8: return TensorType.Value.DOUBLE; + case INT16: return TensorType.Value.DOUBLE; + case INT32: return TensorType.Value.DOUBLE; case INT64: return TensorType.Value.DOUBLE; - case UINT8: return TensorType.Value.FLOAT; - case UINT16: return TensorType.Value.FLOAT; - case UINT32: return TensorType.Value.FLOAT; + case UINT8: return TensorType.Value.DOUBLE; + case UINT16: return TensorType.Value.DOUBLE; + case UINT32: return TensorType.Value.DOUBLE; case UINT64: return TensorType.Value.DOUBLE; default: throw new IllegalArgumentException("A ONNX tensor with data type " + dataType + " cannot be converted to a Vespa tensor type"); diff --git a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/TypeConverter.java b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/TypeConverter.java index d8ddb01b650..08c0564ed8a 100644 --- a/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/TypeConverter.java +++ b/model-integration/src/main/java/ai/vespa/rankingexpression/importer/tensorflow/TypeConverter.java @@ -85,19 +85,19 @@ class TypeConverter { private static TensorType.Value toValueType(DataType dataType) { switch (dataType) { - case DT_FLOAT: return TensorType.Value.FLOAT; + case DT_FLOAT: return TensorType.Value.DOUBLE; case DT_DOUBLE: return TensorType.Value.DOUBLE; // Imperfect conversion, for now: - case DT_BOOL: return TensorType.Value.FLOAT; - case DT_BFLOAT16: return TensorType.Value.FLOAT; - case DT_HALF: return TensorType.Value.FLOAT; - case DT_INT8: return TensorType.Value.FLOAT; - case DT_INT16: return TensorType.Value.FLOAT; - case DT_INT32: return TensorType.Value.FLOAT; + case DT_BOOL: return TensorType.Value.DOUBLE; + case DT_BFLOAT16: return TensorType.Value.DOUBLE; + case DT_HALF: return TensorType.Value.DOUBLE; + case DT_INT8: return TensorType.Value.DOUBLE; + case DT_INT16: return TensorType.Value.DOUBLE; + case DT_INT32: return TensorType.Value.DOUBLE; case DT_INT64: return TensorType.Value.DOUBLE; - case DT_UINT8: return TensorType.Value.FLOAT; - case DT_UINT16: return TensorType.Value.FLOAT; - case DT_UINT32: return TensorType.Value.FLOAT; + case DT_UINT8: return TensorType.Value.DOUBLE; + case DT_UINT16: return TensorType.Value.DOUBLE; + case DT_UINT32: return TensorType.Value.DOUBLE; case DT_UINT64: return TensorType.Value.DOUBLE; default: throw new IllegalArgumentException("A TensorFlow tensor with data type " + dataType + " cannot be converted to a Vespa tensor type"); @@ -106,12 +106,12 @@ class TypeConverter { private static TensorType.Value toValueType(org.tensorflow.DataType dataType) { switch (dataType) { - case FLOAT: return TensorType.Value.FLOAT; + case FLOAT: return TensorType.Value.DOUBLE; case DOUBLE: return TensorType.Value.DOUBLE; // Imperfect conversion, for now: - case BOOL: return TensorType.Value.FLOAT; - case INT32: return TensorType.Value.FLOAT; - case UINT8: return TensorType.Value.FLOAT; + case BOOL: return TensorType.Value.DOUBLE; + case INT32: return TensorType.Value.DOUBLE; + case UINT8: return TensorType.Value.DOUBLE; case INT64: return TensorType.Value.DOUBLE; default: throw new IllegalArgumentException("A TensorFlow tensor with data type " + dataType + " cannot be converted to a Vespa tensor type"); diff --git a/model-integration/src/test/java/ai/vespa/rankingexpression/importer/onnx/OnnxMnistSoftmaxImportTestCase.java b/model-integration/src/test/java/ai/vespa/rankingexpression/importer/onnx/OnnxMnistSoftmaxImportTestCase.java index 07814687dc6..424e4d6c57c 100644 --- a/model-integration/src/test/java/ai/vespa/rankingexpression/importer/onnx/OnnxMnistSoftmaxImportTestCase.java +++ b/model-integration/src/test/java/ai/vespa/rankingexpression/importer/onnx/OnnxMnistSoftmaxImportTestCase.java @@ -43,14 +43,14 @@ public class OnnxMnistSoftmaxImportTestCase { // Check inputs assertEquals(1, model.inputs().size()); assertTrue(model.inputs().containsKey("Placeholder")); - assertEquals(TensorType.fromSpec("tensor<float>(d0[],d1[784])"), model.inputs().get("Placeholder")); + assertEquals(TensorType.fromSpec("tensor(d0[],d1[784])"), model.inputs().get("Placeholder")); // Check signature ImportedMlFunction output = model.defaultSignature().outputFunction("add", "add"); assertNotNull(output); assertEquals("join(reduce(join(rename(Placeholder, (d0, d1), (d0, d2)), constant(test_Variable), f(a,b)(a * b)), sum, d2), constant(test_Variable_1), f(a,b)(a + b))", output.expression()); - assertEquals(TensorType.fromSpec("tensor<float>(d0[],d1[784])"), + assertEquals(TensorType.fromSpec("tensor(d0[],d1[784])"), model.inputs().get(model.defaultSignature().inputs().get("Placeholder"))); assertEquals("{Placeholder=tensor(d0[],d1[784])}", output.argumentTypes().toString()); } diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainer.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainer.java new file mode 100644 index 00000000000..44d43081ef2 --- /dev/null +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainer.java @@ -0,0 +1,446 @@ +package com.yahoo.vespa.hosted.provision.maintenance; + +import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.NodeType; +import com.yahoo.jdisc.Metric; +import com.yahoo.log.LogLevel; +import com.yahoo.vespa.hosted.provision.Node; +import com.yahoo.vespa.hosted.provision.NodeRepository; + +import java.time.Duration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.logging.Logger; +import java.util.stream.Collectors; + +import com.yahoo.vespa.hosted.provision.node.Allocation; + +import java.util.*; +import java.util.function.Function; + +/** + * Performs analysis on the node repository to produce metrics that pertain to the capacity of the node repository. + * These metrics include: + * Spare host capacity, or how many hosts the repository can stand to lose without ending up in a situation where it's + * unable to find a new home for orphaned tenants. + * Overcommitted hosts, which tracks if there are any hosts whose capacity is less than the sum of its children's. + * + * @author mgimle + */ +public class CapacityReportMaintainer extends Maintainer { + + private final Metric metric; + private final NodeRepository nodeRepository; + private static final Logger log = Logger.getLogger(CapacityReportMaintainer.class.getName()); + + CapacityReportMaintainer(NodeRepository nodeRepository, + Metric metric, + Duration interval) { + super(nodeRepository, interval); + this.nodeRepository = nodeRepository; + this.metric = Objects.requireNonNull(metric); + } + + @Override + protected void maintain() { + metric.set("overcommittedHosts", countOvercommittedHosts(), null); + + Optional<HostFailurePath> failurePath = worstCaseHostLossLeadingToFailure(); + if (failurePath.isPresent()) { + int worstCaseHostLoss = failurePath.get().hostsCausingFailure.size(); + metric.set("spareHostCapacity", worstCaseHostLoss - 1, null); + } + } + + protected Optional<HostFailurePath> worstCaseHostLossLeadingToFailure() { + List<Node> hosts = getHosts(); + List<Node> tenants = getTenants(hosts); + Map<String, Node> nodeMap = constructHostnameToNodeMap(hosts); + Map<Node, List<Node>> nodeChildren = constructNodeChildrenMap(tenants, hosts, nodeMap); + Map<Node, AllocationResources> availableResources = constructAvailableResourcesMap(hosts, nodeChildren); + + Map<Node, Integer> timesNodeCanBeRemoved = computeMaximalRepeatedRemovals(hosts, nodeChildren, availableResources); + return greedyHeuristicFindFailurePath(timesNodeCanBeRemoved, hosts, nodeChildren, availableResources); + } + + // We only care about nodes in one of these states. + private Node.State[] relevantNodeStates = { + Node.State.active, + Node.State.inactive, + Node.State.dirty, + Node.State.provisioned, + Node.State.ready, + Node.State.reserved + }; + + private List<Node> getHosts() { + return nodeRepository.getNodes(NodeType.host, relevantNodeStates); + } + + private List<Node> getTenants(List<Node> hosts) { + var parentNames = hosts.stream().map(Node::hostname).collect(Collectors.toSet()); + return nodeRepository.getNodes(NodeType.tenant, relevantNodeStates).stream() + .filter(t -> parentNames.contains(t.parentHostname().orElse(""))) + .collect(Collectors.toList()); + } + + private Optional<HostFailurePath> greedyHeuristicFindFailurePath(Map<Node, Integer> heuristic, List<Node> hosts, + Map<Node, List<Node>> nodeChildren, + Map<Node, AllocationResources> availableResources) { + if (hosts.size() == 0) return Optional.empty(); + List<Node> parentRemovalPriorityList = heuristic.entrySet().stream() + .sorted(Comparator.comparingInt(Map.Entry::getValue)) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + for (int i = 1; i <= parentRemovalPriorityList.size(); i++) { + List<Node> hostsToRemove = parentRemovalPriorityList.subList(0, i); + var hostRemovalFailure = findHostRemovalFailure(hostsToRemove, hosts, nodeChildren, availableResources); + if (hostRemovalFailure.isPresent()) { + HostFailurePath failurePath = new HostFailurePath(); + failurePath.hostsCausingFailure = hostsToRemove; + failurePath.failureReason = hostRemovalFailure.get(); + return Optional.of(failurePath); + } + } + + throw new IllegalStateException("No path to failure found. This should be impossible!"); + } + + protected int countOvercommittedHosts() { + List<Node> hosts = getHosts(); + List<Node> tenants = getTenants(hosts); + var nodeMap = constructHostnameToNodeMap(hosts); + var nodeChildren = constructNodeChildrenMap(tenants, hosts, nodeMap); + var availableResources = constructAvailableResourcesMap(hosts, nodeChildren); + + List<Node> overcommittedNodes = findOvercommittedNodes(availableResources); + if (overcommittedNodes.size() != 0) { + log.log(LogLevel.WARNING, String.format("%d nodes are overcommitted! [ %s ]", overcommittedNodes.size(), + overcommittedNodes.stream().map(Node::hostname).collect(Collectors.joining(", ")))); + } + return overcommittedNodes.size(); + } + + private Map<String, Node> constructHostnameToNodeMap(List<Node> nodes) { + return nodes.stream().collect(Collectors.toMap(Node::hostname, n -> n)); + } + + private Map<Node, List<Node>> constructNodeChildrenMap(List<Node> tenants, List<Node> hosts, Map<String, Node> hostnameToNode) { + Map<Node, List<Node>> nodeChildren = tenants.stream() + .filter(n -> n.parentHostname().isPresent()) + .filter(n -> hostnameToNode.containsKey(n.parentHostname().get())) + .collect(Collectors.groupingBy( + n -> hostnameToNode.get(n.parentHostname().orElseThrow()))); + + for (var host : hosts) nodeChildren.putIfAbsent(host, List.of()); + + return nodeChildren; + } + + private Map<Node, AllocationResources> constructAvailableResourcesMap(List<Node> hosts, Map<Node, List<Node>> nodeChildren) { + Map<Node, AllocationResources> availableResources = new HashMap<>(); + for (var host : hosts) { + NodeResources hostResources = host.flavor().resources(); + int occupiedIps = 0; + Set<String> ipPool = host.ipAddressPool().asSet(); + for (var child : nodeChildren.get(host)) { + hostResources = hostResources.subtract(child.flavor().resources()); + occupiedIps += child.ipAddresses().stream().filter(ipPool::contains).count(); + } + availableResources.put(host, new AllocationResources(hostResources, host.ipAddressPool().asSet().size() - occupiedIps)); + } + + return availableResources; + } + + /** + * Computes a heuristic for each host, with a lower score indicating a higher perceived likelihood that removing + * the host causes an unrecoverable state + */ + private Map<Node, Integer> computeMaximalRepeatedRemovals(List<Node> hosts, Map<Node, List<Node>> nodeChildren, + Map<Node, AllocationResources> availableResources) { + Map<Node, Integer> timesNodeCanBeRemoved = hosts.stream().collect(Collectors.toMap( + Function.identity(), + _x -> Integer.MAX_VALUE + )); + for (Node host : hosts) { + List<Node> children = nodeChildren.get(host); + if (children.size() == 0) continue; + Map<Node, AllocationResources> resourceMap = new HashMap<>(availableResources); + Map<Node, List<Allocation>> containedAllocations = collateAllocations(nodeChildren); + + int timesHostCanBeRemoved = 0; + Optional<Node> unallocatedTenant; + while (timesHostCanBeRemoved < 1000) { // Arbritrary upper bound + unallocatedTenant = tryAllocateNodes(nodeChildren.get(host), hosts, resourceMap, containedAllocations); + if (unallocatedTenant.isEmpty()) { + timesHostCanBeRemoved++; + } else break; + } + timesNodeCanBeRemoved.put(host, timesHostCanBeRemoved); + } + + return timesNodeCanBeRemoved; + } + + private List<Node> findOvercommittedNodes(Map<Node, AllocationResources> availableResources) { + List<Node> overcommittedNodes = new ArrayList<>(); + for (var entry : availableResources.entrySet()) { + var resources = entry.getValue().nodeResources; + if (resources.vcpu() < 0 || resources.memoryGb() < 0 || resources.diskGb() < 0) { + overcommittedNodes.add(entry.getKey()); + } + } + return overcommittedNodes; + } + + private Map<Node, List<Allocation>> collateAllocations(Map<Node, List<Node>> nodeChildren) { + return nodeChildren.entrySet().stream().collect(Collectors.toMap( + Map.Entry::getKey, + e -> e.getValue().stream() + .map(Node::allocation).flatMap(Optional::stream) + .collect(Collectors.toList()) + )); + } + + /** + * Tests whether it's possible to remove the provided hosts. + * Does not mutate any input variable. + * @return Empty optional if removal is possible, information on what caused the failure otherwise + */ + private Optional<HostRemovalFailure> findHostRemovalFailure(List<Node> hostsToRemove, List<Node> allHosts, + Map<Node, List<Node>> nodechildren, + Map<Node, AllocationResources> availableResources) { + var containedAllocations = collateAllocations(nodechildren); + var resourceMap = new HashMap<>(availableResources); + List<Node> validAllocationTargets = allHosts.stream() + .filter(h -> !hostsToRemove.contains(h)) + .collect(Collectors.toList()); + if (validAllocationTargets.size() == 0) { + return Optional.of(HostRemovalFailure.none()); + } + + for (var host : hostsToRemove) { + Optional<Node> unallocatedNode = tryAllocateNodes(nodechildren.get(host), + validAllocationTargets, resourceMap, containedAllocations); + + if (unallocatedNode.isPresent()) { + AllocationFailureReasonList failures = collateAllocationFailures(unallocatedNode.get(), + validAllocationTargets, resourceMap, containedAllocations); + return Optional.of(HostRemovalFailure.create(host, unallocatedNode.get(), failures)); + } + } + return Optional.empty(); + } + + /** + * Attempts to allocate the listed nodes to a new host, mutating availableResources and containedAllocations, + * optionally returning the first node to fail, if one does. + * */ + private Optional<Node> tryAllocateNodes(List<Node> nodes, List<Node> hosts, + Map<Node, AllocationResources> availableResources, + Map<Node, List<Allocation>> containedAllocations) { + for (var node : nodes) { + if (!tryAllocateNode(node, hosts, availableResources, containedAllocations)) { + return Optional.of(node); + } + } + return Optional.empty(); + } + + private boolean tryAllocateNode(Node node, List<Node> hosts, + Map<Node, AllocationResources> availableResources, + Map<Node, List<Allocation>> containedAllocations) { + AllocationResources requiredNodeResources = AllocationResources.from(node.flavor().resources()); + for (var host : hosts) { + var availableHostResources = availableResources.get(host); + if (violatesParentHostPolicy(node, host, containedAllocations)) { + continue; + } + if (availableHostResources.satisfies(requiredNodeResources)) { + availableResources.put(host, availableHostResources.subtract(requiredNodeResources)); + if (node.allocation().isPresent()) { + containedAllocations.get(host).add(node.allocation().get()); + } + return true; + } + } + + return false; + } + + private boolean violatesParentHostPolicy(Node node, Node host, Map<Node, List<Allocation>> containedAllocations) { + if (node.allocation().isEmpty()) return false; + Allocation nodeAllocation = node.allocation().get(); + for (var allocation : containedAllocations.get(host)) { + if (allocation.membership().cluster().equalsIgnoringGroupAndVespaVersion(nodeAllocation.membership().cluster()) + && allocation.owner().equals(nodeAllocation.owner())) { + return true; + } + } + return false; + } + + private AllocationFailureReasonList collateAllocationFailures(Node node, List<Node> hosts, + Map<Node, AllocationResources> availableResources, + Map<Node, List<Allocation>> containedAllocations) { + List<AllocationFailureReason> allocationFailureReasons = new ArrayList<>(); + for (var host : hosts) { + AllocationFailureReason reason = new AllocationFailureReason(host); + var availableHostResources = availableResources.get(host); + reason.violatesParentHostPolicy = violatesParentHostPolicy(node, host, containedAllocations); + + NodeResources l = availableHostResources.nodeResources; + NodeResources r = node.flavor().resources(); + if (l.vcpu() < r.vcpu()) { reason.insufficientVcpu = true; } + if (l.memoryGb() < r.memoryGb()) { reason.insufficientMemoryGb = true; } + if (l.diskGb() < r.diskGb()) { reason.insufficientDiskGb = true; } + if (r.diskSpeed() != NodeResources.DiskSpeed.any && r.diskSpeed() != l.diskSpeed()) + { reason.incompatibleDiskSpeed = true; } + if (availableHostResources.availableIPs < 1) { reason.insufficientAvailableIPs = true; } + + allocationFailureReasons.add(reason); + } + + return new AllocationFailureReasonList(allocationFailureReasons); + } + + /** + * Contains the list of hosts that, upon being removed, caused an unrecoverable state, + * as well as the specific host and tenant which caused it. + */ + public static class HostFailurePath { + List<Node> hostsCausingFailure; + HostRemovalFailure failureReason; + } + + /** + * Data class used for detailing why removing the given tenant from the given host was unsuccessful. + * A failure might not be caused by failing to allocate a specific tenant, in which case the fields + * will be empty. + */ + public static class HostRemovalFailure { + Optional<Node> host; + Optional<Node> tenant; + AllocationFailureReasonList failureReasons; + public static HostRemovalFailure none() { + return new HostRemovalFailure( + Optional.empty(), + Optional.empty(), + new AllocationFailureReasonList(List.of())); + } + public static HostRemovalFailure create(Node host, Node tenant, AllocationFailureReasonList failureReasons) { + return new HostRemovalFailure( + Optional.of(host), + Optional.of(tenant), + failureReasons); + } + private HostRemovalFailure(Optional<Node> host, Optional<Node> tenant, AllocationFailureReasonList failureReasons) { + this.host = host; + this.tenant = tenant; + this.failureReasons = failureReasons; + } + } + + /** + * Used to describe the resources required for a tenant, and available to a host. + */ + private static class AllocationResources { + NodeResources nodeResources; + int availableIPs; + + public static AllocationResources from(NodeResources nodeResources) { + return new AllocationResources(nodeResources, 1); + } + + public AllocationResources(NodeResources nodeResources, int availableIPs) { + this.nodeResources = nodeResources; + this.availableIPs = availableIPs; + } + + public boolean satisfies(AllocationResources other) { + if (!this.nodeResources.satisfies(other.nodeResources)) return false; + return this.availableIPs >= other.availableIPs; + } + + public AllocationResources subtract(AllocationResources other) { + return new AllocationResources(this.nodeResources.subtract(other.nodeResources), this.availableIPs - other.availableIPs); + } + } + + /** + * Keeps track of the reason why a host rejected an allocation. + */ + private class AllocationFailureReason { + Node host; + public AllocationFailureReason (Node host) { + this.host = host; + } + public boolean insufficientVcpu = false; + public boolean insufficientMemoryGb = false; + public boolean insufficientDiskGb = false; + public boolean incompatibleDiskSpeed = false; + public boolean insufficientAvailableIPs = false; + public boolean violatesParentHostPolicy = false; + + public int numberOfReasons() { + int n = 0; + if (insufficientVcpu) n++; + if (insufficientMemoryGb) n++; + if (insufficientDiskGb) n++; + if (incompatibleDiskSpeed) n++; + if (insufficientAvailableIPs) n++; + if (violatesParentHostPolicy) n++; + return n; + } + + @Override + public String toString() { + List<String> reasons = new ArrayList<>(); + if (insufficientVcpu) reasons.add("insufficientVcpu"); + if (insufficientMemoryGb) reasons.add("insufficientMemoryGb"); + if (insufficientDiskGb) reasons.add("insufficientDiskGb"); + if (incompatibleDiskSpeed) reasons.add("incompatibleDiskSpeed"); + if (insufficientAvailableIPs) reasons.add("insufficientAvailableIPs"); + if (violatesParentHostPolicy) reasons.add("violatesParentHostPolicy"); + + return String.format("[%s]", String.join(", ", reasons)); + } + } + + /** + * Provides convenient methods for tallying failures. + */ + public static class AllocationFailureReasonList { + private List<AllocationFailureReason> allocationFailureReasons; + public AllocationFailureReasonList(List<AllocationFailureReason> allocationFailureReasons) { + this.allocationFailureReasons = allocationFailureReasons; + } + + long insufficientVcpu() { return allocationFailureReasons.stream().filter(r -> r.insufficientVcpu).count(); } + long insufficientMemoryGb() { return allocationFailureReasons.stream().filter(r -> r.insufficientMemoryGb).count(); } + long insufficientDiskGb() { return allocationFailureReasons.stream().filter(r -> r.insufficientDiskGb).count(); } + long incompatibleDiskSpeed() { return allocationFailureReasons.stream().filter(r -> r.incompatibleDiskSpeed).count(); } + long insufficientAvailableIps() { return allocationFailureReasons.stream().filter(r -> r.insufficientAvailableIPs).count(); } + long violatesParentHostPolicy() { return allocationFailureReasons.stream().filter(r -> r.violatesParentHostPolicy).count(); } + + public AllocationFailureReasonList singularReasonFailures() { + return new AllocationFailureReasonList(allocationFailureReasons.stream() + .filter(reason -> reason.numberOfReasons() == 1).collect(Collectors.toList())); + } + public AllocationFailureReasonList multipleReasonFailures() { + return new AllocationFailureReasonList(allocationFailureReasons.stream() + .filter(reason -> reason.numberOfReasons() > 1).collect(Collectors.toList())); + } + public long size() { + return allocationFailureReasons.size(); + } + @Override + public String toString() { + return String.format("CPU (%3d), Memory (%3d), Disk size (%3d), Disk speed (%3d), IP (%3d), Parent-Host Policy (%3d)", + insufficientVcpu(), insufficientMemoryGb(), insufficientDiskGb(), + incompatibleDiskSpeed(), insufficientAvailableIps(), violatesParentHostPolicy()); + } + } +} diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java index b9b1200d473..f661977d933 100644 --- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java +++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/maintenance/NodeRepositoryMaintenance.java @@ -46,6 +46,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent { private final Optional<LoadBalancerExpirer> loadBalancerExpirer; private final Optional<HostProvisionMaintainer> hostProvisionMaintainer; private final Optional<HostDeprovisionMaintainer> hostDeprovisionMaintainer; + private final CapacityReportMaintainer capacityReportMaintainer; @Inject public NodeRepositoryMaintenance(NodeRepository nodeRepository, Deployer deployer, InfraDeployer infraDeployer, @@ -81,6 +82,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent { new HostProvisionMaintainer(nodeRepository, durationFromEnv("host_provisioner_interval").orElse(defaults.hostProvisionerInterval), hostProvisioner, flagSource)); hostDeprovisionMaintainer = provisionServiceProvider.getHostProvisioner().map(hostProvisioner -> new HostDeprovisionMaintainer(nodeRepository, durationFromEnv("host_deprovisioner_interval").orElse(defaults.hostDeprovisionerInterval), hostProvisioner, flagSource)); + capacityReportMaintainer = new CapacityReportMaintainer(nodeRepository, metric, durationFromEnv("alert_interval").orElse(defaults.nodeAlerterInterval)); // The DuperModel is filled with infrastructure applications by the infrastructure provisioner, so explicitly run that now infrastructureProvisioner.maintain(); @@ -97,6 +99,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent { failedExpirer.deconstruct(); dirtyExpirer.deconstruct(); nodeRebooter.deconstruct(); + capacityReportMaintainer.deconstruct(); provisionedExpirer.deconstruct(); metricsReporter.deconstruct(); infrastructureProvisioner.deconstruct(); @@ -140,6 +143,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent { private final Duration dirtyExpiry; private final Duration provisionedExpiry; private final Duration rebootInterval; + private final Duration nodeAlerterInterval; private final Duration metricsInterval; private final Duration retiredInterval; private final Duration infrastructureProvisionInterval; @@ -158,6 +162,7 @@ public class NodeRepositoryMaintenance extends AbstractComponent { failedExpirerInterval = Duration.ofMinutes(10); provisionedExpiry = Duration.ofHours(4); rebootInterval = Duration.ofDays(30); + nodeAlerterInterval = Duration.ofHours(1); metricsInterval = Duration.ofMinutes(1); infrastructureProvisionInterval = Duration.ofMinutes(1); throttlePolicy = NodeFailer.ThrottlePolicy.hosted; diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTest.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTest.java new file mode 100644 index 00000000000..a486f8619c5 --- /dev/null +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTest.java @@ -0,0 +1,171 @@ +package com.yahoo.vespa.hosted.provision.maintenance; + +import com.yahoo.config.provision.NodeResources; +import com.yahoo.config.provision.NodeType; +import org.junit.Before; +import org.junit.Test; + +import java.io.IOException; +import java.nio.file.Paths; +import java.util.*; +import static org.junit.Assert.fail; +import static org.junit.Assert.*; + +/** + * @author mgimle + */ +public class CapacityReportMaintainerTest { + private CapacityReportMaintainerTester tester; + private CapacityReportMaintainer capacityReporter; + + @Before + public void setup() { + tester = new CapacityReportMaintainerTester(); + capacityReporter = tester.makeCapacityReportMaintainer(); + } + + @Test + public void testWithRealData() throws IOException { + String path = "./src/test/resources/zookeeper_dump.json"; + + tester.cleanRepository(); + tester.restoreNodeRepositoryFromJsonFile(Paths.get(path)); + var failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + if (failurePath.isPresent()) { + assertTrue(tester.nodeRepository.getNodes(NodeType.host).containsAll(failurePath.get().hostsCausingFailure)); + } else fail(); + } + + @Test + public void testOvercommittedHosts() { + tester.createNodes(7, 4, + 10, new NodeResources(-1, 10, 100), 10, + 0, new NodeResources(1, 10, 100), 10); + int overcommittedHosts = capacityReporter.countOvercommittedHosts(); + assertEquals(tester.nodeRepository.getNodes(NodeType.host).size(), overcommittedHosts); + } + + @Test + public void testEdgeCaseFailurePaths() { + tester.createNodes(1, 1, + 0, new NodeResources(1, 10, 100), 10, + 0, new NodeResources(1, 10, 100), 10); + var failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + assertFalse("Computing worst case host loss with no hosts should return an empty optional.", failurePath.isPresent()); + + // Odd edge case that should never be able to occur in prod + tester.createNodes(1, 10, + 10, new NodeResources(10, 1000, 10000), 100, + 1, new NodeResources(10, 1000, 10000), 100); + failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + assertTrue(failurePath.isPresent()); + assertTrue("Computing worst case host loss if all hosts have to be removed should result in an non-empty failureReason with empty nodes.", + failurePath.get().failureReason.tenant.isEmpty() && failurePath.get().failureReason.host.isEmpty()); + assertEquals(tester.nodeRepository.getNodes(NodeType.host).size(), failurePath.get().hostsCausingFailure.size()); + + tester.createNodes(3, 30, + 10, new NodeResources(0, 0, 10000), 1000, + 0, new NodeResources(0, 0, 0), 0); + failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + assertTrue(failurePath.isPresent()); + if (failurePath.get().failureReason.tenant.isPresent()) { + var failureReasons = failurePath.get().failureReason.failureReasons; + assertEquals("When there are multiple lacking resources, all failures are multipleReasonFailures", + failureReasons.size(), failureReasons.multipleReasonFailures().size()); + assertEquals(0, failureReasons.singularReasonFailures().size()); + } else fail(); + } + + @Test + public void testIpFailurePaths() { + tester.createNodes(1, 10, + 10, new NodeResources(10, 1000, 10000), 1, + 10, new NodeResources(10, 1000, 10000), 1); + var failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + assertTrue(failurePath.isPresent()); + if (failurePath.get().failureReason.tenant.isPresent()) { + var failureReasons = failurePath.get().failureReason.failureReasons; + assertEquals("All failures should be due to hosts having a lack of available ip addresses.", + failureReasons.singularReasonFailures().insufficientAvailableIps(), failureReasons.size()); + } else fail(); + + } + + @Test + public void testNodeResourceFailurePaths() { + tester.createNodes(1, 10, + 10, new NodeResources(1, 100, 1000), 100, + 10, new NodeResources(0, 100, 1000), 100); + var failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + assertTrue(failurePath.isPresent()); + if (failurePath.get().failureReason.tenant.isPresent()) { + var failureReasons = failurePath.get().failureReason.failureReasons; + assertEquals("All failures should be due to hosts lacking cpu cores.", + failureReasons.singularReasonFailures().insufficientVcpu(), failureReasons.size()); + } else fail(); + + tester.createNodes(1, 10, + 10, new NodeResources(10, 1, 1000), 100, + 10, new NodeResources(10, 0, 1000), 100); + failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + assertTrue(failurePath.isPresent()); + if (failurePath.get().failureReason.tenant.isPresent()) { + var failureReasons = failurePath.get().failureReason.failureReasons; + assertEquals("All failures should be due to hosts lacking memory.", + failureReasons.singularReasonFailures().insufficientMemoryGb(), failureReasons.size()); + } else fail(); + + tester.createNodes(1, 10, + 10, new NodeResources(10, 100, 10), 100, + 10, new NodeResources(10, 100, 0), 100); + failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + assertTrue(failurePath.isPresent()); + if (failurePath.get().failureReason.tenant.isPresent()) { + var failureReasons = failurePath.get().failureReason.failureReasons; + assertEquals("All failures should be due to hosts lacking disk space.", + failureReasons.singularReasonFailures().insufficientDiskGb(), failureReasons.size()); + } else fail(); + + int emptyHostsWithSlowDisk = 10; + tester.createNodes(1, 10, List.of(new NodeResources(1, 10, 100)), + 10, new NodeResources(0, 0, 0), 100, + 10, new NodeResources(10, 1000, 10000, NodeResources.DiskSpeed.slow), 100); + failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + assertTrue(failurePath.isPresent()); + if (failurePath.get().failureReason.tenant.isPresent()) { + var failureReasons = failurePath.get().failureReason.failureReasons; + assertEquals("All empty hosts should be invalid due to having incompatible disk speed.", + failureReasons.singularReasonFailures().incompatibleDiskSpeed(), emptyHostsWithSlowDisk); + } else fail(); + + } + + + @Test + public void testParentHostPolicyIntegrityFailurePaths() { + tester.createNodes(1, 1, + 10, new NodeResources(1, 100, 1000), 100, + 10, new NodeResources(10, 1000, 10000), 100); + var failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + assertTrue(failurePath.isPresent()); + if (failurePath.get().failureReason.tenant.isPresent()) { + var failureReasons = failurePath.get().failureReason.failureReasons; + assertEquals("With only one type of tenant, all failures should be due to violation of the parent host policy.", + failureReasons.singularReasonFailures().violatesParentHostPolicy(), failureReasons.size()); + } else fail(); + + tester.createNodes(1, 2, + 10, new NodeResources(10, 100, 1000), 1, + 0, new NodeResources(0, 0, 0), 0); + failurePath = capacityReporter.worstCaseHostLossLeadingToFailure(); + assertTrue(failurePath.isPresent()); + if (failurePath.get().failureReason.tenant.isPresent()) { + var failureReasons = failurePath.get().failureReason.failureReasons; + assertNotEquals("Fewer distinct children than hosts should result in some parent host policy violations.", + failureReasons.size(), failureReasons.singularReasonFailures().violatesParentHostPolicy()); + assertNotEquals(0, failureReasons.singularReasonFailures().violatesParentHostPolicy()); + } else fail(); + } +} + + diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTester.java b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTester.java new file mode 100644 index 00000000000..ccea4691f10 --- /dev/null +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/maintenance/CapacityReportMaintainerTester.java @@ -0,0 +1,290 @@ +package com.yahoo.vespa.hosted.provision.maintenance; + +import com.fasterxml.jackson.annotation.JsonGetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.yahoo.collections.Tuple2; +import com.yahoo.component.Version; +import com.yahoo.config.provision.*; +import com.yahoo.test.ManualClock; +import com.yahoo.vespa.curator.Curator; +import com.yahoo.vespa.curator.mock.MockCurator; +import com.yahoo.vespa.hosted.provision.Node; +import com.yahoo.vespa.hosted.provision.NodeRepository; +import com.yahoo.vespa.hosted.provision.node.IP; +import com.yahoo.vespa.hosted.provision.provisioning.FlavorConfigBuilder; +import com.yahoo.vespa.hosted.provision.testutils.MockNameResolver; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * @author mgimle + */ +public class CapacityReportMaintainerTester { + public static final Zone zone = new Zone(Environment.prod, RegionName.from("us-east")); + + // Components with state + public final ManualClock clock = new ManualClock(); + public final NodeRepository nodeRepository; + + CapacityReportMaintainerTester() { + Curator curator = new MockCurator(); + NodeFlavors f = new NodeFlavors(new FlavorConfigBuilder().build()); + nodeRepository = new NodeRepository(f, curator, clock, zone, new MockNameResolver().mockAnyLookup(), + DockerImage.fromString("docker-registry.domain.tld:8080/dist/vespa"), true); + } + + CapacityReportMaintainer makeCapacityReportMaintainer() { + return new CapacityReportMaintainer(nodeRepository, new MetricsReporterTest.TestMetric(), Duration.ofDays(1)); + } + + List<NodeModel> createDistinctChildren(int amount, List<NodeResources> childResources) { + String wantedVespaVersion = "7.67.9"; + List<String> tenants = List.of("foocom", "barinc"); + List<String> applications = List.of("ranking", "search"); + List<Tuple2<ClusterSpec.Type, String>> clusterSpecs = List.of( + new Tuple2<>(ClusterSpec.Type.content, "content"), + new Tuple2<>(ClusterSpec.Type.container, "suggest"), + new Tuple2<>(ClusterSpec.Type.admin, "log")); + int childCombinations = tenants.size() * applications.size() * clusterSpecs.size(); + List<NodeModel> distinctChildren = new ArrayList<>(); + + for (int j = 0; j < amount;) + for (var tenant : tenants) + for (var application : applications) + for (var clusterSpec : clusterSpecs) { + if (j >= amount) continue; + NodeModel child = new NodeModel(); + child.type = NodeType.tenant; + NodeResources cnr = childResources.get(j % childResources.size()); + child.minCpuCores = cnr.vcpu(); + child.minMainMemoryAvailableGb = cnr.memoryGb(); + child.minDiskAvailableGb = cnr.diskGb(); + child.fastDisk = true; + child.ipAddresses = Set.of(); + child.additionalIpAddresses = Set.of(); + child.owner = new NodeModel.OwnerModel(); + child.owner.tenant = tenant + j / childCombinations; + child.owner.application = application; + child.owner.instance = "default"; + child.membership = NodeModel.MembershipModel.from(clusterSpec.first, clusterSpec.second, 0); + child.wantedVespaVersion = wantedVespaVersion; + child.state = Node.State.active; + child.environment = Flavor.Type.DOCKER_CONTAINER; + + distinctChildren.add(child); + j++; + } + + return distinctChildren; + } + + List<Node> createHostsWithChildren(int childrenPerHost, List<NodeModel> distinctChildren, int amount, NodeResources excessCapacity, int excessIps) { + List<Node> hosts = new ArrayList<>(); + int j = 0; + for (int i = 0; i < amount; i++) { + String parentRoot = ".not.a.real.hostname.yahoo.com"; + String parentName = "parent" + i; + String hostname = parentName + parentRoot; + + List<NodeResources> childResources = new ArrayList<>(); + for (int k = 0; k < childrenPerHost; k++, j++) { + NodeModel childModel = distinctChildren.get(j % distinctChildren.size()); + String childHostName = parentName + "-v6-" + k + parentRoot; + childModel.id = childHostName; + childModel.hostname = childHostName; + childModel.ipAddresses = Set.of(String.format("%04X::%04X", i, k)); + childModel.membership.index = j / distinctChildren.size(); + childModel.parentHostname = Optional.of(hostname); + + Node childNode = createNodeFromModel(childModel); + childResources.add(childNode.flavor().resources()); + hosts.add(childNode); + } + + final int hostindex = i; + Set<String> availableIps = IntStream.range(0, childrenPerHost + excessIps) + .mapToObj(n -> String.format("%04X::%04X", hostindex, n)) + .collect(Collectors.toSet()); + + NodeResources nr = containingNodeResources(childResources, + excessCapacity); + Node node = nodeRepository.createNode(hostname, hostname, + new IP.Config(Set.of("::"), availableIps), Optional.empty(), + Optional.empty(), new Flavor(nr), NodeType.host); + hosts.add(node); + } + return hosts; + } + + List<Node> createEmptyHosts(int baseIndex, int amount, NodeResources capacity, int ips) { + List<Node> hosts = new ArrayList<>(); + for (int i = baseIndex; i < baseIndex + amount; i++) { + String parentRoot = ".empty.not.a.real.hostname.yahoo.com"; + String parentName = "parent" + i; + String hostname = parentName + parentRoot; + + final int hostid = i; + Set<String> availableIps = IntStream.range(0, ips) + .mapToObj(n -> String.format("%04X::%04X", hostid, n)) + .collect(Collectors.toSet()); + Node node = nodeRepository.createNode(hostname, hostname, + new IP.Config(Set.of("::"), availableIps), Optional.empty(), + Optional.empty(), new Flavor(capacity), NodeType.host); + hosts.add(node); + } + return hosts; + } + + void createNodes(int childrenPerHost, int numDistinctChildren, + int numHosts, NodeResources hostExcessCapacity, int hostExcessIps, + int numEmptyHosts, NodeResources emptyHostExcessCapacity, int emptyHostExcessIps) { + List<NodeResources> childResources = List.of( + new NodeResources(1, 10, 100) + + ); + createNodes(childrenPerHost, numDistinctChildren, childResources, + numHosts, hostExcessCapacity, hostExcessIps, + numEmptyHosts, emptyHostExcessCapacity, emptyHostExcessIps); + } + void createNodes(int childrenPerHost, int numDistinctChildren, List<NodeResources> childResources, + int numHosts, NodeResources hostExcessCapacity, int hostExcessIps, + int numEmptyHosts, NodeResources emptyHostExcessCapacity, int emptyHostExcessIps) { + cleanRepository(); + List<NodeModel> possibleChildren = createDistinctChildren(numDistinctChildren, childResources); + + List<Node> nodes = new ArrayList<>(); + nodes.addAll(createHostsWithChildren(childrenPerHost, possibleChildren, numHosts, hostExcessCapacity, hostExcessIps)); + nodes.addAll(createEmptyHosts(numHosts, numEmptyHosts, emptyHostExcessCapacity, emptyHostExcessIps)); + + nodeRepository.addNodes(nodes); + } + + + NodeResources containingNodeResources(List<NodeResources> resources, NodeResources excessCapacity) { + NodeResources usedByChildren = resources.stream() + .reduce(new NodeResources(0, 0, 0), NodeResources::add); + return usedByChildren.add(excessCapacity); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + static class NodeModel { + static class MembershipModel { + @JsonProperty ClusterSpec.Type clustertype; + @JsonProperty String clusterid; + @JsonProperty String group; + @JsonProperty int index; + @JsonProperty boolean retired; + + public static MembershipModel from(ClusterSpec.Type type, String id, int index) { + MembershipModel m = new MembershipModel(); + m.clustertype = type; + m.clusterid = id; + m.group = "0"; + m.index = index; + m.retired = false; + return m; + } + public String toString() { + return String.format("%s/%s/%s/%d%s", clustertype, clusterid, group, index, retired ? "/retired" : ""); + } + } + static class OwnerModel { + @JsonProperty String tenant; + @JsonProperty String application; + @JsonProperty String instance; + } + + @JsonProperty String id; + @JsonProperty String hostname; + @JsonProperty NodeType type; + Optional<String> parentHostname = Optional.empty(); + @JsonSetter("parentHostname") + private void setParentHostname(String name) { this.parentHostname = Optional.ofNullable(name); } + @JsonGetter("parentHostname") + String getParentHostname() { return parentHostname.orElse(null); } + @JsonProperty double minDiskAvailableGb; + @JsonProperty double minMainMemoryAvailableGb; + @JsonProperty double minCpuCores; + @JsonProperty boolean fastDisk; + @JsonProperty Set<String> ipAddresses; + @JsonProperty Set<String> additionalIpAddresses; + + @JsonProperty OwnerModel owner; + @JsonProperty MembershipModel membership; + @JsonProperty String wantedVespaVersion; + @JsonProperty Node.State state; + @JsonProperty Flavor.Type environment; + } + + static class NodeRepositoryModel { + @JsonProperty + List<NodeModel> nodes; + } + + Node createNodeFromModel(NodeModel nodeModel) { + ClusterMembership membership = null; + ApplicationId owner = null; + if (nodeModel.membership != null && nodeModel.owner != null) { + membership = ClusterMembership.from( + nodeModel.membership.toString(), + Version.fromString(nodeModel.wantedVespaVersion)); + owner = ApplicationId.from(nodeModel.owner.tenant, nodeModel.owner.application, nodeModel.owner.instance); + } + + NodeResources.DiskSpeed diskSpeed; + // According to @kraune, container tenants are not fuzzy about disk type + if (membership != null && nodeModel.type == NodeType.tenant && membership.cluster().type() == ClusterSpec.Type.container) { + diskSpeed = NodeResources.DiskSpeed.any; + } else { + diskSpeed = nodeModel.fastDisk ? NodeResources.DiskSpeed.fast : NodeResources.DiskSpeed.slow; + } + NodeResources nr = new NodeResources(nodeModel.minCpuCores, nodeModel.minMainMemoryAvailableGb, + nodeModel.minDiskAvailableGb, diskSpeed); + Flavor f = new Flavor(nr); + + Node node = nodeRepository.createNode(nodeModel.id, nodeModel.hostname, + new IP.Config(nodeModel.ipAddresses, nodeModel.additionalIpAddresses), + nodeModel.parentHostname, Optional.empty(), f, nodeModel.type); + + if (membership != null) { + return node.allocate(owner, membership, Instant.now()); + } else { + return node; + } + } + + public void restoreNodeRepositoryFromJsonFile(Path path) throws IOException { + byte[] jsonData = Files.readAllBytes(path); + ObjectMapper om = new ObjectMapper(); + + NodeRepositoryModel repositoryModel = om.readValue(jsonData, NodeRepositoryModel.class); + List<NodeModel> nmods = repositoryModel.nodes; + + List<Node> nodes = new ArrayList<>(); + for (var nmod : nmods) { + if (nmod.type != NodeType.host && nmod.type != NodeType.tenant) continue; + + nodes.add(createNodeFromModel(nmod)); + } + + nodeRepository.addNodes(nodes); + } + + void cleanRepository() { + nodeRepository.getNodes(NodeType.host).forEach(n -> nodeRepository.removeRecursively(n, true)); + nodeRepository.getNodes().forEach(n -> nodeRepository.removeRecursively(n, true)); + if (nodeRepository.getNodes().size() != 0) { + throw new IllegalStateException("Cleaning repository didn't remove all nodes! [" + nodeRepository.getNodes().size() + "]"); + } + } +} diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/maintenance.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/maintenance.json index b72523963c0..28881717e7c 100644 --- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/maintenance.json +++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/v2/responses/maintenance.json @@ -1,6 +1,9 @@ { "jobs": [ { + "name": "CapacityReportMaintainer" + }, + { "name": "DirtyExpirer" }, { diff --git a/node-repository/src/test/resources/zookeeper_dump.json b/node-repository/src/test/resources/zookeeper_dump.json new file mode 100644 index 00000000000..40786cc95dd --- /dev/null +++ b/node-repository/src/test/resources/zookeeper_dump.json @@ -0,0 +1 @@ +{"nodes":[{"id":"2132864288","hostname":"2132864288","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c27::10e0"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":34,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"399083239"},{"id":"-167779330","hostname":"-167779330","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c2a::105c","1.1.77.48"],"additionalIpAddresses":["c2a::1064","c2a::1063","c2a::1062","c2a::1061","c2a::1060","c2a::106f","c2a::106e","c2a::107c","c2a::107b","c2a::107a","c2a::1069","c2a::1068","c2a::1067","c2a::1066","c2a::1065","c2a::1075","c2a::1074","c2a::1073","c2a::1072","c2a::1071","c2a::1070","c2a::105f","c2a::105e","c2a::105d","c2a::106d","c2a::106c","c2a::106b","c2a::106a","c2a::1079","c2a::1078","c2a::1077","c2a::1076"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":22,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"119146780","hostname":"119146780","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c40::10c1"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"50511102","group":"0","index":1,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1163357258"},{"id":"578117757","hostname":"578117757","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c2a::1046"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":37,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"547954621"},{"id":"-727133536","hostname":"-727133536","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c28::10b4"],"additionalIpAddresses":[],"owner":{"tenant":"-2059328563","application":"-2059328563","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"661679481","hostname":"661679481","type":"host","minDiskAvailableGb":480.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.75.240","c26::1006"],"additionalIpAddresses":["c26::100a","c26::100e","c26::100d","c26::100c","c26::100b","c26::1009","c26::1008","c26::1007"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1882495550","hostname":"-1882495550","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.90.177","c40::1099"],"additionalIpAddresses":["c40::10ac","c40::10ad","c40::10ae","c40::10af","c40::10a0","c40::10a1","c40::10a2","c40::10a3","c40::10a4","c40::10a5","c40::10a6","c40::10a7","c40::10a8","c40::10a9","c40::109a","c40::109b","c40::109c","c40::109d","c40::109e","c40::109f","c40::10b0","c40::10b1","c40::10b2","c40::10b3","c40::10b4","c40::10b5","c40::10b6","c40::10b7","c40::10b8","c40::10b9","c40::10aa","c40::10ab"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":33,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1139644897","hostname":"-1139644897","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::100c"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1028554796","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1921643747"},{"id":"-160875418","hostname":"-160875418","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c40::103a"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-348618915"},{"id":"952554185","hostname":"952554185","type":"proxy","minDiskAvailableGb":380.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.177","c29::100e"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"1385652422","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1385652422","group":"0","index":11,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-2012135647"},{"id":"1388729559","hostname":"1388729559","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.77.114","c2b::10c0"],"additionalIpAddresses":["c2b::10cf","c2b::10ce","c2b::10cb","c2b::10ca","c2b::10cd","c2b::10cc","c2b::10e0","c2b::10d7","c2b::10d6","c2b::10d9","c2b::10d8","c2b::10d3","c2b::10d2","c2b::10d5","c2b::10d4","c2b::10df","c2b::10dc","c2b::10db","c2b::10de","c2b::10dd","c2b::10d1","c2b::10d0","c2b::10c6","c2b::10c5","c2b::10c8","c2b::10c7","c2b::10c2","c2b::10c1","c2b::10c4","c2b::10c3","c2b::10da","c2b::10c9"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":29,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-59894657","hostname":"-59894657","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c2a::1068"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-989926828","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-989926828","group":"3","index":13,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-167779330"},{"id":"1334604191","hostname":"1334604191","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c45::106e"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3020272","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-86489022"},{"id":"948605591","hostname":"948605591","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c40::10bc"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1163357258"},{"id":"-721473314","hostname":"-721473314","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c45::10a6"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":23,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-496292156"},{"id":"1522752738","hostname":"1522752738","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c28::107c"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1938379550","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1207107937"},{"id":"-614255295","hostname":"-614255295","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2a::1001"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-157661344"},{"id":"-1025299769","hostname":"-1025299769","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c28::103e"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1080688167","group":"0","index":31,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"514006240"},{"id":"-1475643347","hostname":"-1475643347","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.90.166","c40::1073"],"additionalIpAddresses":["c40::1079","c40::107a","c40::107b","c40::1074","c40::1075","c40::1076","c40::1077","c40::1078"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":1,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1069032350","hostname":"1069032350","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c2a::109e"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":38,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1229740191"},{"id":"1978709977","hostname":"1978709977","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c27::10cc"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1080688167","group":"0","index":34,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"2047882875"},{"id":"-802112485","hostname":"-802112485","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c40::107b"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":48,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1475643347"},{"id":"364514184","hostname":"364514184","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c40::103d"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":59,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1867043290"},{"id":"-1443714106","hostname":"-1443714106","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::1006"],"additionalIpAddresses":[],"owner":{"tenant":"-1983508318","application":"-1983508318","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1983508318","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-157661344"},{"id":"-213243522","hostname":"-213243522","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::106b"],"additionalIpAddresses":[],"owner":{"tenant":"20489253","application":"509174424","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"20489253","group":"0","index":7,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"470255561"},{"id":"-2112448477","hostname":"-2112448477","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2b::104a"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1080688167","group":"0","index":30,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1649000480"},{"id":"740337689","hostname":"740337689","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c27::10ab"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":18,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-838248773"},{"id":"505145380","hostname":"505145380","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1073"],"additionalIpAddresses":[],"owner":{"tenant":"223606124","application":"222116894","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":1,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-86489022"},{"id":"-1622997757","hostname":"-1622997757","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c27::10aa"],"additionalIpAddresses":[],"owner":{"tenant":"-2059328563","application":"-2059328563","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"2142745311","group":"0","index":7,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-838248773"},{"id":"771948099","hostname":"771948099","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c2a::1003"],"additionalIpAddresses":[],"owner":{"tenant":"-2059328563","application":"-2059328563","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-157661344"},{"id":"514006240","hostname":"514006240","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.111","c28::1037"],"additionalIpAddresses":["c28::103a","c28::103b","c28::103c","c28::103d","c28::103e","c28::103f","c28::1040","c28::1041","c28::1042","c28::1043","c28::1044","c28::1045","c28::1046","c28::1047","c28::1048","c28::1049","c28::104a","c28::104b","c28::104c","c28::104d","c28::104e","c28::104f","c28::1050","c28::1051","c28::1052","c28::1053","c28::1054","c28::1055","c28::1056","c28::1057","c28::1038","c28::1039"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":15,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"616215289","hostname":"616215289","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1066"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-267429795","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1609604578","group":"0","index":7,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"470255561"},{"id":"599368188","hostname":"599368188","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::107d"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-1937557253","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1714276213"},{"id":"2083350634","hostname":"2083350634","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c27::1059"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":15,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1354781178"},{"id":"446594262","hostname":"446594262","type":"config","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["1.1.88.253","c2f::101c"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"-1457980858","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1457980858","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1735904210"},{"id":"-2023242964","hostname":"-2023242964","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c2b::10c3"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1938379550","group":"0","index":11,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1388729559"},{"id":"-1679023711","hostname":"-1679023711","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.113","c28::1058"],"additionalIpAddresses":["c28::105a","c28::105b","c28::105c","c28::105d","c28::105e","c28::105f","c28::1060","c28::1061","c28::1062","c28::1063","c28::1064","c28::1065","c28::1066","c28::1067","c28::1068","c28::1069","c28::106a","c28::106b","c28::106c","c28::106d","c28::106e","c28::106f","c28::1070","c28::1071","c28::1072","c28::1073","c28::1074","c28::1075","c28::1076","c28::1077","c28::1078","c28::1059"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":25,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-57510712","hostname":"-57510712","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::1008"],"additionalIpAddresses":[],"owner":{"tenant":"-1983508318","application":"-1983508318","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-157661344"},{"id":"1428826999","hostname":"1428826999","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1078"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-267429795","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1609604578","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1714276213"},{"id":"-881058014","hostname":"-881058014","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1071"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":1,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-86489022"},{"id":"-1267056614","hostname":"-1267056614","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c40::10bf"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1281572300","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1163357258"},{"id":"-348618915","hostname":"-348618915","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.90.176","c40::1016"],"additionalIpAddresses":["c40::1038","c40::108a","c40::108b","c40::108c","c40::108d","c40::108e","c40::108f","c40::1080","c40::1081","c40::1082","c40::1083","c40::1084","c40::1085","c40::1020","c40::1086","c40::1087","c40::1088","c40::1089","c40::107c","c40::107d","c40::103a","c40::107e","c40::107f","c40::1090","c40::1091","c40::1092","c40::1093","c40::1094","c40::1095","c40::1096","c40::1097","c40::1098"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":38,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1637544448","hostname":"-1637544448","type":"tenant","minDiskAvailableGb":100.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c2a::1049"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-354378891","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-354378891","group":"0","index":14,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"547954621"},{"id":"1902610178","hostname":"1902610178","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2b::10ab"],"additionalIpAddresses":[],"owner":{"tenant":"-840754951","application":"-594933386","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1909456459","group":"0","index":27,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1248447295"},{"id":"-1820717283","hostname":"-1820717283","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c40::1060"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":51,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1444436682"},{"id":"1649000480","hostname":"1649000480","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.77.112","c2b::103c"],"additionalIpAddresses":["c2b::1058","c2b::1057","c2b::1059","c2b::1054","c2b::1053","c2b::1056","c2b::1055","c2b::104a","c2b::103f","c2b::103e","c2b::105c","c2b::103d","c2b::1050","c2b::1052","c2b::1051","c2b::1047","c2b::1046","c2b::1049","c2b::1048","c2b::1043","c2b::1042","c2b::1045","c2b::1044","c2b::105b","c2b::105a","c2b::104f","c2b::104c","c2b::104b","c2b::104e","c2b::104d","c2b::1041","c2b::1040"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":31,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1921643747","hostname":"1921643747","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.77.33","c2a::100a"],"additionalIpAddresses":["c2a::100f","c2a::100e","c2a::100d","c2a::100c","c2a::100b","c2a::1012","c2a::1011","c2a::1010"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":12,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1964321151","hostname":"1964321151","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2a::1048"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1080688167","group":"0","index":36,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"547954621"},{"id":"2047882875","hostname":"2047882875","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c27::10b8","1.1.76.48"],"additionalIpAddresses":["c27::10ce","c27::10cd","c27::10cf","c27::10ca","c27::10cc","c27::10cb","c27::10c5","c27::10c4","c27::10c7","c27::10c6","c27::10c9","c27::10c8","c27::10c1","c27::10c0","c27::10c3","c27::10c2","c27::10bd","c27::10bc","c27::10bf","c27::10be","c27::10bb","c27::10ba","c27::10d6","c27::10d5","c27::10d8","c27::10d7","c27::10b9","c27::10d0","c27::10d2","c27::10d1","c27::10d4","c27::10d3"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":20,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1546836058","hostname":"-1546836058","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c28::109c"],"additionalIpAddresses":[],"owner":{"tenant":"20489253","application":"20489253","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"235035247","hostname":"235035247","type":"confighost","minDiskAvailableGb":500.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":24.0,"fastDisk":false,"ipAddresses":["c18::101c","1.1.72.112"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"1456486768","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1456486768","group":"0","index":2,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1890405598","hostname":"1890405598","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c2b::108a"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":44,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-156463203"},{"id":"600750011","hostname":"600750011","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::105a"],"additionalIpAddresses":[],"owner":{"tenant":"223606124","application":"222116894","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1115596927","group":"0","index":1,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"547954621"},{"id":"311892192","hostname":"311892192","type":"proxyhost","minDiskAvailableGb":480.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.176","c29::100b"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"-1907449433","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-1907449433","group":"0","index":1,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"364756938","hostname":"364756938","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c26::1009"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"661679481"},{"id":"1918672427","hostname":"1918672427","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c28::10a1"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-1117571703","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"-37262403","hostname":"-37262403","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":16.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c2a::107c"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-1117571703","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-633292731","group":"0","index":7,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-167779330"},{"id":"-1883322427","hostname":"-1883322427","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c45::108b"],"additionalIpAddresses":[],"owner":{"tenant":"-840754951","application":"-594933386","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1909456459","group":"5","index":32,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1189393853"},{"id":"-2107433954","hostname":"-2107433954","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c28::10bc"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1701655965"},{"id":"664730080","hostname":"664730080","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":8.0,"fastDisk":true,"ipAddresses":["c45::10a8"],"additionalIpAddresses":[],"owner":{"tenant":"-840754951","application":"-594933386","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":11,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-496292156"},{"id":"-272869570","hostname":"-272869570","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c40::106b"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":49,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"887692099"},{"id":"-1386011164","hostname":"-1386011164","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c28::107e"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-989926828","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-989926828","group":"2","index":12,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1207107937"},{"id":"-7467260","hostname":"-7467260","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::103c"],"additionalIpAddresses":[],"owner":{"tenant":"-2059328563","application":"-2059328563","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":4,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"547954621"},{"id":"-1574159711","hostname":"-1574159711","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1070"],"additionalIpAddresses":[],"owner":{"tenant":"-1983508318","application":"-1983508318","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1983508318","group":"0","index":10,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-86489022"},{"id":"1357831777","hostname":"1357831777","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c45::10a9"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1938379550","group":"0","index":12,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-496292156"},{"id":"1750960332","hostname":"1750960332","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c26::100b"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-354378891","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":11,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"661679481"},{"id":"1867043290","hostname":"1867043290","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.90.178","c40::103b"],"additionalIpAddresses":["c40::1057","c40::1058","c40::1059","c40::104a","c40::104b","c40::104c","c40::104d","c40::104e","c40::104f","c40::1040","c40::1041","c40::1042","c40::1043","c40::1044","c40::1045","c40::1046","c40::1047","c40::1048","c40::1049","c40::105a","c40::105b","c40::103c","c40::103d","c40::103e","c40::103f","c40::1050","c40::1051","c40::1052","c40::1053","c40::1054","c40::1055","c40::1056"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":36,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1207107937","hostname":"1207107937","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.114","c28::1079"],"additionalIpAddresses":["c28::107a","c28::107b","c28::107c","c28::107d","c28::107e","c28::107f","c28::1090","c28::1080","c28::1081","c28::1082","c28::1083","c28::1084","c28::1085","c28::1086","c28::1087","c28::1088","c28::1089","c28::108a","c28::108b","c28::108c","c28::108d","c28::108e","c28::108f","c28::1091","c28::1092","c28::1093","c28::1094","c28::1095","c28::1096","c28::1097","c28::1098","c28::1099"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":16,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1584485603","hostname":"1584485603","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2b::105f"],"additionalIpAddresses":[],"owner":{"tenant":"-840754951","application":"-594933386","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1909456459","group":"1","index":33,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"955898783"},{"id":"-1684989694","hostname":"-1684989694","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c2b::103e"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":19,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1649000480"},{"id":"532469033","hostname":"532469033","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c28::109f"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":39,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"-86489022","hostname":"-86489022","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.91.230","c45::106d"],"additionalIpAddresses":["c45::1073","c45::1072","c45::1071","c45::1070","c45::106f","c45::106e","c45::1075","c45::1074"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1900209634","hostname":"1900209634","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c2a::109f","1.1.77.50"],"additionalIpAddresses":["c2a::10a9","c2a::10a8","c2a::10a7","c2a::10a6","c2a::10a5","c2a::10a4","c2a::10a3","c2a::10a2","c2a::10a1","c2a::10a0","c2a::10b0","c2a::10bf","c2a::10be","c2a::10bd","c2a::10bc","c2a::10bb","c2a::10ba","c2a::10b9","c2a::10b8","c2a::10b7","c2a::10b6","c2a::10b5","c2a::10b4","c2a::10b3","c2a::10b2","c2a::10b1","c2a::10af","c2a::10ae","c2a::10ad","c2a::10ac","c2a::10ab","c2a::10aa"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":17,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"2002418683","hostname":"2002418683","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":8.0,"fastDisk":true,"ipAddresses":["c45::1068"],"additionalIpAddresses":[],"owner":{"tenant":"-840754951","application":"-594933386","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"470255561"},{"id":"-1711460001","hostname":"-1711460001","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":16.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c2b::108b"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-1117571703","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-633292731","group":"0","index":10,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-156463203"},{"id":"-2017379996","hostname":"-2017379996","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c2b::1060"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":58,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"955898783"},{"id":"-1735904210","hostname":"-1735904210","type":"confighost","minDiskAvailableGb":500.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":24.0,"fastDisk":false,"ipAddresses":["1.1.88.253","c2f::101c"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"1456486768","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1456486768","group":"0","index":3,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1929743267","hostname":"1929743267","type":"proxyhost","minDiskAvailableGb":480.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c26::1019","1.1.75.239"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"-1907449433","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-1907449433","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1225570730","hostname":"1225570730","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c28::10a0"],"additionalIpAddresses":[],"owner":{"tenant":"-1983508318","application":"-1983508318","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"-85024190","hostname":"-85024190","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":16.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c28::10d0"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-1117571703","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-633292731","group":"0","index":9,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1701655965"},{"id":"-1547078812","hostname":"-1547078812","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c40::1020"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"103891066","group":"0","index":38,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-348618915"},{"id":"-496292156","hostname":"-496292156","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c45::10a4","1.1.91.241"],"additionalIpAddresses":["c45::10b9","c45::10b8","c45::10b7","c45::10b6","c45::10b5","c45::10b4","c45::10b3","c45::10b2","c45::10b1","c45::10b0","c45::10bf","c45::10be","c45::10bd","c45::10bc","c45::10bb","c45::10ba","c45::10a9","c45::10a8","c45::10a7","c45::10a6","c45::10a5","c45::10c4","c45::10c3","c45::10c2","c45::10c1","c45::10c0","c45::10af","c45::10ae","c45::10ad","c45::10ac","c45::10ab","c45::10aa"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":35,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1536527140","hostname":"1536527140","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2b::107f"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":3,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-156463203"},{"id":"1854900286","hostname":"1854900286","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c45::101e"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":22,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1189393853"},{"id":"-838248773","hostname":"-838248773","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c27::1097","1.1.76.51"],"additionalIpAddresses":["c27::10ac","c27::10ab","c27::10ae","c27::10ad","c27::10af","c27::1099","c27::10aa","c27::1098","c27::10a3","c27::10a2","c27::10a5","c27::10a4","c27::10a7","c27::10a6","c27::10a9","c27::10a8","c27::10a1","c27::10a0","c27::109a","c27::10b4","c27::10b3","c27::10b6","c27::10b5","c27::10b7","c27::109c","c27::109b","c27::109e","c27::109d","c27::10b0","c27::109f","c27::10b2","c27::10b1"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":27,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1960158311","hostname":"-1960158311","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c40::10be"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-1937557253","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1163357258"},{"id":"-1106087263","hostname":"-1106087263","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c2a::10bf"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":42,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1900209634"},{"id":"-926263172","hostname":"-926263172","type":"tenant","minDiskAvailableGb":100.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c45::1033"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-354378891","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-354378891","group":"0","index":18,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1041720612"},{"id":"-707456158","hostname":"-707456158","type":"tenant","minDiskAvailableGb":100.0,"minMainMemoryAvailableGb":16.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c2a::107f"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-1117571703","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":10,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1229740191"},{"id":"-236952228","hostname":"-236952228","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c27::10cf"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":35,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"2047882875"},{"id":"1578622635","hostname":"1578622635","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2b::10c2"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":4,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1388729559"},{"id":"1162041343","hostname":"1162041343","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2a::10a0"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":2,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1900209634"},{"id":"633207040","hostname":"633207040","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2a::1069"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1080688167","group":"0","index":33,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-167779330"},{"id":"1916875905","hostname":"1916875905","type":"tenant","minDiskAvailableGb":100.0,"minMainMemoryAvailableGb":16.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c2b::103d"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-1117571703","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":9,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1649000480"},{"id":"1641707288","hostname":"1641707288","type":"tenant","minDiskAvailableGb":100.0,"minMainMemoryAvailableGb":16.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c40::10bd"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-1117571703","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1163357258"},{"id":"-1334376780","hostname":"-1334376780","type":"host","minDiskAvailableGb":3840.0,"minMainMemoryAvailableGb":384.0,"minCpuCores":72.0,"fastDisk":true,"ipAddresses":["1.1.108.16","c6e::1000"],"additionalIpAddresses":["c6e::1030","c6e::101e","c6e::101d","c6e::101f","c6e::101a","c6e::101c","c6e::101b","c6e::1019","c6e::1018","c6e::1015","c6e::1014","c6e::1017","c6e::1016","c6e::1011","c6e::1010","c6e::1013","c6e::1012","c6e::1020","c6e::100d","c6e::102f","c6e::100c","c6e::102e","c6e::100f","c6e::100e","c6e::102b","c6e::102a","c6e::100b","c6e::102d","c6e::100a","c6e::102c","c6e::1008","c6e::1007","c6e::1029","c6e::1009","c6e::1004","c6e::1026","c6e::1003","c6e::1025","c6e::1006","c6e::1028","c6e::1005","c6e::1027","c6e::1022","c6e::1021","c6e::1002","c6e::1024","c6e::1001","c6e::1023"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":40,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-160632664","hostname":"-160632664","type":"tenant","minDiskAvailableGb":200.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c28::109e"],"additionalIpAddresses":[],"owner":{"tenant":"20489253","application":"20489253","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"20489253","group":"1","index":11,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"1791360512","hostname":"1791360512","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::10b3"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1298275357","group":"0","index":2,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1900209634"},{"id":"-14354461","hostname":"-14354461","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c2a::1080"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":20,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1229740191"},{"id":"1301179204","hostname":"1301179204","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c28::10d2"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":43,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1701655965"},{"id":"1444436682","hostname":"1444436682","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.90.167","c40::101a"],"additionalIpAddresses":["c40::1035","c40::2005","c40::1060","c40::105c","c40::105d","c40::1033","c40::105e","c40::105f"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":4,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1599446916","hostname":"-1599446916","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1069"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-267429795","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"470255561"},{"id":"4045543","hostname":"4045543","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c27::1056"],"additionalIpAddresses":[],"owner":{"tenant":"20489253","application":"20489253","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":9,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1354781178"},{"id":"-1799188960","hostname":"-1799188960","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":16.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c2a::10be"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-1117571703","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-633292731","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1900209634"},{"id":"588123629","hostname":"588123629","type":"confighost","minDiskAvailableGb":500.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":24.0,"fastDisk":false,"ipAddresses":["1.1.88.118","c2e::1024"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"1456486768","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1456486768","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-233161475","hostname":"-233161475","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1034"],"additionalIpAddresses":[],"owner":{"tenant":"20489253","application":"20489253","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1041720612"},{"id":"-41196190","hostname":"-41196190","type":"proxyhost","minDiskAvailableGb":480.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.175","c29::100c"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"-1907449433","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-1907449433","group":"0","index":2,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1802676639","hostname":"1802676639","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c2a::10bd"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1938379550","group":"0","index":7,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1900209634"},{"id":"885520938","hostname":"885520938","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":16.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c2b::10c1"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-1117571703","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-633292731","group":"0","index":11,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1388729559"},{"id":"599465803","hostname":"599465803","type":"proxy","minDiskAvailableGb":380.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c26::1019","1.1.75.239"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"1385652422","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1385652422","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1929743267"},{"id":"-1701655965","hostname":"-1701655965","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c28::10bb","1.1.76.115"],"additionalIpAddresses":["c28::10d0","c28::10d1","c28::10d2","c28::10d3","c28::10d4","c28::10d5","c28::10d6","c28::10d7","c28::10d8","c28::10d9","c28::10da","c28::10db","c28::10bc","c28::10bd","c28::10be","c28::10bf","c28::10c0","c28::10c1","c28::10c2","c28::10c3","c28::10c4","c28::10c5","c28::10c6","c28::10c7","c28::10c8","c28::10c9","c28::10ca","c28::10cb","c28::10cc","c28::10cd","c28::10ce","c28::10cf"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":18,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1088285226","hostname":"-1088285226","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c2b::10e0"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":45,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1388729559"},{"id":"-1324278299","hostname":"-1324278299","type":"tenant","minDiskAvailableGb":100.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c2b::1061"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-354378891","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-354378891","group":"0","index":19,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"955898783"},{"id":"-853977115","hostname":"-853977115","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c40::1038"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":61,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-348618915"},{"id":"2027705888","hostname":"2027705888","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::106f"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-1937557253","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-989926828","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-86489022"},{"id":"-325256607","hostname":"-325256607","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c2b::108d"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1938379550","group":"0","index":10,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-156463203"},{"id":"608077507","hostname":"608077507","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c28::10d1"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1938379550","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1701655965"},{"id":"946330777","hostname":"946330777","type":"tenant","minDiskAvailableGb":100.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c27::106a"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-354378891","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-354378891","group":"0","index":16,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1354781178"},{"id":"1057615881","hostname":"1057615881","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c40::103e"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":7,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1867043290"},{"id":"-1683193172","hostname":"-1683193172","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c28::10a2"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-267429795","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"-750612409","hostname":"-750612409","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::1007"],"additionalIpAddresses":[],"owner":{"tenant":"223606124","application":"222116894","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-157661344"},{"id":"-906345219","hostname":"-906345219","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::106a"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3020272","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1938379550","group":"0","index":4,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"470255561"},{"id":"-1877433577","hostname":"-1877433577","type":"config","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c18::101c","1.1.72.112"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"-1457980858","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1457980858","group":"0","index":7,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"235035247"},{"id":"-28371617","hostname":"-28371617","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c45::10a7"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"103891066","group":"0","index":35,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-496292156"},{"id":"-93733509","hostname":"-93733509","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::107c"],"additionalIpAddresses":[],"owner":{"tenant":"20489253","application":"509174424","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"20489253","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1714276213"},{"id":"-76886408","hostname":"-76886408","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1065"],"additionalIpAddresses":[],"owner":{"tenant":"20489253","application":"509174424","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":4,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"470255561"},{"id":"829651041","hostname":"829651041","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c28::107b"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":19,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1207107937"},{"id":"-2027478477","hostname":"-2027478477","type":"host","minDiskAvailableGb":3840.0,"minMainMemoryAvailableGb":384.0,"minCpuCores":72.0,"fastDisk":true,"ipAddresses":["c6d::1000","1.1.107.211"],"additionalIpAddresses":["c6d::100f","c6d::100d","c6d::102f","c6d::100e","c6d::100b","c6d::102d","c6d::100c","c6d::102e","c6d::102b","c6d::100a","c6d::102c","c6d::1020","c6d::1021","c6d::1006","c6d::1028","c6d::1007","c6d::1029","c6d::1004","c6d::1026","c6d::1005","c6d::1027","c6d::1002","c6d::1024","c6d::1003","c6d::1025","c6d::1022","c6d::1001","c6d::1023","c6d::1008","c6d::1009","c6d::101e","c6d::101f","c6d::101c","c6d::101d","c6d::101a","c6d::101b","c6d::1010","c6d::1030","c6d::1017","c6d::1018","c6d::1015","c6d::1016","c6d::1013","c6d::1014","c6d::1011","c6d::1012","c6d::102a","c6d::1019"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":39,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"547954621","hostname":"547954621","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.77.52","c2a::103b"],"additionalIpAddresses":["c2a::1042","c2a::1041","c2a::1040","c2a::104f","c2a::104e","c2a::104d","c2a::104c","c2a::105b","c2a::105a","c2a::1049","c2a::1048","c2a::1047","c2a::1046","c2a::1045","c2a::1044","c2a::1043","c2a::1053","c2a::1052","c2a::1051","c2a::1050","c2a::103f","c2a::103e","c2a::103d","c2a::103c","c2a::104b","c2a::104a","c2a::1059","c2a::1058","c2a::1057","c2a::1056","c2a::1055","c2a::1054"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":28,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1623155622","hostname":"-1623155622","type":"tenant","minDiskAvailableGb":100.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c27::10cd"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-354378891","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-354378891","group":"0","index":17,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"2047882875"},{"id":"1163357258","hostname":"1163357258","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.90.165","c40::10bb"],"additionalIpAddresses":["c40::10bd","c40::10be","c40::10bf","c40::10c0","c40::10c1","c40::10c2","c40::10c3","c40::10bc"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":9,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1377413432","hostname":"1377413432","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.52","c27::1076"],"additionalIpAddresses":["c27::1079","c27::1078","c27::1093","c27::1092","c27::1095","c27::1094","c27::1096","c27::1077","c27::1080","c27::108b","c27::108a","c27::108d","c27::108c","c27::108f","c27::108e","c27::1089","c27::1082","c27::1081","c27::1084","c27::1083","c27::1086","c27::1085","c27::1088","c27::1087","c27::1091","c27::1090","c27::107a","c27::107c","c27::107b","c27::107e","c27::107d","c27::107f"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":26,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1293851708","hostname":"1293851708","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::105b"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":2,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"547954621"},{"id":"-328344759","hostname":"-328344759","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c26::1008"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"661679481"},{"id":"-872134365","hostname":"-872134365","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c28::105c"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1080688167","group":"0","index":35,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1679023711"},{"id":"735725302","hostname":"735725302","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1077"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-267429795","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1714276213"},{"id":"957646904","hostname":"957646904","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c27::1074"],"additionalIpAddresses":[],"owner":{"tenant":"-1983508318","application":"-1983508318","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1983508318","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1354781178"},{"id":"1752827223","hostname":"1752827223","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c45::105e"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":52,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1464327220"},{"id":"399083239","hostname":"399083239","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["c27::10d9","1.1.76.34"],"additionalIpAddresses":["c27::10df","c27::10de","c27::10e1","c27::10e0","c27::10db","c27::10da","c27::10dd","c27::10dc"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":13,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-2012135647","hostname":"-2012135647","type":"proxyhost","minDiskAvailableGb":480.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.177","c29::100e"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"-1907449433","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-1907449433","group":"0","index":3,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-156463203","hostname":"-156463203","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c2b::107e","1.1.77.116"],"additionalIpAddresses":["c2b::1098","c2b::1097","c2b::1099","c2b::108c","c2b::108b","c2b::108e","c2b::108d","c2b::108a","c2b::107f","c2b::1094","c2b::1093","c2b::1096","c2b::1095","c2b::1090","c2b::1092","c2b::1091","c2b::1087","c2b::1086","c2b::1089","c2b::1088","c2b::109d","c2b::109c","c2b::109e","c2b::109b","c2b::109a","c2b::108f","c2b::1083","c2b::1082","c2b::1085","c2b::1084","c2b::1081","c2b::1080"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":23,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1157803570","hostname":"-1157803570","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c26::100d"],"additionalIpAddresses":[],"owner":{"tenant":"223606124","application":"222116894","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1115596927","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"661679481"},{"id":"-1041720612","hostname":"-1041720612","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c45::1009","1.1.91.242"],"additionalIpAddresses":["c45::2004","c45::2005","c45::101d","c45::101c","c45::101a","c45::103b","c45::103a","c45::1025","c45::1024","c45::1023","c45::1022","c45::1021","c45::1020","c45::100d","c45::102f","c45::100c","c45::102e","c45::100b","c45::102d","c45::100a","c45::102c","c45::1019","c45::1039","c45::1038","c45::1037","c45::1036","c45::1035","c45::1034","c45::1033","c45::1032","c45::1031","c45::1030"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":37,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1390248937","hostname":"1390248937","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c27::1058"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"103891066","group":"0","index":33,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1354781178"},{"id":"1354781178","hostname":"1354781178","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.49","c27::1055"],"additionalIpAddresses":["c27::1057","c27::1056","c27::1059","c27::1058","c27::1071","c27::1070","c27::1073","c27::1072","c27::1075","c27::1074","c27::106b","c27::106a","c27::106d","c27::106c","c27::106f","c27::106e","c27::1068","c27::1067","c27::1069","c27::1060","c27::1062","c27::1061","c27::1064","c27::1063","c27::1066","c27::1065","c27::105f","c27::105a","c27::105c","c27::105b","c27::105e","c27::105d"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":19,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-853734361","hostname":"-853734361","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c28::109d"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"-1113612637","hostname":"-1113612637","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c45::1099"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":56,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1189393853"},{"id":"-141137706","hostname":"-141137706","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c27::108a"],"additionalIpAddresses":[],"owner":{"tenant":"-840754951","application":"-594933386","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1909456459","group":"2","index":29,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1377413432"},{"id":"891383906","hostname":"891383906","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c2b::105e"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"103891066","group":"0","index":36,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"955898783"},{"id":"1153041919","hostname":"1153041919","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1036"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-354378891","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1041720612"},{"id":"1057858635","hostname":"1057858635","type":"tenant","minDiskAvailableGb":200.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c26::100a"],"additionalIpAddresses":[],"owner":{"tenant":"20489253","application":"20489253","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"20489253","group":"0","index":12,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"661679481"},{"id":"-918737798","hostname":"-918737798","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c27::10d8"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"2047882875"},{"id":"-1420235233","hostname":"-1420235233","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c28::10b3"],"additionalIpAddresses":[],"owner":{"tenant":"-2059328563","application":"-2059328563","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"2142745311","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"-1371473654","hostname":"-1371473654","type":"proxy","minDiskAvailableGb":380.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.175","c29::100c"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"1385652422","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1385652422","group":"0","index":10,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-41196190"},{"id":"-573954917","hostname":"-573954917","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c40::10c0"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1028554796","group":"0","index":1,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1163357258"},{"id":"-1248447295","hostname":"-1248447295","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.77.111","c2b::109f"],"additionalIpAddresses":["c2b::10ad","c2b::10ac","c2b::10af","c2b::10ae","c2b::10ab","c2b::10aa","c2b::10b5","c2b::10b4","c2b::10b7","c2b::10b6","c2b::10b1","c2b::10b0","c2b::10b3","c2b::10b2","c2b::10b9","c2b::10b8","c2b::10be","c2b::10bd","c2b::10bf","c2b::10ba","c2b::10bc","c2b::10bb","c2b::10a4","c2b::10a3","c2b::10a6","c2b::10a5","c2b::10a0","c2b::10a2","c2b::10a1","c2b::10a8","c2b::10a7","c2b::10a9"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":32,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1189393853","hostname":"-1189393853","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c45::101b","1.1.91.240"],"additionalIpAddresses":["c45::1095","c45::1094","c45::1093","c45::1092","c45::1091","c45::1090","c45::101f","c45::101e","c45::109f","c45::109e","c45::109d","c45::109c","c45::109b","c45::109a","c45::1089","c45::1088","c45::10a3","c45::10a2","c45::102b","c45::10a1","c45::102a","c45::10a0","c45::108f","c45::108e","c45::108d","c45::108c","c45::108b","c45::108a","c45::1099","c45::1098","c45::1097","c45::1096"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":34,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"2055029541","hostname":"2055029541","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c28::109b"],"additionalIpAddresses":[],"owner":{"tenant":"223606124","application":"222116894","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"685634437","hostname":"685634437","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::103d"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":7,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"547954621"},{"id":"-1714276213","hostname":"-1714276213","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.91.215","c45::1076"],"additionalIpAddresses":["c45::107c","c45::107b","c45::107a","c45::1079","c45::1078","c45::1077","c45::107e","c45::107d"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":10,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1832746594","hostname":"-1832746594","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::100b"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"50511102","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1921643747"},{"id":"-1746965313","hostname":"-1746965313","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c45::101f"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"103891066","group":"0","index":34,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1189393853"},{"id":"497260739","hostname":"497260739","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c28::103a"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-989926828","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-989926828","group":"1","index":11,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"514006240"},{"id":"479858175","hostname":"479858175","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::106c"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-1937557253","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":4,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"470255561"},{"id":"955898783","hostname":"955898783","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c2b::105d","1.1.77.113"],"additionalIpAddresses":["c2b::1079","c2b::1076","c2b::1075","c2b::1078","c2b::1077","c2b::106a","c2b::106c","c2b::106b","c2b::105f","c2b::105e","c2b::1072","c2b::1071","c2b::1074","c2b::1073","c2b::1070","c2b::1069","c2b::1068","c2b::1065","c2b::1064","c2b::1067","c2b::1066","c2b::107b","c2b::107a","c2b::107d","c2b::107c","c2b::106e","c2b::106d","c2b::106f","c2b::1061","c2b::1060","c2b::1063","c2b::1062"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":30,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-328587513","hostname":"-328587513","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c40::103c"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"103891066","group":"0","index":37,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1867043290"},{"id":"-187956317","hostname":"-187956317","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1072"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1298275357","group":"0","index":1,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-86489022"},{"id":"829408287","hostname":"829408287","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c40::109a"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":9,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1882495550"},{"id":"1309316986","hostname":"1309316986","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1067"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3020272","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"470255561"},{"id":"-2079112861","hostname":"-2079112861","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c28::107d"],"additionalIpAddresses":[],"owner":{"tenant":"-840754951","application":"-594933386","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1909456459","group":"4","index":31,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1207107937"},{"id":"78846402","hostname":"78846402","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c2a::1002"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-157661344"},{"id":"-1464327220","hostname":"-1464327220","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["c45::1026","1.1.91.231"],"additionalIpAddresses":["c45::1062","c45::1061","c45::1060","c45::1027","c45::105f","c45::105e","c45::105d","c45::1063"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1292469885","hostname":"1292469885","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::107e"],"additionalIpAddresses":[],"owner":{"tenant":"20489253","application":"509174424","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1714276213"},{"id":"-782222819","hostname":"-782222819","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c27::10af"],"additionalIpAddresses":[],"owner":{"tenant":"-840754951","application":"-594933386","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1909456459","group":"3","index":30,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-838248773"},{"id":"1785286633","hostname":"1785286633","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.76.33","c27::1000"],"additionalIpAddresses":["c27::1002","c27::1001","c27::1004","c27::1003","c27::1006","c27::1005","c27::1008","c27::1007"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":14,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"752622429","hostname":"752622429","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c40::1062"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":50,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1580793796"},{"id":"2121928696","hostname":"2121928696","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":8.0,"fastDisk":true,"ipAddresses":["c45::1079"],"additionalIpAddresses":[],"owner":{"tenant":"-840754951","application":"-594933386","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":9,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1714276213"},{"id":"-839959959","hostname":"-839959959","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c27::1077"],"additionalIpAddresses":[],"owner":{"tenant":"-828802209","application":"-1480249367","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1281572300","group":"0","index":1,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1377413432"},{"id":"1883464133","hostname":"1883464133","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":16.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c28::103c"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-1117571703","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-633292731","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"514006240"},{"id":"-1475324516","hostname":"-1475324516","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c27::10ae"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":41,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-838248773"},{"id":"246558497","hostname":"246558497","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c2a::100e"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1938379550","group":"0","index":9,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1921643747"},{"id":"-726245083","hostname":"-726245083","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c2b::104c"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":16,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1649000480"},{"id":"-1518514965","hostname":"-1518514965","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c27::105a"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":1,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1354781178"},{"id":"1465049796","hostname":"1465049796","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c2a::1004"],"additionalIpAddresses":[],"owner":{"tenant":"20489253","application":"509174424","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-157661344"},{"id":"-493022966","hostname":"-493022966","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c27::10bd"],"additionalIpAddresses":[],"owner":{"tenant":"-1983508318","application":"-1983508318","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"2047882875"},{"id":"-1553982724","hostname":"-1553982724","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c28::109a","1.1.76.116"],"additionalIpAddresses":["c28::109b","c28::109c","c28::109d","c28::109e","c28::109f","c28::10b0","c28::10b1","c28::10b2","c28::10b3","c28::10b4","c28::10b5","c28::10b6","c28::10b7","c28::10b8","c28::10b9","c28::10a9","c28::10ba","c28::10a0","c28::10a1","c28::10a2","c28::10a3","c28::10a4","c28::10a5","c28::10a6","c28::10a7","c28::10a8","c28::10aa","c28::10ab","c28::10ac","c28::10ad","c28::10ae","c28::10af"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":21,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1891348774","hostname":"1891348774","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::1075"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-354378891","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":10,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-86489022"},{"id":"1505350174","hostname":"1505350174","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c40::10c3"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-354378891","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1163357258"},{"id":"-714928956","hostname":"-714928956","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":8.0,"fastDisk":true,"ipAddresses":["c2b::1056"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-989926828","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1649000480"},{"id":"2091475201","hostname":"2091475201","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2b::10a1"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1248447295"},{"id":"887692099","hostname":"887692099","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.90.164","c40::106a"],"additionalIpAddresses":["c40::1070","c40::1071","c40::1072","c40::106b","c40::106c","c40::106d","c40::106e","c40::106f"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":2,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1479936903","hostname":"-1479936903","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::107a"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3020272","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1714276213"},{"id":"-1524345195","hostname":"-1524345195","type":"config","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["1.1.88.118","c2e::1024"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"-1457980858","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1457980858","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"588123629"},{"id":"283824987","hostname":"283824987","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c45::1082"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":54,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-222846136"},{"id":"551963991","hostname":"551963991","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c27::108b"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":17,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1377413432"},{"id":"1343527840","hostname":"1343527840","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c28::1059"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":60,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1679023711"},{"id":"1614460806","hostname":"1614460806","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c2a::109b"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1080688167","group":"0","index":37,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1229740191"},{"id":"-1608048742","hostname":"-1608048742","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":48.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c45::2004"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-989926828","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-989926828","group":"0","index":10,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1041720612"},{"id":"-1419346780","hostname":"-1419346780","type":"tenant","minDiskAvailableGb":100.0,"minMainMemoryAvailableGb":32.0,"minCpuCores":6.0,"fastDisk":true,"ipAddresses":["c2b::104b"],"additionalIpAddresses":[],"owner":{"tenant":"1914981343","application":"-354378891","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-354378891","group":"0","index":15,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1649000480"},{"id":"-446543200","hostname":"-446543200","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":8.0,"fastDisk":true,"ipAddresses":["c2a::100d"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-989926828","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1921643747"},{"id":"-1018385272","hostname":"-1018385272","type":"proxy","minDiskAvailableGb":380.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["1.1.76.176","c29::100b"],"additionalIpAddresses":[],"owner":{"tenant":"-1802291313","application":"1385652422","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1385652422","group":"0","index":9,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"311892192"},{"id":"470255561","hostname":"470255561","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.91.229","c45::1064"],"additionalIpAddresses":["c45::106b","c45::106a","c45::1069","c45::1068","c45::1067","c45::1066","c45::1065","c45::106c"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":8,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1198247077","hostname":"1198247077","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c45::1074"],"additionalIpAddresses":[],"owner":{"tenant":"-840754951","application":"-594933386","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-86489022"},{"id":"1229740191","hostname":"1229740191","type":"host","minDiskAvailableGb":1920.0,"minMainMemoryAvailableGb":256.0,"minCpuCores":48.0,"fastDisk":true,"ipAddresses":["c2a::107e","1.1.77.49"],"additionalIpAddresses":["c2a::1086","c2a::1085","c2a::1084","c2a::1083","c2a::1082","c2a::1081","c2a::1080","c2a::109e","c2a::109d","c2a::109c","c2a::109b","c2a::109a","c2a::1089","c2a::1088","c2a::1087","c2a::1097","c2a::1096","c2a::1095","c2a::1094","c2a::1093","c2a::1092","c2a::1091","c2a::1090","c2a::107f","c2a::108f","c2a::108e","c2a::108d","c2a::108c","c2a::108b","c2a::108a","c2a::1099","c2a::1098"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":24,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-1718401466","hostname":"-1718401466","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":8.0,"fastDisk":true,"ipAddresses":["c28::103d"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-989926828","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":7,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"514006240"},{"id":"-1021446456","hostname":"-1021446456","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c26::1007"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-989926828","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"661679481"},{"id":"-146858262","hostname":"-146858262","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c27::1078"],"additionalIpAddresses":[],"owner":{"tenant":"-2059328563","application":"-2059328563","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"2142745311","group":"0","index":6,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1377413432"},{"id":"1398373504","hostname":"1398373504","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c2b::10a0"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":47,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1248447295"},{"id":"25238104","hostname":"25238104","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c27::1001"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1080688167","group":"0","index":41,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1785286633"},{"id":"-188953757","hostname":"-188953757","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":8.0,"fastDisk":true,"ipAddresses":["c2a::105d"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-989926828","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":9,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-167779330"},{"id":"-786835206","hostname":"-786835206","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c45::107b"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3020272","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-1938379550","group":"0","index":5,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1714276213"},{"id":"546243435","hostname":"546243435","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c27::1079"],"additionalIpAddresses":[],"owner":{"tenant":"103891066","application":"103891066","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1544803905","group":"0","index":21,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1377413432"},{"id":"-2136815803","hostname":"-2136815803","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":1.5,"fastDisk":true,"ipAddresses":["c2a::1005"],"additionalIpAddresses":[],"owner":{"tenant":"-989926828","application":"-1937557253","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"-989926828","group":"0","index":4,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-157661344"},{"id":"1722396485","hostname":"1722396485","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c45::100a"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1080688167","group":"0","index":40,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1041720612"},{"id":"1245065688","hostname":"1245065688","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c27::108c"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":40,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1377413432"},{"id":"921359109","hostname":"921359109","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":8.0,"minCpuCores":4.0,"fastDisk":true,"ipAddresses":["c2a::109a"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"-410956671","group":"0","index":20,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1229740191"},{"id":"-1414575011","hostname":"-1414575011","type":"tenant","minDiskAvailableGb":400.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":24.0,"fastDisk":true,"ipAddresses":["c45::10a5"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"987366342","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"987366342","group":"0","index":57,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-496292156"},{"id":"812248477","hostname":"812248477","type":"tenant","minDiskAvailableGb":50.0,"minMainMemoryAvailableGb":3.0,"minCpuCores":0.5,"fastDisk":true,"ipAddresses":["c40::10c2"],"additionalIpAddresses":[],"owner":{"tenant":"-1938379550","application":"3449687","instance":"1544803905"},"membership":{"clustertype":"admin","clusterid":"-1440395385","group":"0","index":0,"retired":false},"wantedVespaVersion":"7.72.36","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"1163357258"},{"id":"795426972","hostname":"795426972","type":"tenant","minDiskAvailableGb":800.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":12.0,"fastDisk":true,"ipAddresses":["c28::10b0"],"additionalIpAddresses":[],"owner":{"tenant":"1010990605","application":"1080688167","instance":"1544803905"},"membership":{"clustertype":"content","clusterid":"1080688167","group":"0","index":38,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"DOCKER_CONTAINER","parentHostname":"-1553982724"},{"id":"-157661344","hostname":"-157661344","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.77.34","c2a::1000"],"additionalIpAddresses":["c2a::1008","c2a::1007","c2a::1006","c2a::1005","c2a::1004","c2a::1003","c2a::1002","c2a::1001"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":11,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"1580793796","hostname":"1580793796","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["c40::1061","1.1.90.137"],"additionalIpAddresses":["c40::1068","c40::1069","c40::1062","c40::1063","c40::1064","c40::1065","c40::1066","c40::1067"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":3,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null},{"id":"-222846136","hostname":"-222846136","type":"host","minDiskAvailableGb":960.0,"minMainMemoryAvailableGb":64.0,"minCpuCores":64.0,"fastDisk":true,"ipAddresses":["1.1.91.228","c45::107f"],"additionalIpAddresses":["c45::1084","c45::1083","c45::1082","c45::1081","c45::1080","c45::1087","c45::1086","c45::1085"],"owner":{"tenant":"-1802291313","application":"1843333355","instance":"1544803905"},"membership":{"clustertype":"container","clusterid":"1843333355","group":"0","index":7,"retired":false},"wantedVespaVersion":"7.73.13","state":"active","environment":"BARE_METAL","parentHostname":null}]}
\ No newline at end of file diff --git a/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp b/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp index f741002ea5e..11a6839ca59 100644 --- a/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp +++ b/searchlib/src/vespa/searchlib/tensor/dense_tensor_store.cpp @@ -13,6 +13,7 @@ using vespalib::tensor::Tensor; using vespalib::tensor::DenseTensorView; using vespalib::tensor::MutableDenseTensorView; using vespalib::eval::ValueType; +using CellType = vespalib::eval::ValueType::CellType; namespace search::tensor { @@ -21,12 +22,20 @@ namespace { constexpr size_t MIN_BUFFER_ARRAYS = 1024; constexpr size_t DENSE_TENSOR_ALIGNMENT = 32; +size_t size_of(CellType type) { + switch (type) { + case CellType::DOUBLE: return sizeof(double); + case CellType::FLOAT: return sizeof(float); + } + abort(); +} + } DenseTensorStore::TensorSizeCalc::TensorSizeCalc(const ValueType &type) : _numBoundCells(1u), _numUnboundDims(0u), - _cellSize(sizeof(double)) + _cellSize(size_of(type.cell_type())) { for (const auto & dim : type.dimensions()) { if (dim.is_bound()) { @@ -237,6 +246,7 @@ checkMatchingType(const ValueType &lhs, const ValueType &rhs, size_t numCells) checkNumCells *= rhsItr->size; ++rhsItr; } + assert(lhs.cell_type() == rhs.cell_type()); assert(numCells == checkNumCells); assert(rhsItr == rhsItrEnd); } diff --git a/vespajlib/src/main/java/com/yahoo/tensor/TensorType.java b/vespajlib/src/main/java/com/yahoo/tensor/TensorType.java index 8e566fac0b6..9869f1e908c 100644 --- a/vespajlib/src/main/java/com/yahoo/tensor/TensorType.java +++ b/vespajlib/src/main/java/com/yahoo/tensor/TensorType.java @@ -168,7 +168,11 @@ public class TensorType { @Override public String toString() { - return "tensor(" + dimensions.stream().map(Dimension::toString).collect(Collectors.joining(",")) + ")"; + if ((rank() == 0) || (valueType == Value.DOUBLE)) { + return "tensor(" + dimensions.stream().map(Dimension::toString).collect(Collectors.joining(",")) + ")"; + } else { + return "tensor<" + valueType + ">(" + dimensions.stream().map(Dimension::toString).collect(Collectors.joining(",")) + ")"; + } } @Override @@ -177,6 +181,7 @@ public class TensorType { if (o == null || getClass() != o.getClass()) return false; TensorType other = (TensorType)o; + if ( (this.rank() == 0) && (other.rank() == 0)) return true; if ( this.valueType != other.valueType) return false; if ( ! this.dimensions.equals(other.dimensions)) return false; return true; diff --git a/zkfacade/CMakeLists.txt b/zkfacade/CMakeLists.txt index cf02f163e67..52eae58e108 100644 --- a/zkfacade/CMakeLists.txt +++ b/zkfacade/CMakeLists.txt @@ -2,7 +2,7 @@ install_fat_java_artifact(zkfacade) install_fat_java_artifact(zkctl) -vespa_install_script(src/main/sh/zkcat vespa-zkcat bin) -vespa_install_script(src/main/sh/zkcli vespa-zkcli bin) -vespa_install_script(src/main/sh/zkctl vespa-zkctl bin) -vespa_install_script(src/main/sh/zkls vespa-zkls bin) +vespa_install_script(src/main/sh/vespa-zkcat vespa-zkcat bin) +vespa_install_script(src/main/sh/vespa-zkcli vespa-zkcli bin) +vespa_install_script(src/main/sh/vespa-zkctl vespa-zkctl bin) +vespa_install_script(src/main/sh/vespa-zkls vespa-zkls bin) diff --git a/zkfacade/src/main/sh/zkcat b/zkfacade/src/main/sh/vespa-zkcat index 675aeabbcfb..ba8924b456f 100755 --- a/zkfacade/src/main/sh/zkcat +++ b/zkfacade/src/main/sh/vespa-zkcat @@ -70,4 +70,4 @@ findhost # END environment bootstrap section -$VESPA_HOME/bin/zkctl get $@ +$VESPA_HOME/bin/vespa-zkctl get $@ diff --git a/zkfacade/src/main/sh/zkcli b/zkfacade/src/main/sh/vespa-zkcli index cbc356ae9e1..318836ec574 100755 --- a/zkfacade/src/main/sh/zkcli +++ b/zkfacade/src/main/sh/vespa-zkcli @@ -70,7 +70,25 @@ findhost # END environment bootstrap section -sudo -u ${VESPA_USER} java \ +usage() { + echo "Run Zookeeper command-line client" + echo "The following options are recognized:" + echo "" + + echo "-h|-help) print this help text" + echo "-nosudo do not use sudo when running command" +} + +sudo="sudo -u ${VESPA_USER}" +while [ $# -gt 0 ]; do + case $1 in + -h|-help) usage; exit 0;; + -nosudo) shift; sudo="" ;; + *) echo "Unrecognized option '$1'" >&2; exit 1;; + esac +done + +$sudo java \ -cp $VESPA_HOME/lib/jars/zkctl-jar-with-dependencies.jar \ -Dlog4j.configuration=file:$VESPA_HOME/etc/log4j-vespa.properties \ org.apache.zookeeper.ZooKeeperMain "$@" diff --git a/zkfacade/src/main/sh/zkctl b/zkfacade/src/main/sh/vespa-zkctl index 721becf8c79..43112abd925 100755 --- a/zkfacade/src/main/sh/zkctl +++ b/zkfacade/src/main/sh/vespa-zkctl @@ -71,4 +71,4 @@ findhost # END environment bootstrap section # Get rid of the interactive zkcli prompt by running it in a subshell -(echo "$@" | $VESPA_HOME/bin/zkcli) +(echo "$@" | $VESPA_HOME/bin/vespa-zkcli) diff --git a/zkfacade/src/main/sh/zkls b/zkfacade/src/main/sh/vespa-zkls index c75f63efbde..5ab1204cea8 100755 --- a/zkfacade/src/main/sh/zkls +++ b/zkfacade/src/main/sh/vespa-zkls @@ -70,4 +70,4 @@ findhost # END environment bootstrap section -$VESPA_HOME/bin/zkctl ls $@ +$VESPA_HOME/bin/vespa-zkctl ls $@ |