Skip to content

Commit c9eb8ce

Browse files
committed
Add logging and other minor changes for drs
1 parent 7877b43 commit c9eb8ce

10 files changed

Lines changed: 126 additions & 45 deletions

File tree

api/src/main/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithm.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -149,17 +149,17 @@ private Double getMetricValuePostMigration(Long clusterId, Ternary<Long, Long, L
149149
return getMetricValue(clusterId, used, free, actualTotal, null);
150150
}
151151

152-
private Double getImbalance(List<Double> metricList) {
152+
private static Double getImbalance(List<Double> metricList) {
153153
Double clusterMeanMetric = getClusterMeanMetric(metricList);
154154
Double clusterStandardDeviation = getClusterStandardDeviation(metricList, clusterMeanMetric);
155155
return clusterStandardDeviation / clusterMeanMetric;
156156
}
157157

158-
default String getClusterDrsMetric(long clusterId) {
158+
static String getClusterDrsMetric(long clusterId) {
159159
return ClusterDrsMetric.valueIn(clusterId);
160160
}
161161

162-
default Double getMetricValue(long clusterId, long used, long free, long total, Float skipThreshold) {
162+
static Double getMetricValue(long clusterId, long used, long free, long total, Float skipThreshold) {
163163
boolean useRatio = getDrsMetricUseRatio(clusterId);
164164
switch (getDrsMetricType(clusterId)) {
165165
case "free":
@@ -187,7 +187,7 @@ default Double getMetricValue(long clusterId, long used, long free, long total,
187187
* Cluster Mean Metric, mavg = (∑mi) / N, where mi is a measurable metric for a
188188
* resource ‘i’ in a cluster with total N number of resources.
189189
*/
190-
default Double getClusterMeanMetric(List<Double> metricList) {
190+
static Double getClusterMeanMetric(List<Double> metricList) {
191191
return new Mean().evaluate(metricList.stream().mapToDouble(i -> i).toArray());
192192
}
193193

@@ -202,19 +202,19 @@ default Double getClusterMeanMetric(List<Double> metricList) {
202202
* mean metric value and mi is a measurable metric for some resource ‘i’ in the
203203
* cluster with total N number of resources.
204204
*/
205-
default Double getClusterStandardDeviation(List<Double> metricList, Double mean) {
205+
static Double getClusterStandardDeviation(List<Double> metricList, Double mean) {
206206
if (mean != null) {
207207
return new StandardDeviation(false).evaluate(metricList.stream().mapToDouble(i -> i).toArray(), mean);
208208
} else {
209209
return new StandardDeviation(false).evaluate(metricList.stream().mapToDouble(i -> i).toArray());
210210
}
211211
}
212212

213-
default boolean getDrsMetricUseRatio(long clusterId) {
213+
static boolean getDrsMetricUseRatio(long clusterId) {
214214
return ClusterDrsMetricUseRatio.valueIn(clusterId);
215215
}
216216

217-
default String getDrsMetricType(long clusterId) {
217+
static String getDrsMetricType(long clusterId) {
218218
return ClusterDrsMetricType.valueIn(clusterId);
219219
}
220220

@@ -228,7 +228,7 @@ default String getDrsMetricType(long clusterId) {
228228
* Cluster Imbalance, Ic = σc / mavg , where σc is the standard deviation and
229229
* mavg is the mean metric value for the cluster.
230230
*/
231-
default Double getClusterImbalance(Long clusterId, List<Ternary<Long, Long, Long>> cpuList,
231+
static Double getClusterImbalance(Long clusterId, List<Ternary<Long, Long, Long>> cpuList,
232232
List<Ternary<Long, Long, Long>> memoryList, Float skipThreshold) throws ConfigurationException {
233233
String metric = getClusterDrsMetric(clusterId);
234234
List<Double> list;
@@ -246,7 +246,7 @@ default Double getClusterImbalance(Long clusterId, List<Ternary<Long, Long, Long
246246
return getImbalance(list);
247247
}
248248

249-
default List<Double> getMetricList(Long clusterId, List<Ternary<Long, Long, Long>> hostMetricsList,
249+
static List<Double> getMetricList(Long clusterId, List<Ternary<Long, Long, Long>> hostMetricsList,
250250
Float skipThreshold) {
251251
List<Double> list = new ArrayList<>();
252252
for (Ternary<Long, Long, Long> ternary : hostMetricsList) {

api/src/test/java/org/apache/cloudstack/cluster/ClusterDrsAlgorithmTest.java

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,19 +23,21 @@
2323
import junit.framework.TestCase;
2424
import org.junit.Test;
2525
import org.junit.runner.RunWith;
26-
import org.mockito.Spy;
26+
import org.mockito.MockedStatic;
27+
import org.mockito.Mockito;
2728
import org.mockito.junit.MockitoJUnitRunner;
2829

2930
import java.util.List;
3031

31-
import static org.mockito.Mockito.doReturn;
32+
import static org.apache.cloudstack.cluster.ClusterDrsAlgorithm.getMetricValue;
33+
import static org.mockito.ArgumentMatchers.any;
34+
import static org.mockito.ArgumentMatchers.anyFloat;
35+
import static org.mockito.ArgumentMatchers.anyLong;
36+
import static org.mockito.Mockito.when;
3237

3338
@RunWith(MockitoJUnitRunner.class)
3439
public class ClusterDrsAlgorithmTest extends TestCase {
3540

36-
@Spy
37-
ClusterDrsAlgorithm drsAlgorithm;
38-
3941
@Test
4042
public void testGetMetricValue() {
4143
List<Ternary<Boolean, String, Double>> testData = List.of(
@@ -54,10 +56,13 @@ public void testGetMetricValue() {
5456
String metricType = data.second();
5557
double expectedValue = data.third();
5658

57-
doReturn(useRatio).when(drsAlgorithm).getDrsMetricUseRatio(1L);
58-
doReturn(metricType).when(drsAlgorithm).getDrsMetricType(1L);
59+
try (MockedStatic<ClusterDrsAlgorithm> ignored = Mockito.mockStatic(ClusterDrsAlgorithm.class)) {
60+
when(ClusterDrsAlgorithm.getDrsMetricUseRatio(1L)).thenReturn(useRatio);
61+
when(ClusterDrsAlgorithm.getDrsMetricType(1L)).thenReturn(metricType);
62+
when(ClusterDrsAlgorithm.getMetricValue(anyLong(), anyLong(), anyLong(), anyLong(), any())).thenCallRealMethod();
5963

60-
assertEquals(expectedValue, drsAlgorithm.getMetricValue(1, used, free, total, null));
64+
assertEquals(expectedValue, getMetricValue(1, used, free, total, null));
65+
}
6166
}
6267
}
6368

@@ -80,10 +85,13 @@ public void testGetMetricValueWithSkipThreshold() {
8085
Double expectedValue = data.third();
8186
float skipThreshold = metricType.equals("free") ? 0.1f : 0.7f;
8287

83-
doReturn(useRatio).when(drsAlgorithm).getDrsMetricUseRatio(1L);
84-
doReturn(metricType).when(drsAlgorithm).getDrsMetricType(1L);
88+
try (MockedStatic<ClusterDrsAlgorithm> ignored = Mockito.mockStatic(ClusterDrsAlgorithm.class)) {
89+
when(ClusterDrsAlgorithm.getDrsMetricUseRatio(1L)).thenReturn(useRatio);
90+
when(ClusterDrsAlgorithm.getDrsMetricType(1L)).thenReturn(metricType);
91+
when(ClusterDrsAlgorithm.getMetricValue(anyLong(), anyLong(), anyLong(), anyLong(), anyFloat())).thenCallRealMethod();
8592

86-
assertEquals(expectedValue, drsAlgorithm.getMetricValue(1L, used, free, total, skipThreshold));
93+
assertEquals(expectedValue, ClusterDrsAlgorithm.getMetricValue(1L, used, free, total, skipThreshold));
94+
}
8795
}
8896
}
8997
}

plugins/drs/cluster/balanced/src/main/java/org/apache/cloudstack/cluster/Balanced.java

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.cloud.utils.Ternary;
2525
import com.cloud.utils.component.AdapterBase;
2626
import com.cloud.vm.VirtualMachine;
27+
import org.apache.log4j.Logger;
2728

2829
import javax.naming.ConfigurationException;
2930
import java.util.ArrayList;
@@ -34,31 +35,47 @@
3435

3536
public class Balanced extends AdapterBase implements ClusterDrsAlgorithm {
3637

37-
@Override
38-
public String getName() {
39-
return "balanced";
40-
}
38+
private static final Logger logger = Logger.getLogger(Balanced.class);
4139

4240
@Override
4341
public boolean needsDrs(long clusterId, List<Ternary<Long, Long, Long>> cpuList,
4442
List<Ternary<Long, Long, Long>> memoryList) throws ConfigurationException {
4543
double threshold = getThreshold(clusterId);
46-
Double imbalance = getClusterImbalance(clusterId, cpuList, memoryList, null);
47-
return imbalance > threshold;
44+
Double imbalance = ClusterDrsAlgorithm.getClusterImbalance(clusterId, cpuList, memoryList, null);
45+
String drsMetric = ClusterDrsAlgorithm.getClusterDrsMetric(clusterId);
46+
String metricType = ClusterDrsAlgorithm.getDrsMetricType(clusterId);
47+
Boolean useRatio = ClusterDrsAlgorithm.getDrsMetricUseRatio(clusterId);
48+
if (imbalance > threshold) {
49+
logger.debug(String.format("Cluster %d needs DRS. Imbalance: %s Threshold: %s Algorithm: %s DRS metric: %s Metric Type: %s Use ratio: %s",
50+
clusterId, imbalance, threshold, getName(), drsMetric, metricType, useRatio));
51+
return true;
52+
} else {
53+
logger.debug(String.format("Cluster %d does not need DRS. Imbalance: %s Threshold: %s Algorithm: %s DRS metric: %s Metric Type: %s Use ratio: %s",
54+
clusterId, imbalance, threshold, getName(), drsMetric, metricType, useRatio));
55+
return false;
56+
}
4857
}
4958

5059
private double getThreshold(long clusterId) {
5160
return 1.0 - ClusterDrsImbalanceThreshold.valueIn(clusterId);
5261
}
5362

63+
@Override
64+
public String getName() {
65+
return "balanced";
66+
}
67+
5468
@Override
5569
public Ternary<Double, Double, Double> getMetrics(long clusterId, VirtualMachine vm,
5670
ServiceOffering serviceOffering, Host destHost,
5771
Map<Long, Ternary<Long, Long, Long>> hostCpuMap, Map<Long, Ternary<Long, Long, Long>> hostMemoryMap,
5872
Boolean requiresStorageMotion) throws ConfigurationException {
59-
Double preImbalance = getClusterImbalance(clusterId, new ArrayList<>(hostCpuMap.values()), new ArrayList<>(hostMemoryMap.values()), null);
73+
Double preImbalance = ClusterDrsAlgorithm.getClusterImbalance(clusterId, new ArrayList<>(hostCpuMap.values()), new ArrayList<>(hostMemoryMap.values()), null);
6074
Double postImbalance = getImbalancePostMigration(serviceOffering, vm, destHost, hostCpuMap, hostMemoryMap);
6175

76+
logger.debug(String.format("Cluster %d pre-imbalance: %s post-imbalance: %s Algorithm: %s VM: %s dest Host: %s",
77+
clusterId, preImbalance, postImbalance, getName(), vm.getUuid(), destHost.getUuid()));
78+
6279
// This needs more research to determine the cost and benefit of a migration
6380
// TODO: Cost should be a factor of the VM size and the host capacity
6481
// TODO: Benefit should be a factor of the VM size and the host capacity and the number of VMs on the host

plugins/drs/cluster/condensed/src/main/java/org/apache/cloudstack/cluster/Condensed.java

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import com.cloud.utils.Ternary;
2525
import com.cloud.utils.component.AdapterBase;
2626
import com.cloud.vm.VirtualMachine;
27-
27+
import org.apache.log4j.Logger;
2828

2929
import javax.naming.ConfigurationException;
3030
import java.util.ArrayList;
@@ -36,31 +36,50 @@
3636

3737
public class Condensed extends AdapterBase implements ClusterDrsAlgorithm {
3838

39-
@Override
40-
public String getName() {
41-
return "condensed";
42-
}
39+
private static final Logger logger = Logger.getLogger(Condensed.class);
4340

4441
@Override
4542
public boolean needsDrs(long clusterId, List<Ternary<Long, Long, Long>> cpuList,
4643
List<Ternary<Long, Long, Long>> memoryList) throws ConfigurationException {
4744
double threshold = getThreshold(clusterId);
48-
Double imbalance = getClusterImbalance(clusterId, cpuList, memoryList, ClusterDrsImbalanceSkipThreshold.valueIn(clusterId));
49-
return imbalance < threshold;
45+
Float skipThreshold = ClusterDrsImbalanceSkipThreshold.valueIn(clusterId);
46+
Double imbalance = ClusterDrsAlgorithm.getClusterImbalance(clusterId, cpuList, memoryList, skipThreshold);
47+
String drsMetric = ClusterDrsAlgorithm.getClusterDrsMetric(clusterId);
48+
String metricType = ClusterDrsAlgorithm.getDrsMetricType(clusterId);
49+
Boolean useRatio = ClusterDrsAlgorithm.getDrsMetricUseRatio(clusterId);
50+
if (imbalance < threshold) {
51+
52+
logger.debug(String.format("Cluster %d needs DRS. Imbalance: %s Threshold: %s Algorithm: %s DRS metric: %s Metric Type: %s Use ratio: %s SkipThreshold: %s",
53+
clusterId, imbalance, threshold, getName(), drsMetric, metricType, useRatio, skipThreshold));
54+
return true;
55+
} else {
56+
logger.debug(String.format("Cluster %d does not need DRS. Imbalance: %s Threshold: %s Algorithm: %s DRS metric: %s Metric Type: %s Use ratio: %s SkipThreshold: %s",
57+
clusterId, imbalance, threshold, getName(), drsMetric, metricType, useRatio, skipThreshold));
58+
return false;
59+
}
5060
}
5161

5262
private double getThreshold(long clusterId) {
5363
return ClusterDrsImbalanceThreshold.valueIn(clusterId);
5464
}
5565

66+
@Override
67+
public String getName() {
68+
return "condensed";
69+
}
70+
5671
@Override
5772
public Ternary<Double, Double, Double> getMetrics(long clusterId, VirtualMachine vm,
5873
ServiceOffering serviceOffering, Host destHost,
5974
Map<Long, Ternary<Long, Long, Long>> hostCpuMap, Map<Long, Ternary<Long, Long, Long>> hostMemoryMap,
6075
Boolean requiresStorageMotion) throws ConfigurationException {
61-
Double preImbalance = getClusterImbalance(clusterId, new ArrayList<>(hostCpuMap.values()), new ArrayList<>(hostMemoryMap.values()), null);
76+
Double preImbalance = ClusterDrsAlgorithm.getClusterImbalance(clusterId, new ArrayList<>(hostCpuMap.values()),
77+
new ArrayList<>(hostMemoryMap.values()), null);
6278
Double postImbalance = getImbalancePostMigration(serviceOffering, vm, destHost, hostCpuMap, hostMemoryMap);
6379

80+
logger.debug(String.format("Cluster %d pre-imbalance: %s post-imbalance: %s Algorithm: %s VM: %s destHost: %s",
81+
clusterId, preImbalance, postImbalance, getName(), vm.getUuid(), destHost.getUuid()));
82+
6483
// This needs more research to determine the cost and benefit of a migration
6584
// TODO: Cost should be a factor of the VM size and the host capacity
6685
// TODO: Benefit should be a factor of the VM size and the host capacity and the number of VMs on the host

plugins/metrics/src/main/java/org/apache/cloudstack/metrics/MetricsServiceImpl.java

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@
2929
import java.util.Properties;
3030

3131
import javax.inject.Inject;
32+
import javax.naming.ConfigurationException;
3233

34+
import com.cloud.utils.Ternary;
3335
import org.apache.cloudstack.api.ApiErrorCode;
3436
import org.apache.cloudstack.api.ListClustersMetricsCmd;
3537
import org.apache.cloudstack.api.ListDbMetricsCmd;
@@ -55,6 +57,7 @@
5557
import org.apache.cloudstack.api.response.UserVmResponse;
5658
import org.apache.cloudstack.api.response.VolumeResponse;
5759
import org.apache.cloudstack.api.response.ZoneResponse;
60+
import org.apache.cloudstack.cluster.ClusterDrsAlgorithm;
5861
import org.apache.cloudstack.context.CallContext;
5962
import org.apache.cloudstack.management.ManagementServerHost.State;
6063
import org.apache.cloudstack.response.ClusterMetricsResponse;
@@ -762,10 +765,13 @@ public List<ClusterMetricsResponse> listClusterMetrics(Pair<List<ClusterResponse
762765
final Long clusterId = cluster.getId();
763766

764767
// CPU and memory capacities
765-
final CapacityDaoImpl.SummedCapacity cpuCapacity = getCapacity((int) Capacity.CAPACITY_TYPE_CPU, null, clusterId);
766-
final CapacityDaoImpl.SummedCapacity memoryCapacity = getCapacity((int) Capacity.CAPACITY_TYPE_MEMORY, null, clusterId);
768+
final CapacityDaoImpl.SummedCapacity cpuCapacity = getCapacity(Capacity.CAPACITY_TYPE_CPU, null, clusterId);
769+
final CapacityDaoImpl.SummedCapacity memoryCapacity = getCapacity(Capacity.CAPACITY_TYPE_MEMORY, null, clusterId);
767770
final HostMetrics hostMetrics = new HostMetrics(cpuCapacity, memoryCapacity);
768771

772+
List<Ternary<Long, Long, Long>> cpuList = new ArrayList<>();
773+
List<Ternary<Long, Long, Long>> memoryList = new ArrayList<>();
774+
769775
for (final Host host: hostDao.findByClusterId(clusterId)) {
770776
if (host == null || host.getType() != Host.Type.Routing) {
771777
continue;
@@ -774,7 +780,18 @@ public List<ClusterMetricsResponse> listClusterMetrics(Pair<List<ClusterResponse
774780
hostMetrics.incrUpResources();
775781
}
776782
hostMetrics.incrTotalResources();
777-
updateHostMetrics(hostMetrics, hostJoinDao.findById(host.getId()));
783+
HostJoinVO hostJoin = hostJoinDao.findById(host.getId());
784+
updateHostMetrics(hostMetrics, hostJoin);
785+
786+
cpuList.add(new Ternary<>(hostJoin.getCpuUsedCapacity(), hostJoin.getCpuReservedCapacity(), hostJoin.getCpus() * hostJoin.getSpeed()));
787+
memoryList.add(new Ternary<>(hostJoin.getMemUsedCapacity(), hostJoin.getMemReservedCapacity(), hostJoin.getTotalMemory()));
788+
}
789+
790+
try {
791+
Double imbalance = ClusterDrsAlgorithm.getClusterImbalance(clusterId, cpuList, memoryList, null);
792+
metricsResponse.setDrsImbalance(imbalance.isNaN() ? null : imbalance);
793+
} catch (ConfigurationException e) {
794+
LOGGER.warn("Failed to get cluster imbalance for cluster " + clusterId, e);
778795
}
779796

780797
metricsResponse.setState(clusterResponse.getAllocationState(), clusterResponse.getManagedState());

plugins/metrics/src/main/java/org/apache/cloudstack/response/ClusterMetricsResponse.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ public class ClusterMetricsResponse extends ClusterResponse implements HostMetri
9494
@Param(description = "memory allocated disable threshold exceeded")
9595
private Boolean memoryAllocatedDisableThresholdExceeded;
9696

97+
@SerializedName("drsimbalance")
98+
@Param(description = "DRS imbalance for the cluster")
99+
private Double drsImbalance;
100+
97101
public void setState(final String allocationState, final String managedState) {
98102
this.state = allocationState;
99103
if (managedState.equals("Unmanaged")) {
@@ -208,4 +212,8 @@ public void setMemoryAllocatedDisableThreshold(final Long memAllocated, final Lo
208212
this.memoryAllocatedDisableThresholdExceeded = (1.0 * memAllocated / memTotal) > threshold;
209213
}
210214
}
215+
216+
public void setDrsImbalance(Double drsImbalance) {
217+
this.drsImbalance = drsImbalance;
218+
}
211219
}

ui/public/locales/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,7 @@
782782
"label.dpd": "Dead peer detection",
783783
"label.driver": "Driver",
784784
"label.drs": "DRS",
785+
"label.drsimbalance": "DRS imbalance",
785786
"label.drs.plan": "DRS Plan",
786787
"label.drs.generate.plan": "Generate DRS plan",
787788
"label.drs.no.plan.generated": "No DRS plan has been generated as the cluster is not imbalanced according to the threshold set",

ui/src/components/view/ListView.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,9 @@
337337
<template v-if="column.key === 'templateversion'">
338338
<span> {{ record.version }} </span>
339339
</template>
340+
<template v-if="column.key === 'drsimbalance'">
341+
<span> {{ record.drsimbalance.toFixed(3) }} </span>
342+
</template>
340343
<template v-if="column.key === 'softwareversion'">
341344
<span> {{ record.softwareversion ? record.softwareversion : 'N/A' }} </span>
342345
</template>

ui/src/config/section/infra/clusters.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ export default {
2626
permission: ['listClustersMetrics'],
2727
columns: () => {
2828
const fields = ['name', 'state', 'allocationstate', 'clustertype', 'hypervisortype', 'hosts']
29-
const metricsFields = ['cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal']
29+
const metricsFields = ['cpuused', 'cpumaxdeviation', 'cpuallocated', 'cputotal', 'memoryused', 'memorymaxdeviation', 'memoryallocated', 'memorytotal', 'drsimbalance']
3030
if (store.getters.metrics) {
3131
fields.push(...metricsFields)
3232
}
3333
fields.push('podname')
3434
fields.push('zonename')
3535
return fields
3636
},
37-
details: ['name', 'id', 'allocationstate', 'clustertype', 'managedstate', 'hypervisortype', 'podname', 'zonename'],
37+
details: ['name', 'id', 'allocationstate', 'clustertype', 'managedstate', 'hypervisortype', 'podname', 'zonename', 'drsimbalance'],
3838
related: [{
3939
name: 'host',
4040
title: 'label.hosts',

0 commit comments

Comments
 (0)