summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJon Bratseth <bratseth@gmail.com>2021-03-25 13:01:09 +0100
committerJon Bratseth <bratseth@gmail.com>2021-03-25 13:01:09 +0100
commitf97a835dea395f0d18256535c559bd66746cf04d (patch)
treee8601ab9c2366b3f06a92c329465eecf72e1ed26
parent748f6279d83e25183e9ed1053f81f4ea0204008b (diff)
Add stats to controller/v1 API
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Load.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepoStats.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepository.java2
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepoStatsData.java6
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java3
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/StatsResponse.java61
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/integration/NodeRepositoryMock.java15
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java16
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/root.json8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/stats.json64
-rw-r--r--node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java4
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/root.json17
-rw-r--r--node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/stats.json2
13 files changed, 189 insertions, 13 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Load.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Load.java
index a16d05a7310..d3fef13aa77 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Load.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/Load.java
@@ -24,4 +24,6 @@ public class Load {
return "load: cpu " + cpu + ", memory " + memory + ", disk " + disk;
}
+ public static Load zero() { return new Load(0, 0, 0); }
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepoStats.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepoStats.java
index 31e5730b6a6..5b5ac21b500 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepoStats.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/configserver/NodeRepoStats.java
@@ -10,7 +10,7 @@ public class NodeRepoStats {
private final Load load;
private final Load activeLoad;
- private List<ApplicationStats> applicationStats;
+ private final List<ApplicationStats> applicationStats;
public NodeRepoStats(Load load, Load activeLoad, List<ApplicationStats> applicationStats) {
this.load = load;
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 bbe982bd5fe..7d454b2134f 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
@@ -79,6 +79,8 @@ public interface NodeRepository {
void patchApplication(ZoneId zone, ApplicationId application,
double currentReadShare, double maxReadShare);
+ NodeRepoStats getStats(ZoneId zone);
+
Map<TenantName, URI> getArchiveUris(ZoneId zone);
void setArchiveUri(ZoneId zone, TenantName tenantName, URI archiveUri);
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepoStatsData.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepoStatsData.java
index d3a81201c78..a4b3174b442 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepoStatsData.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/noderepository/NodeRepoStatsData.java
@@ -22,12 +22,12 @@ public class NodeRepoStatsData {
@JsonProperty("activeLoad")
public LoadData activeLoad;
- @JsonProperty("applicationStats")
- public List<ApplicationStatsData> applicationStats;
+ @JsonProperty("applications")
+ public List<ApplicationStatsData> applications;
public NodeRepoStats toNodeRepoStats() {
return new NodeRepoStats(load.toLoad(), activeLoad.toLoad(),
- applicationStats.stream().map(stats -> stats.toApplicationStats()).collect(Collectors.toList()));
+ applications.stream().map(stats -> stats.toApplicationStats()).collect(Collectors.toList()));
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
index 10c7452a6af..98a9ade1b16 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiHandler.java
@@ -68,6 +68,7 @@ public class ControllerApiHandler extends AuditLoggingRequestHandler {
if (path.matches("/controller/v1/")) return root(request);
if (path.matches("/controller/v1/auditlog/")) return new AuditLogResponse(controller.auditLogger().readLog());
if (path.matches("/controller/v1/maintenance/")) return new JobsResponse(controller.jobControl());
+ if (path.matches("/controller/v1/stats")) return new StatsResponse(controller);
if (path.matches("/controller/v1/jobs/upgrader")) return new UpgraderResponse(maintenance.upgrader());
if (path.matches("/controller/v1/metering/tenant/{tenant}/month/{month}")) return new MeteringResponse(controller.serviceRegistry().meteringService(), path.get("tenant"), path.get("month"));
return notFound(path);
@@ -94,7 +95,7 @@ public class ControllerApiHandler extends AuditLoggingRequestHandler {
private HttpResponse notFound(Path path) { return ErrorResponse.notFoundError("Nothing at " + path); }
private HttpResponse root(HttpRequest request) {
- return new ResourceResponse(request, "auditlog", "jobs/upgrader", "maintenance");
+ return new ResourceResponse(request, "auditlog", "maintenance", "stats", "jobs/upgrader", "metering/tenant");
}
private HttpResponse configureUpgrader(HttpRequest request) {
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/StatsResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/StatsResponse.java
new file mode 100644
index 00000000000..e68026f2192
--- /dev/null
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/StatsResponse.java
@@ -0,0 +1,61 @@
+// Copyright Verizon Media. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+package com.yahoo.vespa.hosted.controller.restapi.controller;
+
+import com.yahoo.config.provision.zone.ZoneId;
+import com.yahoo.restapi.SlimeJsonResponse;
+import com.yahoo.slime.Cursor;
+import com.yahoo.slime.Slime;
+import com.yahoo.vespa.hosted.controller.Controller;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ApplicationStats;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Load;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepoStats;
+
+/**
+ * A response containing statistics about this controller and its zones.
+ *
+ * @author bratseth
+ */
+public class StatsResponse extends SlimeJsonResponse {
+
+ public StatsResponse(Controller controller) {
+ super(toSlime(controller));
+ }
+
+ private static Slime toSlime(Controller controller) {
+ try {
+ Slime slime = new Slime();
+ Cursor root = slime.setObject();
+ Cursor zonesArray = root.setArray("zones");
+ for (ZoneId zone : controller.zoneRegistry().zones().all().ids()) {
+ NodeRepoStats stats = controller.serviceRegistry().configServer().nodeRepository().getStats(zone);
+ if (stats.applicationStats().isEmpty()) continue; // skip empty zones
+ Cursor zoneObject = zonesArray.addObject();
+ zoneObject.setString("id", zone.toString());
+ toSlime(stats.load(), zoneObject.setObject("load"));
+ toSlime(stats.load(), zoneObject.setObject("activeLoad"));
+ Cursor applicationsArray = zoneObject.setArray("applications");
+ for (var applicationStats : stats.applicationStats())
+ toSlime(applicationStats, applicationsArray.addObject());
+ }
+ return slime;
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ throw e;
+ }
+ }
+
+ private static void toSlime(ApplicationStats stats, Cursor applicationObject) {
+ applicationObject.setString("id", stats.id().toFullString());
+ toSlime(stats.load(), applicationObject.setObject("load"));
+ applicationObject.setDouble("cost", stats.cost());
+ applicationObject.setDouble("unutilizedCost", stats.unutilizedCost());
+ }
+
+ private static void toSlime(Load load, Cursor loadObject) {
+ loadObject.setDouble("cpu", load.cpu());
+ loadObject.setDouble("memory", load.memory());
+ loadObject.setDouble("disk", load.disk());
+ }
+
+}
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 85e82bb3fcd..2eabc5dc21b 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
@@ -12,7 +12,10 @@ import com.yahoo.config.provision.TenantName;
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.Application;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.ApplicationStats;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Load;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.Node;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepoStats;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.NodeRepository;
import com.yahoo.vespa.hosted.controller.api.integration.configserver.TargetVersions;
import com.yahoo.vespa.hosted.controller.api.integration.noderepository.NodeList;
@@ -66,6 +69,18 @@ public class NodeRepositoryMock implements NodeRepository {
applications.get(zone).put(application.id(), application);
}
+ @Override
+ public NodeRepoStats getStats(ZoneId zone) {
+ List<ApplicationStats> applicationStats =
+ applications.containsKey(zone)
+ ? applications.get(zone).keySet().stream()
+ .map(id -> new ApplicationStats(id, Load.zero(), 0, 0))
+ .collect(Collectors.toList())
+ : List.of();
+
+ return new NodeRepoStats(Load.zero(), Load.zero(), applicationStats);
+ }
+
public Pair<Double, Double> getTrafficFraction(ApplicationId application, ZoneId zone) {
return trafficFractions.get(new DeploymentId(application, zone));
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java
index 5523ce8dd9f..8f6988dbc27 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java
@@ -8,8 +8,10 @@ import com.yahoo.container.jdisc.HttpRequest;
import com.yahoo.test.ManualClock;
import com.yahoo.vespa.flags.InMemoryFlagSource;
import com.yahoo.vespa.flags.PermanentFlags;
+import com.yahoo.vespa.hosted.controller.api.integration.configserver.Application;
import com.yahoo.vespa.hosted.controller.api.integration.resource.ResourceSnapshot;
import com.yahoo.vespa.hosted.controller.auditlog.AuditLogger;
+import com.yahoo.vespa.hosted.controller.integration.NodeRepositoryMock;
import com.yahoo.vespa.hosted.controller.restapi.ContainerTester;
import com.yahoo.vespa.hosted.controller.restapi.ControllerContainerTest;
import org.junit.Before;
@@ -50,6 +52,20 @@ public class ControllerApiTest extends ControllerContainerTest {
}
@Test
+ public void testStats() {
+ var mock = (NodeRepositoryMock)tester.controller().serviceRegistry().configServer().nodeRepository();
+ mock.putApplication(ZoneId.from("prod", "us-west-1"),
+ new Application(ApplicationId.fromFullString("t1.a1.i1"), List.of()));
+ mock.putApplication(ZoneId.from("prod", "us-west-1"),
+ new Application(ApplicationId.fromFullString("t2.a2.i2"), List.of()));
+ mock.putApplication(ZoneId.from("prod", "us-east-3"),
+ new Application(ApplicationId.fromFullString("t1.a1.i1"), List.of()));
+
+ tester.assertResponse(authenticatedRequest("http://localhost:8080/controller/v1/stats", "", Request.Method.GET),
+ new File("stats.json"));
+ }
+
+ @Test
public void testUpgraderApi() {
// Get current configuration
tester.assertResponse(authenticatedRequest("http://localhost:8080/controller/v1/jobs/upgrader", "", Request.Method.GET),
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/root.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/root.json
index 0ff5a8bf443..6d800db303c 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/root.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/root.json
@@ -4,10 +4,16 @@
"url": "http://localhost:8080/controller/v1/auditlog/"
},
{
+ "url": "http://localhost:8080/controller/v1/maintenance/"
+ },
+ {
+ "url": "http://localhost:8080/controller/v1/stats/"
+ },
+ {
"url": "http://localhost:8080/controller/v1/jobs/upgrader/"
},
{
- "url": "http://localhost:8080/controller/v1/maintenance/"
+ "url": "http://localhost:8080/controller/v1/metering/tenant/"
}
]
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/stats.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/stats.json
new file mode 100644
index 00000000000..1c9230798dd
--- /dev/null
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/responses/stats.json
@@ -0,0 +1,64 @@
+{
+ "zones": [
+ {
+ "id": "prod.us-east-3",
+ "load": {
+ "cpu": 0.0,
+ "memory": 0.0,
+ "disk": 0.0
+ },
+ "activeLoad": {
+ "cpu": 0.0,
+ "memory": 0.0,
+ "disk": 0.0
+ },
+ "applications": [
+ {
+ "id": "t1.a1.i1",
+ "load": {
+ "cpu": 0.0,
+ "memory": 0.0,
+ "disk": 0.0
+ },
+ "cost": 0.0,
+ "unutilizedCost": 0.0
+ }
+ ]
+ },
+ {
+ "id": "prod.us-west-1",
+ "load": {
+ "cpu": 0.0,
+ "memory": 0.0,
+ "disk": 0.0
+ },
+ "activeLoad": {
+ "cpu": 0.0,
+ "memory": 0.0,
+ "disk": 0.0
+ },
+ "applications": [
+ {
+ "id": "t1.a1.i1",
+ "load": {
+ "cpu": 0.0,
+ "memory": 0.0,
+ "disk": 0.0
+ },
+ "cost": 0.0,
+ "unutilizedCost": 0.0
+ },
+ {
+ "id": "t2.a2.i2",
+ "load": {
+ "cpu": 0.0,
+ "memory": 0.0,
+ "disk": 0.0
+ },
+ "cost": 0.0,
+ "unutilizedCost": 0.0
+ }
+ ]
+ }
+ ]
+} \ No newline at end of file
diff --git a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
index 750fe4f0538..9fd23d0d2c2 100644
--- a/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
+++ b/node-repository/src/main/java/com/yahoo/vespa/hosted/provision/restapi/NodesV2ApiHandler.java
@@ -112,7 +112,7 @@ public class NodesV2ApiHandler extends LoggingRequestHandler {
private HttpResponse handleGET(HttpRequest request) {
Path path = new Path(request.getUri());
String pathS = request.getUri().getPath();
- if (path.matches( "/nodes/v2")) return new ResourceResponse(request.getUri(), "state", "node", "command", "maintenance", "upgrade", "application", "archive", "locks");
+ if (path.matches( "/nodes/v2")) return new ResourceResponse(request.getUri(), "node", "state", "acl", "command", "archive", "locks", "maintenance", "upgrade", "capacity", "application", "stats");
if (path.matches( "/nodes/v2/node")) return new NodesResponse(ResponseType.nodeList, request, orchestrator, nodeRepository);
if (pathS.startsWith("/nodes/v2/node/")) return new NodesResponse(ResponseType.singleNode, request, orchestrator, nodeRepository);
if (path.matches( "/nodes/v2/state")) return new NodesResponse(ResponseType.stateList, request, orchestrator, nodeRepository);
@@ -462,7 +462,7 @@ public class NodesV2ApiHandler extends LoggingRequestHandler {
toSlime(stats.load(), root.setObject("load"));
toSlime(stats.load(), root.setObject("activeLoad"));
- Cursor applicationsObject = root.setArray("applicationStats");
+ Cursor applicationsObject = root.setArray("applications");
for (int i = 0; i <= 5; i++) {
if (i >= stats.applicationStats().size()) break;
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/root.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/root.json
index 91b513cd9bc..201cc47d66e 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/root.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/root.json
@@ -1,28 +1,37 @@
{
"resources": [
{
+ "url": "http://localhost:8080/nodes/v2/node/"
+ },
+ {
"url": "http://localhost:8080/nodes/v2/state/"
},
{
- "url": "http://localhost:8080/nodes/v2/node/"
+ "url": "http://localhost:8080/nodes/v2/acl/"
},
{
"url": "http://localhost:8080/nodes/v2/command/"
},
{
+ "url":"http://localhost:8080/nodes/v2/archive/"
+ },
+ {
+ "url":"http://localhost:8080/nodes/v2/locks/"
+ },
+ {
"url": "http://localhost:8080/nodes/v2/maintenance/"
},
{
"url":"http://localhost:8080/nodes/v2/upgrade/"
},
{
- "url":"http://localhost:8080/nodes/v2/application/"
+ "url":"http://localhost:8080/nodes/v2/capacity/"
},
{
- "url":"http://localhost:8080/nodes/v2/archive/"
+ "url":"http://localhost:8080/nodes/v2/application/"
},
{
- "url":"http://localhost:8080/nodes/v2/locks/"
+ "url":"http://localhost:8080/nodes/v2/stats/"
}
]
} \ No newline at end of file
diff --git a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/stats.json b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/stats.json
index c8e41078a7a..8867520fef6 100644
--- a/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/stats.json
+++ b/node-repository/src/test/java/com/yahoo/vespa/hosted/provision/restapi/responses/stats.json
@@ -9,7 +9,7 @@
"memory": 0.0,
"disk": 0.0
},
- "applicationStats": [
+ "applications": [
{
"id": "tenant3.application3.instance3",
"load": {