summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorØyvind Grønnesby <oyving@yahooinc.com>2023-03-08 15:04:28 +0100
committerGitHub <noreply@github.com>2023-03-08 15:04:28 +0100
commit2134efb6eb0f3988571e773f5fe996ec3c3e47ab (patch)
treeeea17153b99b95678d2b5224c77a4bc51412224b
parentb180fdfea329bc7e6e09cbcd1fefe67dc2f22202 (diff)
parent3c325c2ee25bc1d39190dfe7ee5b43eadc6225c0 (diff)
Merge pull request #26173 from vespa-engine/ogronnesby/billing-dimensions
New billing dimensions
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Bill.java13
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistryMock.java3
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/CostInfo.java8
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MeteringData.java42
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java12
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java59
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceUsage.java9
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainer.java52
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java9
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java2
-rw-r--r--controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/MeteringResponse.java8
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java30
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java2
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/billing-all-tenants.json9
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/line-item-list.json3
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view.json6
-rw-r--r--controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/controller/ControllerApiTest.java11
17 files changed, 140 insertions, 138 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Bill.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Bill.java
index 01488711f59..854413a4adb 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Bill.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/Bill.java
@@ -189,6 +189,7 @@ public class Bill {
private BigDecimal memoryCost;
private BigDecimal diskCost;
private NodeResources.Architecture architecture;
+ private int majorVersion;
public LineItem(String id, String description, BigDecimal amount, String plan, String agent, ZonedDateTime addedAt) {
this.id = id;
@@ -200,7 +201,7 @@ public class Bill {
}
public LineItem(String id, String description, BigDecimal amount, String plan, String agent, ZonedDateTime addedAt, ZonedDateTime startedAt, ZonedDateTime endedAt, ApplicationId applicationId, ZoneId zoneId,
- BigDecimal cpuHours, BigDecimal memoryHours, BigDecimal diskHours, BigDecimal cpuCost, BigDecimal memoryCost, BigDecimal diskCost, NodeResources.Architecture architecture) {
+ BigDecimal cpuHours, BigDecimal memoryHours, BigDecimal diskHours, BigDecimal cpuCost, BigDecimal memoryCost, BigDecimal diskCost, NodeResources.Architecture architecture, int majorVersion) {
this(id, description, amount, plan, agent, addedAt);
this.startedAt = startedAt;
this.endedAt = endedAt;
@@ -217,6 +218,7 @@ public class Bill {
this.memoryCost = memoryCost;
this.diskCost = diskCost;
this.architecture = architecture;
+ this.majorVersion = majorVersion;
}
/** The opaque ID of this */
@@ -297,6 +299,10 @@ public class Bill {
return Optional.ofNullable(architecture);
}
+ public int getMajorVersion() {
+ return majorVersion;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
@@ -311,12 +317,13 @@ public class Bill {
startedAt.equals(lineItem.startedAt) &&
endedAt.equals(lineItem.endedAt) &&
applicationId.equals(lineItem.applicationId) &&
- zoneId.equals(lineItem.zoneId);
+ zoneId.equals(lineItem.zoneId) &&
+ majorVersion == lineItem.majorVersion;
}
@Override
public int hashCode() {
- return Objects.hash(id, description, amount, plan, agent, addedAt, startedAt, endedAt, applicationId, zoneId);
+ return Objects.hash(id, description, amount, plan, agent, addedAt, startedAt, endedAt, applicationId, zoneId, majorVersion);
}
@Override
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistryMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistryMock.java
index 723ffa383eb..ecce9b7ccfa 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistryMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/billing/PlanRegistryMock.java
@@ -124,7 +124,8 @@ public class PlanRegistryMock implements PlanRegistry {
cpuCost,
memCost,
dgbCost,
- usage.getArchitecture()
+ usage.getArchitecture(),
+ usage.getMajorVersion()
);
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/CostInfo.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/CostInfo.java
index c756100e563..571a7b5a58e 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/CostInfo.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/CostInfo.java
@@ -21,11 +21,12 @@ public class CostInfo {
private final BigDecimal memoryCost;
private final BigDecimal diskCost;
private final NodeResources.Architecture architecture;
+ private final int majorVersion;
public CostInfo(ApplicationId applicationId, ZoneId zoneId,
BigDecimal cpuHours, BigDecimal memoryHours, BigDecimal diskHours,
- BigDecimal cpuCost, BigDecimal memoryCost, BigDecimal diskCost, NodeResources.Architecture architecture) {
+ BigDecimal cpuCost, BigDecimal memoryCost, BigDecimal diskCost, NodeResources.Architecture architecture, int majorVersion) {
this.applicationId = applicationId;
this.zoneId = zoneId;
this.cpuHours = cpuHours;
@@ -35,6 +36,7 @@ public class CostInfo {
this.memoryCost = memoryCost;
this.diskCost = diskCost;
this.architecture = architecture;
+ this.majorVersion = majorVersion;
}
public ApplicationId getApplicationId() {
@@ -77,4 +79,8 @@ public class CostInfo {
return architecture;
}
+ public int getMajorVersion() {
+ return majorVersion;
+ }
+
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MeteringData.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MeteringData.java
deleted file mode 100644
index 59ea8440701..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/MeteringData.java
+++ /dev/null
@@ -1,42 +0,0 @@
-// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-package com.yahoo.vespa.hosted.controller.api.integration.resource;
-
-import com.yahoo.config.provision.ApplicationId;
-
-import java.util.List;
-import java.util.Map;
-
-/**
- * @author olaa
- */
-public class MeteringData {
-
- private final ResourceAllocation thisMonth;
- private final ResourceAllocation lastMonth;
- private final ResourceAllocation currentSnapshot;
- Map<ApplicationId, List<ResourceSnapshot>> snapshotHistory;
-
- public MeteringData(ResourceAllocation thisMonth, ResourceAllocation lastMonth, ResourceAllocation currentSnapshot, Map<ApplicationId, List<ResourceSnapshot>> snapshotHistory) {
- this.thisMonth = thisMonth;
- this.lastMonth = lastMonth;
- this.currentSnapshot = currentSnapshot;
- this.snapshotHistory = snapshotHistory;
- }
-
- public ResourceAllocation getThisMonth() {
- return thisMonth;
- }
-
- public ResourceAllocation getLastMonth() {
- return lastMonth;
- }
-
- public ResourceAllocation getCurrentSnapshot() {
- return currentSnapshot;
- }
-
- public Map<ApplicationId, List<ResourceSnapshot>> getSnapshotHistory() {
- return snapshotHistory;
- }
-
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java
index 542bdd52477..c1d210e990d 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceDatabaseClientMock.java
@@ -78,10 +78,11 @@ public class ResourceDatabaseClientMock implements ResourceDatabaseClient {
a.getApplicationId(),
a.getZoneId(),
plan,
- a.getArchitecture(),
- BigDecimal.valueOf(a.getCpuCores()).multiply(d),
- BigDecimal.valueOf(a.getMemoryGb()).multiply(d),
- BigDecimal.valueOf(a.getDiskGb()).multiply(d)
+ a.resources().architecture(),
+ a.getVersion().getMajor(),
+ BigDecimal.valueOf(a.resources().vcpu()).multiply(d),
+ BigDecimal.valueOf(a.resources().memoryGb()).multiply(d),
+ BigDecimal.valueOf(a.resources().diskGb()).multiply(d)
);
})
.toList();
@@ -97,6 +98,7 @@ public class ResourceDatabaseClientMock implements ResourceDatabaseClient {
a.getZoneId(),
a.getPlan(),
a.getArchitecture(),
+ a.getMajorVersion(),
a.getCpuMillis().add(b.getCpuMillis()),
a.getMemoryMillis().add(b.getMemoryMillis()),
a.getDiskMillis().add(b.getDiskMillis())
@@ -112,7 +114,7 @@ public class ResourceDatabaseClientMock implements ResourceDatabaseClient {
.filter(snapshot -> snapshot.getTimestamp().isBefore(Instant.ofEpochMilli(end)))
.filter(snapshot -> snapshot.getApplicationId().tenant().equals(tenantName))
.collect(Collectors.groupingBy(
- usage -> Objects.hash(usage.getApplicationId(), usage.getZoneId(), tenantPlan.id().value())
+ usage -> Objects.hash(usage.getApplicationId(), usage.getZoneId(), tenantPlan.id().value(), usage.resources().architecture(), usage.getVersion().getMajor())
))
.values().stream()
.map(snapshots -> resourceUsageFromSnapshots(tenantPlan, snapshots))
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java
index 85ee23f4df0..4a849b831b7 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceSnapshot.java
@@ -1,6 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.resource;
+import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.zone.ZoneId;
@@ -20,19 +21,21 @@ import java.util.stream.Collectors;
public class ResourceSnapshot {
private final ApplicationId applicationId;
- private final ResourceAllocation resourceAllocation;
+ private final NodeResources resources;
private final Instant timestamp;
private final ZoneId zoneId;
+ private final Version version;
- public ResourceSnapshot(ApplicationId applicationId, double cpuCores, double memoryGb, double diskGb, NodeResources.Architecture architecture, Instant timestamp, ZoneId zoneId) {
+ public ResourceSnapshot(ApplicationId applicationId, NodeResources resources, Instant timestamp, ZoneId zoneId, Version version) {
this.applicationId = applicationId;
- this.resourceAllocation = new ResourceAllocation(cpuCores, memoryGb, diskGb, architecture);
+ this.resources = resources;
this.timestamp = timestamp;
this.zoneId = zoneId;
+ this.version = version;
}
- public static ResourceSnapshot from(ApplicationId applicationId, int nodes, double cpuCores, double memoryGb, double diskGb, NodeResources.Architecture architecture, Instant timestamp, ZoneId zoneId) {
- return new ResourceSnapshot(applicationId, cpuCores * nodes, memoryGb * nodes, diskGb * nodes, architecture, timestamp, zoneId);
+ public static ResourceSnapshot from(ApplicationId applicationId, int nodes, NodeResources resources, Instant timestamp, ZoneId zoneId) {
+ return new ResourceSnapshot(applicationId, resources.multipliedBy(nodes), timestamp, zoneId, Version.emptyVersion);
}
public static ResourceSnapshot from(List<Node> nodes, Instant timestamp, ZoneId zoneId) {
@@ -41,37 +44,26 @@ public class ResourceSnapshot {
.map(node -> node.owner().get())
.collect(Collectors.toSet());
+ Set<Version> versions = nodes.stream()
+ .map(Node::currentVersion)
+ .collect(Collectors.toSet());
+
if (applicationIds.size() != 1) throw new IllegalArgumentException("List of nodes can only represent one application");
+ if (versions.size() != 1) throw new IllegalArgumentException("List of nodes can only represent one version");
+
+ var resources = nodes.stream()
+ .map(Node::resources)
+ .reduce(NodeResources.zero(), NodeResources::add);
- return new ResourceSnapshot(
- applicationIds.iterator().next(),
- nodes.stream().map(Node::resources).mapToDouble(NodeResources::vcpu).sum(),
- nodes.stream().map(Node::resources).mapToDouble(NodeResources::memoryGb).sum(),
- nodes.stream().map(Node::resources).mapToDouble(NodeResources::diskGb).sum(),
- nodes.stream().map(node -> node.resources().architecture()).findFirst().orElse(NodeResources.Architecture.getDefault()),
- timestamp,
- zoneId
- );
+ return new ResourceSnapshot(applicationIds.iterator().next(), resources, timestamp, zoneId, versions.iterator().next());
}
public ApplicationId getApplicationId() {
return applicationId;
}
- public ResourceAllocation allocation() {
- return resourceAllocation;
- }
-
- public double getCpuCores() {
- return resourceAllocation.getCpuCores();
- }
-
- public double getMemoryGb() {
- return resourceAllocation.getMemoryGb();
- }
-
- public double getDiskGb() {
- return resourceAllocation.getDiskGb();
+ public NodeResources resources() {
+ return resources;
}
public Instant getTimestamp() {
@@ -82,8 +74,8 @@ public class ResourceSnapshot {
return zoneId;
}
- public NodeResources.Architecture getArchitecture() {
- return resourceAllocation.getArchitecture();
+ public Version getVersion() {
+ return version;
}
@Override
@@ -93,13 +85,14 @@ public class ResourceSnapshot {
ResourceSnapshot other = (ResourceSnapshot) o;
return this.applicationId.equals(other.applicationId) &&
- this.resourceAllocation.equals(other.resourceAllocation) &&
+ this.resources.equals(other.resources) &&
this.timestamp.equals(other.timestamp) &&
- this.zoneId.equals(other.zoneId);
+ this.zoneId.equals(other.zoneId) &&
+ this.version.equals(other.version);
}
@Override
public int hashCode(){
- return Objects.hash(applicationId, resourceAllocation, timestamp, zoneId);
+ return Objects.hash(applicationId, resources, timestamp, zoneId, version);
}
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceUsage.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceUsage.java
index 850ca040d03..bd1252d0879 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceUsage.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/resource/ResourceUsage.java
@@ -1,6 +1,7 @@
// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.controller.api.integration.resource;
+import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.zone.ZoneId;
@@ -17,9 +18,10 @@ public class ResourceUsage {
private final BigDecimal memoryMillis;
private final BigDecimal diskMillis;
private final NodeResources.Architecture architecture;
+ private final int majorVersion;
public ResourceUsage(ApplicationId applicationId, ZoneId zoneId, Plan plan, NodeResources.Architecture architecture,
- BigDecimal cpuMillis, BigDecimal memoryMillis, BigDecimal diskMillis) {
+ int majorVersion, BigDecimal cpuMillis, BigDecimal memoryMillis, BigDecimal diskMillis) {
this.applicationId = applicationId;
this.zoneId = zoneId;
this.cpuMillis = cpuMillis;
@@ -27,6 +29,7 @@ public class ResourceUsage {
this.diskMillis = diskMillis;
this.plan = plan;
this.architecture = architecture;
+ this.majorVersion = majorVersion;
}
public ApplicationId getApplicationId() {
@@ -53,6 +56,10 @@ public class ResourceUsage {
return plan;
}
+ public int getMajorVersion() {
+ return majorVersion;
+ }
+
public NodeResources.Architecture getArchitecture() {
return architecture;
}
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 d47b82a231f..26edd210075 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
@@ -120,7 +120,7 @@ public class ResourceMeterMaintainer extends ControllerMaintainer {
Map<ZoneId, Double> deploymentCosts = snapshotsByInstance.getOrDefault(instanceName, List.of()).stream()
.collect(Collectors.toUnmodifiableMap(
ResourceSnapshot::getZoneId,
- snapshot -> cost(snapshot.allocation(), systemName),
+ snapshot -> cost(snapshot.resources(), systemName),
Double::sum));
locked = locked.with(instanceName, i -> i.withDeploymentCosts(deploymentCosts));
updateCostMetrics(tenantAndApplication.instance(instanceName), deploymentCosts);
@@ -201,10 +201,11 @@ public class ResourceMeterMaintainer extends ControllerMaintainer {
// Grouping by ApplicationId -> Architecture -> ResourceSnapshot
.collect(Collectors.groupingBy(node ->
node.owner().get(),
- groupSnapshotsByArchitecture(zoneId)))
+ groupSnapshotsByArchitectureAndMajorVersion(zoneId)))
.values()
.stream()
- .flatMap(list -> list.values().stream())
+ .flatMap(byArch -> byArch.values().stream())
+ .flatMap(byMajor -> byMajor.values().stream())
.toList();
}
@@ -234,10 +235,14 @@ public class ResourceMeterMaintainer extends ControllerMaintainer {
}
private static double cost(ResourceAllocation allocation, SystemName systemName) {
+ var resources = new NodeResources(allocation.getCpuCores(), allocation.getMemoryGb(), allocation.getDiskGb(), 0);
+ return cost(resources, systemName);
+ }
+
+ private static double cost(NodeResources resources, SystemName systemName) {
// Divide cost by 3 in non-public zones to show approx. AWS equivalent cost
double costDivisor = systemName.isPublic() ? 1.0 : 3.0;
- double cost = new NodeResources(allocation.getCpuCores(), allocation.getMemoryGb(), allocation.getDiskGb(), 0).cost();
- return Math.round(cost * 100.0 / costDivisor) / 100.0;
+ return Math.round(resources.cost() * 100.0 / costDivisor) / 100.0;
}
private void updateMeteringMetrics(Collection<ResourceSnapshot> resourceSnapshots) {
@@ -245,14 +250,14 @@ public class ResourceMeterMaintainer extends ControllerMaintainer {
// total metered resource usage, for alerting on drastic changes
metric.set(METERING_TOTAL_REPORTED,
resourceSnapshots.stream()
- .mapToDouble(r -> r.getCpuCores() + r.getMemoryGb() + r.getDiskGb()).sum(),
+ .mapToDouble(r -> r.resources().vcpu() + r.resources().memoryGb() + r.resources().diskGb()).sum(),
metric.createContext(Collections.emptyMap()));
resourceSnapshots.forEach(snapshot -> {
var context = getMetricContext(snapshot);
- metric.set("metering.vcpu", snapshot.getCpuCores(), context);
- metric.set("metering.memoryGB", snapshot.getMemoryGb(), context);
- metric.set("metering.diskGB", snapshot.getDiskGb(), context);
+ metric.set("metering.vcpu", snapshot.resources().vcpu(), context);
+ metric.set("metering.memoryGB", snapshot.resources().memoryGb(), context);
+ metric.set("metering.diskGB", snapshot.resources().diskGb(), context);
});
}
@@ -276,23 +281,26 @@ public class ResourceMeterMaintainer extends ControllerMaintainer {
"tenant", snapshot.getApplicationId().tenant().value(),
"applicationId", snapshot.getApplicationId().toFullString(),
"zoneId", snapshot.getZoneId().value(),
- "architecture", snapshot.getArchitecture()
+ "architecture", snapshot.resources().architecture()
));
}
- private Collector<Node, ?, Map<NodeResources.Architecture, ResourceSnapshot>> groupSnapshotsByArchitecture(ZoneId zoneId) {
- return Collectors.collectingAndThen(
- Collectors.groupingBy(node -> node.resources().architecture()),
- convertNodeListToResourceSnapshot(zoneId)
- );
+ private Collector<Node, ?, Map<NodeResources.Architecture, Map<Integer, ResourceSnapshot>>> groupSnapshotsByArchitectureAndMajorVersion(ZoneId zoneId) {
+ return Collectors.groupingBy(
+ (Node node) -> node.resources().architecture(),
+ Collectors.collectingAndThen(
+ Collectors.groupingBy(
+ (Node node) -> node.currentVersion().getMajor(),
+ Collectors.toList()),
+ convertNodeListToResourceSnapshot(zoneId)));
}
- private Function<Map<NodeResources.Architecture, List<Node>>, Map<NodeResources.Architecture, ResourceSnapshot>> convertNodeListToResourceSnapshot(ZoneId zoneId) {
- return nodeMap -> nodeMap.entrySet()
- .stream()
- .collect(Collectors.toMap(
- Map.Entry::getKey,
- entry -> ResourceSnapshot.from(entry.getValue(), clock.instant(), zoneId))
- );
+ private Function<Map<Integer, List<Node>>, Map<Integer, ResourceSnapshot>> convertNodeListToResourceSnapshot(ZoneId zoneId) {
+ return nodesByMajor -> {
+ return nodesByMajor.entrySet().stream()
+ .collect(Collectors.toMap(
+ entry -> entry.getKey(),
+ entry -> ResourceSnapshot.from(entry.getValue(), clock.instant(), zoneId)));
+ };
}
}
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java
index 6e012ba619d..bc7dd4199c7 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandler.java
@@ -35,8 +35,6 @@ import java.io.IOException;
import java.math.BigDecimal;
import java.security.Principal;
import java.time.LocalDate;
-import java.time.ZoneId;
-import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Comparator;
@@ -44,7 +42,6 @@ import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
-import java.util.stream.Collectors;
/**
* @author andreer
@@ -421,6 +418,12 @@ public class BillingApiHandler extends ThreadedHttpRequestHandler {
cursor.setString("zone", zoneId.value())
);
+ lineItem.getArchitecture().ifPresent(architecture -> {
+ cursor.setString("architecture", architecture.name());
+ });
+
+ cursor.setLong("majorVersion", lineItem.getMajorVersion());
+
lineItem.getCpuHours().ifPresent(cpuHours ->
cursor.setString("cpuHours", cpuHours.toString())
);
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java
index 0ddaa409ef8..67dd172fd83 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2.java
@@ -311,6 +311,8 @@ public class BillingApiHandlerV2 extends RestApiRequestHandler<BillingApiHandler
slime.setString("description", item.description());
slime.setString("amount",item.amount().toString());
toSlime(slime.setObject("plan"), planRegistry.plan(item.plan()).orElseThrow(() -> new RuntimeException("No such plan: '" + item.plan() + "'")));
+ item.getArchitecture().ifPresent(arch -> slime.setString("architecture", arch.name()));
+ slime.setLong("majorVersion", item.getMajorVersion());
item.applicationId().ifPresent(appId -> {
slime.setString("application", appId.application().value());
diff --git a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/MeteringResponse.java b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/MeteringResponse.java
index 17461aafd02..7df216d6c9c 100644
--- a/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/MeteringResponse.java
+++ b/controller-server/src/main/java/com/yahoo/vespa/hosted/controller/restapi/controller/MeteringResponse.java
@@ -29,10 +29,10 @@ public class MeteringResponse extends SlimeJsonResponse {
object.setString("applicationId", snapshot.getApplicationId().toShortString());
object.setLong("timestamp", snapshot.getTimestamp().toEpochMilli());
object.setString("zoneId", snapshot.getZoneId().value());
- object.setDouble("cpu", snapshot.getCpuCores());
- object.setDouble("memory", snapshot.getMemoryGb());
- object.setDouble("disk", snapshot.getDiskGb());
- object.setString("architecture", snapshot.getArchitecture().name());
+ object.setDouble("cpu", snapshot.resources().vcpu());
+ object.setDouble("memory", snapshot.resources().memoryGb());
+ object.setDouble("disk", snapshot.resources().diskGb());
+ object.setString("architecture", snapshot.resources().architecture().name());
});
return slime;
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java
index 157abe17841..3034c93e593 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/maintenance/ResourceMeterMaintainerTest.java
@@ -61,18 +61,20 @@ public class ResourceMeterMaintainerTest {
.collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().cost().getAsDouble())));
List<ResourceSnapshot> resourceSnapshots = List.of(
- new ResourceSnapshot(app1, 12, 34, 56, NodeResources.Architecture.getDefault(), Instant.EPOCH, z1),
- new ResourceSnapshot(app1, 23, 45, 67, NodeResources.Architecture.getDefault(), Instant.EPOCH, z2),
- new ResourceSnapshot(app2, 34, 56, 78, NodeResources.Architecture.getDefault(), Instant.EPOCH, z1));
+ new ResourceSnapshot(app1, resources(12, 34, 56), Instant.EPOCH, z1, Version.emptyVersion),
+ new ResourceSnapshot(app1, resources(23, 45, 67), Instant.EPOCH, z2, Version.emptyVersion),
+ new ResourceSnapshot(app2, resources(34, 56, 78), Instant.EPOCH, z1, Version.emptyVersion));
+
maintainer.updateDeploymentCost(resourceSnapshots);
assertCost.accept(app1, Map.of(z1, 1.72, z2, 3.05));
assertCost.accept(app2, Map.of(z1, 4.39));
// Remove a region from app1 and add region to app2
resourceSnapshots = List.of(
- new ResourceSnapshot(app1, 23, 45, 67, NodeResources.Architecture.getDefault(), Instant.EPOCH, z2),
- new ResourceSnapshot(app2, 34, 56, 78, NodeResources.Architecture.getDefault(), Instant.EPOCH, z1),
- new ResourceSnapshot(app2, 45, 67, 89, NodeResources.Architecture.getDefault(), Instant.EPOCH, z2));
+ new ResourceSnapshot(app1, resources(23, 45, 67), Instant.EPOCH, z2, Version.emptyVersion),
+ new ResourceSnapshot(app2, resources(34, 56, 78), Instant.EPOCH, z1, Version.emptyVersion),
+ new ResourceSnapshot(app2, resources(45, 67, 89), Instant.EPOCH, z2, Version.emptyVersion));
+
maintainer.updateDeploymentCost(resourceSnapshots);
assertCost.accept(app1, Map.of(z2, 3.05));
assertCost.accept(app2, Map.of(z1, 4.39, z2, 5.72));
@@ -97,13 +99,13 @@ public class ResourceMeterMaintainerTest {
ResourceSnapshot app1 = consumedResources.stream().filter(snapshot -> snapshot.getApplicationId().equals(ApplicationId.from("tenant1", "app1", "default"))).findFirst().orElseThrow();
ResourceSnapshot app2 = consumedResources.stream().filter(snapshot -> snapshot.getApplicationId().equals(ApplicationId.from("tenant2", "app2", "default"))).findFirst().orElseThrow();
- assertEquals(24, app1.getCpuCores(), Double.MIN_VALUE);
- assertEquals(24, app1.getMemoryGb(), Double.MIN_VALUE);
- assertEquals(500, app1.getDiskGb(), Double.MIN_VALUE);
+ assertEquals(24, app1.resources().vcpu(), Double.MIN_VALUE);
+ assertEquals(24, app1.resources().memoryGb(), Double.MIN_VALUE);
+ assertEquals(500, app1.resources().diskGb(), Double.MIN_VALUE);
- assertEquals(40, app2.getCpuCores(), Double.MIN_VALUE);
- assertEquals(24, app2.getMemoryGb(), Double.MIN_VALUE);
- assertEquals(500, app2.getDiskGb(), Double.MIN_VALUE);
+ assertEquals(40, app2.resources().vcpu(), Double.MIN_VALUE);
+ assertEquals(24, app2.resources().memoryGb(), Double.MIN_VALUE);
+ assertEquals(500, app2.resources().diskGb(), Double.MIN_VALUE);
assertEquals(tester.clock().millis() / 1000, metrics.getMetric("metering_last_reported"));
assertEquals(2224.0d, (Double) metrics.getMetric("metering_total_reported"), Double.MIN_VALUE);
@@ -154,4 +156,8 @@ public class ResourceMeterMaintainerTest {
.build())
.toList();
}
+
+ private NodeResources resources(double cpu, double ram, double disk) {
+ return new NodeResources(cpu, ram, disk, 0, NodeResources.DiskSpeed.getDefault(), NodeResources.StorageType.getDefault(), NodeResources.Architecture.getDefault(), NodeResources.GpuResources.getDefault());
+ }
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java
index 857dcbac6fd..43271277ce9 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/BillingApiHandlerV2Test.java
@@ -103,7 +103,7 @@ public class BillingApiHandlerV2Test extends ControllerContainerCloudTest {
var singleRequest = request("/billing/v2/tenant/" + tenant + "/bill/id-1").roles(tenantReader);
tester.assertResponse(singleRequest, """
- {"id":"id-1","from":"2020-05-23","to":"2020-05-28","total":"123.00","status":"OPEN","statusHistory":[{"at":"2020-05-23T00:00:00Z","status":"OPEN"}],"items":[{"id":"some-id","description":"description","amount":"123.00","plan":{"id":"paid","name":"Paid Plan - for testing purposes"},"cpu":{},"memory":{},"disk":{}}]}""");
+ {"id":"id-1","from":"2020-05-23","to":"2020-05-28","total":"123.00","status":"OPEN","statusHistory":[{"at":"2020-05-23T00:00:00Z","status":"OPEN"}],"items":[{"id":"some-id","description":"description","amount":"123.00","plan":{"id":"paid","name":"Paid Plan - for testing purposes"},"majorVersion":0,"cpu":{},"memory":{},"disk":{}}]}""");
}
@Test
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/billing-all-tenants.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/billing-all-tenants.json
index e9b18a879b9..d761439667a 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/billing-all-tenants.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/billing-all-tenants.json
@@ -16,7 +16,8 @@
"description": "description",
"amount": "123.00",
"plan": "paid",
- "planName": "Plan with id: paid"
+ "planName": "Plan with id: paid",
+ "majorVersion": 0
}
]
},
@@ -27,7 +28,8 @@
"description": "support",
"amount": "42.00",
"plan": "some-plan",
- "planName": "Plan with id: some-plan"
+ "planName": "Plan with id: some-plan",
+ "majorVersion": 0
}
]
}
@@ -47,7 +49,8 @@
"description": "description",
"amount": "123.00",
"plan": "paid",
- "planName": "Plan with id: paid"
+ "planName": "Plan with id: paid",
+ "majorVersion": 0
}
]
},
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/line-item-list.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/line-item-list.json
index e8404b12dd8..fbfc5ce09ee 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/line-item-list.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/line-item-list.json
@@ -5,7 +5,8 @@
"description": "some description",
"amount": "123.45",
"plan": "some-plan",
- "planName": "Plan with id: some-plan"
+ "planName": "Plan with id: some-plan",
+ "majorVersion": 0
}
]
}
diff --git a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view.json b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view.json
index adb319a3642..4e255205e19 100644
--- a/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view.json
+++ b/controller-server/src/test/java/com/yahoo/vespa/hosted/controller/restapi/billing/responses/tenant-billing-view.json
@@ -12,7 +12,8 @@
"description": "description",
"amount": "123.00",
"plan": "paid",
- "planName": "Plan with id: paid"
+ "planName": "Plan with id: paid",
+ "majorVersion": 0
}
]
},
@@ -38,7 +39,8 @@
"description": "description",
"amount": "123.00",
"plan": "paid",
- "planName": "Plan with id: paid"
+ "planName": "Plan with id: paid",
+ "majorVersion": 0
}
]
}
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 e3a0684771c..ac7287b7e27 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
@@ -2,6 +2,7 @@
package com.yahoo.vespa.hosted.controller.restapi.controller;
import com.yahoo.application.container.handler.Request;
+import com.yahoo.component.Version;
import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.NodeResources;
import com.yahoo.config.provision.zone.ZoneId;
@@ -157,10 +158,12 @@ public class ControllerApiTest extends ControllerContainerTest {
ApplicationId applicationId = ApplicationId.from("tenant", "app", "instance");
Instant timestamp = Instant.ofEpochMilli(123456789);
ZoneId zoneId = ZoneId.defaultId();
- List<ResourceSnapshot> snapshots = List.of(
- new ResourceSnapshot(applicationId, 12, 48, 1200, NodeResources.Architecture.arm64, timestamp, zoneId),
- new ResourceSnapshot(applicationId, 24, 96, 2400, NodeResources.Architecture.x86_64, timestamp, zoneId)
- );
+ var resources = List.of(
+ new NodeResources(12, 48, 1200, 0, NodeResources.DiskSpeed.any, NodeResources.StorageType.any, NodeResources.Architecture.arm64),
+ new NodeResources(24, 96, 2400, 0, NodeResources.DiskSpeed.any, NodeResources.StorageType.any, NodeResources.Architecture.x86_64));
+
+ var snapshots = resources.stream().map(x -> new ResourceSnapshot(applicationId, x, timestamp, zoneId, Version.emptyVersion)).toList();
+
tester.controller().serviceRegistry().resourceDatabase().writeResourceSnapshots(snapshots);
tester.assertResponse(
operatorRequest("http://localhost:8080/controller/v1/metering/tenant/tenantName/month/2020-02", "", Request.Method.GET),