summaryrefslogtreecommitdiffstats
path: root/controller-api
diff options
context:
space:
mode:
authortoby <smorgrav@yahoo-inc.com>2017-09-25 13:23:42 +0200
committertoby <smorgrav@yahoo-inc.com>2017-10-10 13:37:59 +0200
commitcefd3ca7755b1b6ec04a21df4b8adeae02066673 (patch)
tree15aa1d617313fa1bce12809770645152d8c2e23d /controller-api
parent1867b6c057c100708819235e26080463b8001472 (diff)
Move calculateions to CostCluster and CostApplication classes
Diffstat (limited to 'controller-api')
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/Cost.java96
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostApplication.java55
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostCluster.java200
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostHardwareInfo.java39
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostResources.java57
-rw-r--r--controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/restapi/CostJsonModelAdapter.java8
6 files changed, 191 insertions, 264 deletions
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/Cost.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/Cost.java
index b22557dbcb0..6de1c5371bd 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/Cost.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/Cost.java
@@ -2,90 +2,110 @@
package com.yahoo.vespa.hosted.controller.api.integration.cost;
import com.yahoo.config.provision.ApplicationId;
-import com.yahoo.config.provision.Environment;
-import com.yahoo.config.provision.RegionName;
import com.yahoo.config.provision.Zone;
import com.yahoo.vespa.hosted.controller.common.NotFoundCheckedException;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
- * Cost domain model.
- *
+ * Calculate cost, hardware utilization and waste for applications.
+ * <p>
* Cost refers to the total cost ownership aka TCO.
- *
+ * <p>
* We define a target utilization for each cluster in an application and compares this
* to the actual utilization to get a number for ideal cost (if ideally scaled) and waste.
- *
+ * <p>
* The target utilization is defined with the following in mind:
- * 1 Application stats to see contention on CPU above 80%
- * 2 It is scaled for a 50/50 load balancing between two zones (thus must be able to serve the other zone)
- * 3 Peaks are 2x average wrt CPU
- * 4 Memory contention is rising when over 80%
+ * 1 Application stats to see contention on CPU above 80%
+ * 2 It is scaled for a 50/50 load balancing between two zones (thus must be able to serve the other zone)
+ * 3 Peaks are 2x average wrt CPU
+ * 4 Memory contention is rising when over 80%
*
* @author smorgrav
*/
public interface Cost {
/**
- * Collect all information and format it as a CSV blob for download.
- *
- * @return A String with comma separated values. Can be big!
- */
- default String getCsvForLocalAnalysis() {
- return null;
- }
-
- /**
* Get application costs for all applications across all zones
*
- * @return A list of all application costs
+ * @return A list of all application costs in all zones
*/
default List<CostApplication> getApplicationCost() {
- return null;
+ List<CostApplication> costApplications = new ArrayList<>();
+ getApplications().forEach((zone, list) -> {
+ list.forEach(app -> {
+ try {
+ costApplications.add(getApplicationCost(zone, app));
+ } catch (NotFoundCheckedException e) {
+ // Application removed after fetched in getApplications ?
+ // TODO Log
+ }
+ });
+
+ });
+
+ return costApplications;
}
/**
- * Get application costs for a given application instance in a given zone.
- *
- * Default implementation calls out to @see getApplicationCost
- *
- * @param env Environment like test, dev, perf, staging or prod
- * @param region Region name like us-east-1
- * @param app ApplicationId like tenant:application:instance
+ * Get application costs for a specific application deployement
*
- * @return A list of applications in given zone
+ * @param zone The zone - the combination of a environment and region e.g 'test.us-east-1'
+ * @param app ApplicationId e.g tenant:application:instance
+ * @return A list of applications cost in given zone
*/
- default CostApplication getApplicationCost(Environment env, RegionName region, ApplicationId app)
- throws NotFoundCheckedException {
- return null;
+ default CostApplication getApplicationCost(Zone zone, ApplicationId app)
+ throws NotFoundCheckedException {
+
+ Map<String, CostClusterInfo> info = getClusterInfo(zone, app);
+ Map<String, CostResources> util = getClusterUtilization(zone, app);
+ CostResources target = getTargetUtilization(zone, app);
+
+ Map<String, CostCluster> costClusters = new HashMap<>();
+ for (String clusterId : util.keySet()) {
+ costClusters.put(clusterId, new CostCluster(info.get(clusterId), util.get(clusterId), target));
+ }
+
+ return new CostApplication(zone, app, costClusters);
}
/**
* Provides target utilization - default targets ARE XXX
*
+ * @param zone The zone - the combination of a environment and region e.g 'test.us-east-1'
+ * @param app ApplicationId e.g tenant:application:instance
* @return Target utilization
*/
- default CostHardwareInfo getUsageTarget(Environment env, RegionName region, ApplicationId app) {
- return new CostHardwareInfo(0.8, 0.3, 0.4, 0.3);
+ default CostResources getTargetUtilization(Zone zone, ApplicationId app) {
+ return new CostResources(0.8, 0.3, 0.4, 0.3);
}
+ /**
+ * @return zone->app for all known zones and applications
+ */
+ Map<Zone, List<ApplicationId>> getApplications();
/**
* Provides information about the clusters in the application like
* what hardware that it is using, the TCO for the hardware and number of hosts.
*
+ * @param zone The zone - the combination of a environment and region e.g 'test.us-east-1'
+ * @param app ApplicationId e.g tenant:application:instance
* @return Map between clusterid -> costclusterinfo
*/
Map<String, CostClusterInfo> getClusterInfo(Zone zone, ApplicationId app);
/**
- * Provides measurements as absolute numbers of hardware resources.
- *
+ * Provides ratio of available hardware used.
+ * <p>
* Used to calculate the utilization of the hardware.
*
- * @return Map between clusterid -> costhardwareinfo
+ * @param zone The zone - the combination of a environment and region e.g 'test.us-east-1'
+ * @param app ApplicationId e.g tenant:application:instance
+ * @return Map between clusterid -> resource utilization
*/
- Map<String, CostHardwareInfo> getUsageMetrics(Zone zone, ApplicationId app);
+ Map<String, CostResources> getClusterUtilization(Zone zone, ApplicationId app);
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostApplication.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostApplication.java
index f41a3d7e183..7e683969ccb 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostApplication.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostApplication.java
@@ -1,67 +1,64 @@
// 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.integration.cost;
+import com.yahoo.config.provision.ApplicationId;
import com.yahoo.config.provision.Zone;
import java.util.HashMap;
import java.util.Map;
/**
- * Cost data model for an application instance. I.e one running vespa application in one zone.
+ * Calcalute cost for for an application instance. I.e one running vespa application in one zone.
*
* @author smorgrav
*/
-// TODO: Make the Application own this and rename to Cost
-// TODO: Enforce constraints
-// TODO: Remove application id elements
public class CostApplication {
- /** This contains environment.region */
private final Zone zone;
-
- private final String tenant;
-
- // This must contain applicationName.instanceName. TODO: Fix
- private final String app;
-
- private final int tco;
+ private final ApplicationId appId;
private final double utilization;
private final double waste;
- private final Map<String, CostCluster> cluster;
+ private final double tco;
+ private final Map<String, CostCluster> clusters;
- public CostApplication(Zone zone, String tenant, String app, int tco, float utilization, float waste,
- Map<String, CostCluster> clusterCost) {
- if (utilization < 0) throw new IllegalArgumentException("Utilization cannot be negative");
+ public CostApplication(Zone zone, ApplicationId appId, Map<String, CostCluster> clusterCosts) {
this.zone = zone;
- this.tenant = tenant;
- this.app = app;
- this.tco = tco;
- this.utilization = utilization;
+ this.appId = appId;
+ clusters = new HashMap<>(clusterCosts);
+
+ double tco = 0;
+ double util = 0;
+ double waste = 0;
+
+ for (CostCluster costCluster : clusterCosts.values()) {
+ tco += costCluster.getTco();
+ waste += costCluster.getWaste();
+ int nodesInCluster = costCluster.getClusterInfo().getHostnames().size();
+ util = Math.max(util, nodesInCluster*costCluster.getResultUtilization().getMaxUtilization());
+ }
+
+ this.utilization = util;
this.waste = waste;
- cluster = new HashMap<>(clusterCost);
+ this.tco = tco;
}
public Zone getZone() {
return zone;
}
- public String getApp() {
- return app;
+ public ApplicationId getAppId() {
+ return appId;
}
public Map<String, CostCluster> getCluster() {
- return cluster;
+ return clusters;
}
- public int getTco() {
+ public double getTco() {
return tco;
}
- public String getTenant() {
- return tenant;
- }
-
public double getUtilization() {
return utilization;
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostCluster.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostCluster.java
index e9920f36bca..37bf62e5b9e 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostCluster.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostCluster.java
@@ -1,181 +1,73 @@
// 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.integration.cost;
-import java.util.List;
-
/**
- * Cost data model for a cluster. I.e one cluster within one Vespa application in one zone.
+ * Calculate tco, waste and result utilization
+ * for one cluster within one Vespa application in one zone.
*
* @author smorgrav
*/
-// TODO: Make immutable
-// TODO: Enforce constraints
-// TODO: Document content
public class CostCluster {
-
- private int count;
- private String resource;
- private double utilization;
- private int tco;
- private String flavor;
- private int waste;
- private String type;
- private double utilMem;
- private double utilCpu;
- private double utilDisk;
- private double utilDiskBusy;
- private double usageMem;
- private double usageCpu;
- private double usageDisk;
- private double usageDiskBusy;
- private List<String> hostnames;
-
- /** Create an empty (invalid) cluster cost */
- public CostCluster() {}
-
- public int getCount() {
- return count;
- }
-
- public void setCount(int count) {
- this.count = count;
- }
-
- public String getFlavor() {
- return flavor;
- }
-
- public void setFlavor(String flavor) {
- this.flavor = flavor;
- }
-
- public List<String> getHostnames() {
- return hostnames;
- }
-
- public void setHostnames(List<String> hostnames) {
- this.hostnames = hostnames;
- }
-
- public String getResource() {
- return resource;
- }
-
- public void setResource(String resource) {
- this.resource = resource;
- }
-
- public int getTco() {
+ private final double tco;
+ private final double waste;
+ private final CostClusterInfo clusterInfo;
+ private final CostResources systemUtilization;
+ private final CostResources targetUtilization;
+ private final CostResources resultUtilization;
+
+ /**
+ * @param clusterInfo Value object with cluster info e.g. hardware tco
+ * @param systemUtilization Utilization of system resources (in percentage)
+ * @param targetUtilization Target utilization (usually < 1.0)
+ */
+ public CostCluster(CostClusterInfo clusterInfo,
+ CostResources systemUtilization,
+ CostResources targetUtilization) {
+
+ this.clusterInfo = clusterInfo;
+ this.systemUtilization = systemUtilization;
+ this.targetUtilization = targetUtilization;
+ this.resultUtilization = calculateResultUtilization(systemUtilization, targetUtilization);
+
+ this.tco = clusterInfo.getFlavor().cost() * Math.min(1, this.resultUtilization.getMaxUtilization());
+ this.waste = clusterInfo.getFlavor().cost() - tco;
+ }
+
+ public double getTco() {
return tco;
}
- public void setTco(int tco) {
- this.tco = tco;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- public double getUtilization() {
- return utilization;
- }
-
- public void setUtilization(float utilization) {
- validateUtilRatio(utilization);
- this.utilization = utilization;
- }
-
- public int getWaste() {
+ public double getWaste() {
return waste;
}
- public void setWaste(int waste) {
- this.waste = waste;
- }
-
- public double getUsageCpu() {
- return usageCpu;
- }
-
- public void setUsageCpu(float usageCpu) {
- validateUsageRatio(usageCpu);
- this.usageCpu = usageCpu;
- }
-
- public double getUsageDisk() {
- return usageDisk;
- }
-
- public void setUsageDisk(float usageDisk) {
- validateUsageRatio(usageDisk);
- this.usageDisk = usageDisk;
- }
-
- public double getUsageMem() {
- return usageMem;
- }
-
- public void setUsageMem(float usageMem) {
- validateUsageRatio(usageMem);
- this.usageMem = usageMem;
+ public CostClusterInfo getClusterInfo() {
+ return clusterInfo;
}
- public double getUtilCpu() {
- return utilCpu;
+ public CostResources getSystemUtilization() {
+ return systemUtilization;
}
- public void setUtilCpu(float utilCpu) {
- validateUtilRatio(utilCpu);
- this.utilCpu = utilCpu;
+ public CostResources getTargetUtilization() {
+ return targetUtilization;
}
- public double getUtilDisk() {
- return utilDisk;
+ public CostResources getResultUtilization() {
+ return resultUtilization;
}
- public void setUtilDisk(float utilDisk) {
- validateUtilRatio(utilDisk);
- this.utilDisk = utilDisk;
- }
-
- public double getUtilMem() {
- return utilMem;
- }
-
- public void setUtilMem(float utilMem) {
- validateUsageRatio(utilMem);
- this.utilMem = utilMem;
- }
-
- public double getUsageDiskBusy() {
- return usageDiskBusy;
- }
-
- public void setUsageDiskBusy(float usageDiskBusy) {
- validateUsageRatio(usageDiskBusy);
- this.usageDiskBusy = usageDiskBusy;
- }
-
- public double getUtilDiskBusy() {
- return utilDiskBusy;
- }
-
- public void setUtilDiskBusy(float utilDiskBusy) {
- validateUtilRatio(utilDiskBusy);
- this.utilDiskBusy = utilDiskBusy;
- }
+ static CostResources calculateResultUtilization(CostResources system, CostResources target) {
+ double cpu = system.getCpu()/target.getCpu();
+ double mem = system.getMemory()/target.getMemory();
+ double disk = system.getDisk()/target.getDisk();
+ double diskbusy = system.getDiskBusy()/target.getDiskBusy();
- private void validateUsageRatio(float ratio) {
- if (ratio < 0) throw new IllegalArgumentException("Usage cannot be negative");
- if (ratio > 1) throw new IllegalArgumentException("Usage exceed 1 (using more than it has available)");
+ return new CostResources(mem, cpu, disk, diskbusy);
}
- private void validateUtilRatio(float ratio) {
- if (ratio < 0) throw new IllegalArgumentException("Utilization cannot be negative");
+ static double ration(double a, double b) {
+ if (b == 0) return 1;
+ return a/b;
}
}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostHardwareInfo.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostHardwareInfo.java
deleted file mode 100644
index 1035c081376..00000000000
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostHardwareInfo.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.yahoo.vespa.hosted.controller.api.integration.cost;// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
-
-/**
- * Value object for hardware resources.
- *
- * Can be for actual readings or target numbers.
- *
- * @author smorgrav
- */
-public class CostHardwareInfo {
-
- private final double memoryGb;
- private final double cpuCores;
- private final double diskGb;
- private final double diskBusyPercentage;
-
- public CostHardwareInfo(double memoryGb, double cpuCores, double diskGb, double diskBusyPercentage) {
- this.memoryGb = memoryGb;
- this.cpuCores = cpuCores;
- this.diskGb = diskGb;
- this.diskBusyPercentage = diskBusyPercentage;
- }
-
- public double getMemoryGb() {
- return memoryGb;
- }
-
- public double getCpuCores() {
- return cpuCores;
- }
-
- public double getDiskGb() {
- return diskGb;
- }
-
- public double getDiskBusyPercentage() {
- return diskBusyPercentage;
- }
-}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostResources.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostResources.java
new file mode 100644
index 00000000000..44ce5098e9f
--- /dev/null
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/CostResources.java
@@ -0,0 +1,57 @@
+package com.yahoo.vespa.hosted.controller.api.integration.cost;// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
+
+/**
+ * Value object for system resources as ratios of available resources.
+ *
+ * Can be for actual readings or target numbers.
+ *
+ * @author smorgrav
+ */
+public class CostResources {
+
+ private final double memory;
+ private final double cpu;
+ private final double disk;
+ private final double diskBusy;
+ private final double maxUtilization;
+
+ public CostResources(double memory, double cpu, double disk, double diskBusy) {
+ this.memory = memory;
+ this.cpu = cpu;
+ this.disk = disk;
+ this.diskBusy = diskBusy;
+
+ double maxUtil = Math.max(cpu, disk);
+ maxUtil = Math.max(maxUtil, memory);
+ this.maxUtilization = Math.max(maxUtil, diskBusy);
+ }
+
+ public double getMaxUtilization() {
+ return maxUtilization;
+ }
+
+ public double getMemory() {
+ return memory;
+ }
+
+ public double getCpu() {
+ return cpu;
+ }
+
+ public double getDisk() {
+ return disk;
+ }
+
+ public double getDiskBusy() {
+ return diskBusy;
+ }
+
+ private void validateUsageRatio(float ratio) {
+ if (ratio < 0) throw new IllegalArgumentException("Usage cannot be negative");
+ if (ratio > 1) throw new IllegalArgumentException("Usage exceed 1 (using more than it has available)");
+ }
+
+ private void validateUtilRatio(float ratio) {
+ if (ratio < 0) throw new IllegalArgumentException("Utilization cannot be negative");
+ }
+}
diff --git a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/restapi/CostJsonModelAdapter.java b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/restapi/CostJsonModelAdapter.java
index f02f8582c52..2e963106129 100644
--- a/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/restapi/CostJsonModelAdapter.java
+++ b/controller-api/src/main/java/com/yahoo/vespa/hosted/controller/api/integration/cost/restapi/CostJsonModelAdapter.java
@@ -19,9 +19,9 @@ public class CostJsonModelAdapter {
public static CostJsonModel.Application toJsonModel(CostApplication appCost) {
CostJsonModel.Application app = new CostJsonModel.Application();
app.zone = appCost.getZone().toString();
- app.tenant = appCost.getTenant();
- app.app = appCost.getApp();
- app.tco = appCost.getTco();
+ app.tenant = appCost.getAppId().tenant().toString();
+ app.app = appCost.getAppId().application().toString();
+ app.tco = (int)appCost.getTco();
app.utilization = appCost.getUtilization();
app.waste = appCost.getWaste();
app.cluster = new HashMap<>();
@@ -37,7 +37,7 @@ public class CostJsonModelAdapter {
object.setString("zone", appCost.getZone().toString());
object.setString("tenant", appCost.getTenant());
object.setString("app", appCost.getApp());
- object.setLong("tco", appCost.getTco());
+ object.setLong("tco", (long)appCost.getTco());
object.setDouble("utilization", appCost.getUtilization());
object.setDouble("waste", appCost.getWaste());
Cursor clustersObject = object.setObject("cluster");