Skip to content

Commit 9872bfd

Browse files
kvm: use the base CPU frequency as the host speed and expose the maximum frequency as a host detail (#14383)
* kvm: use the base CPU frequency as the host speed and expose the maximum frequency as a host detail * Remove turbo from UI label * Change how the maximum frequency is displayed in the UI * Apply suggestion from @sureshanaparti --------- Co-authored-by: Suresh Kumar Anaparti <sureshkumar.anaparti@gmail.com>
1 parent 30ed411 commit 9872bfd

6 files changed

Lines changed: 92 additions & 9 deletions

File tree

‎api/src/main/java/com/cloud/host/Host.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public static String[] toStrings(Host.Type... types) {
6464
String HOST_OVFTOOL_VERSION = "host.ovftool.version";
6565
String HOST_VIRTV2V_VERSION = "host.virtv2v.version";
6666
String HOST_SSH_PORT = "host.ssh.port";
67+
String HOST_CPU_MAX_SPEED_MHZ = "host.cpu.max.speed.mhz";
6768

6869
int DEFAULT_SSH_PORT = 22;
6970

‎plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// under the License.
1717
package com.cloud.hypervisor.kvm.resource;
1818

19+
import static com.cloud.host.Host.HOST_CPU_MAX_SPEED_MHZ;
1920
import static com.cloud.host.Host.HOST_INSTANCE_CONVERSION;
2021
import static com.cloud.host.Host.HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM;
2122
import static com.cloud.host.Host.HOST_OVFTOOL_VERSION;
@@ -4265,6 +4266,9 @@ public StartupCommand[] initialize() {
42654266
new StartupRoutingCommand(info.getAllocatableCpus(), info.getCpuSpeed(), info.getTotalMemory(), info.getReservedMemory(), capabilities, hypervisorType,
42664267
RouterPrivateIpStrategy.HostLocal);
42674268
cmd.setCpuSockets(info.getCpuSockets());
4269+
if (info.getCpuMaxSpeed() > 0) {
4270+
cmd.getHostDetails().put(HOST_CPU_MAX_SPEED_MHZ, String.valueOf(info.getCpuMaxSpeed()));
4271+
}
42684272
fillNetworkInformation(cmd);
42694273
privateIp = cmd.getPrivateIpAddress();
42704274
cmd.getHostDetails().putAll(getVersionStrings());

‎plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java‎

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,13 @@ public class KVMHostInfo {
5353
private int allocatableCpus;
5454
private int cpusockets;
5555
private long cpuSpeed;
56+
private long cpuMaxSpeed;
5657
private String cpuArch;
5758
private long totalMemory;
5859
private long reservedMemory;
5960
private long overCommitMemory;
6061
private List<String> capabilities = new ArrayList<>();
62+
private static final String LSCPU_CPU_MAX_MHZ_COMMAND = "lscpu | grep -i 'CPU max MHz' | head -n 1 | sed 's/^.*: //' | xargs";
6163
private static String cpuArchRetrieveExecutable = "arch";
6264
private static List<String> cpuInfoFreqFileNames = List.of("/sys/devices/system/cpu/cpu0/cpufreq/base_frequency","/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
6365

@@ -90,6 +92,14 @@ public long getCpuSpeed() {
9092
return this.cpuSpeed;
9193
}
9294

95+
/**
96+
* @return the maximum frequency of the CPU, in MHz, which may include the turbo frequency; or 0 if it could not be retrieved. This value is only
97+
* informative; {@link #getCpuSpeed()} is the one used to calculate the capacity of the host.
98+
*/
99+
public long getCpuMaxSpeed() {
100+
return this.cpuMaxSpeed;
101+
}
102+
93103
public long getTotalMemory() {
94104
return this.totalMemory;
95105
}
@@ -136,25 +146,30 @@ protected static long getCpuSpeed(final String cpabilities, final NodeInfo nodeI
136146
return speed;
137147
}
138148

139-
private static long getCpuSpeedFromCommandLscpu() {
149+
/**
150+
* Retrieves the CPU speed, in MHz, from the "lscpu" command. The base frequency advertised in the model name is preferred because the CPU speed is used to
151+
* calculate the capacity of the host and the CPU quota of the VMs with CPU cap enabled; thus, it must be a frequency that the CPU is able to sustain.
152+
* "CPU max MHz" may include the turbo frequency; therefore, it is only used when the model name does not advertise a frequency.
153+
*/
154+
protected static long getCpuSpeedFromCommandLscpu() {
140155
long speed = 0L;
141156
LOGGER.info("Fetching CPU speed from command \"lscpu\".");
142157
try {
143-
String command = "lscpu | grep -i 'CPU max MHz' | head -n 1 | sed 's/^.*: //' | xargs";
158+
String command = "lscpu | grep -i 'Model name' | head -n 1 | egrep -o '[[:digit:]].[[:digit:]]+GHz' | sed 's/GHz//g'";
159+
if (isHostS390x()) {
160+
command = "lscpu | grep 'CPU dynamic MHz' | cut -d ':' -f 2 | tr -d ' ' | awk '{printf \"%.1f\\n\", $1 / 1000}'";
161+
}
144162
String result = Script.runSimpleBashScript(command);
145-
speed = (long) (Float.parseFloat(result));
163+
speed = (long) (Float.parseFloat(result) * 1000);
146164
LOGGER.info(String.format("Command [%s] resulted in the value [%s] for CPU speed.", command, speed));
147165
return speed;
148166
} catch (NullPointerException | NumberFormatException e) {
149167
LOGGER.error(String.format("Unable to retrieve the CPU speed from lscpu."), e);
150168
}
151169
try {
152-
String command = "lscpu | grep -i 'Model name' | head -n 1 | egrep -o '[[:digit:]].[[:digit:]]+GHz' | sed 's/GHz//g'";
153-
if(isHostS390x()) {
154-
command = "lscpu | grep 'CPU dynamic MHz' | cut -d ':' -f 2 | tr -d ' ' | awk '{printf \"%.1f\\n\", $1 / 1000}'";
155-
}
170+
String command = LSCPU_CPU_MAX_MHZ_COMMAND;
156171
String result = Script.runSimpleBashScript(command);
157-
speed = (long) (Float.parseFloat(result) * 1000);
172+
speed = (long) (Float.parseFloat(result));
158173
LOGGER.info(String.format("Command [%s] resulted in the value [%s] for CPU speed.", command, speed));
159174
return speed;
160175
} catch (NullPointerException | NumberFormatException e) {
@@ -163,6 +178,16 @@ private static long getCpuSpeedFromCommandLscpu() {
163178
return speed;
164179
}
165180

181+
protected static long getCpuMaxSpeedFromCommandLscpu() {
182+
String result = Script.runSimpleBashScript(LSCPU_CPU_MAX_MHZ_COMMAND);
183+
try {
184+
return (long) Float.parseFloat(result);
185+
} catch (NullPointerException | NumberFormatException e) {
186+
LOGGER.debug(String.format("Unable to retrieve the CPU max speed from lscpu; command [%s] resulted in the value [%s].", LSCPU_CPU_MAX_MHZ_COMMAND, result));
187+
return 0L;
188+
}
189+
}
190+
166191
private static long getCpuSpeedFromFile() {
167192
for (final String cpuInfoFreqFileName: cpuInfoFreqFileNames) {
168193
LOGGER.info(String.format("Fetching CPU speed from file [%s].", cpuInfoFreqFileName));
@@ -215,6 +240,7 @@ private void getHostInfoFromLibvirt() {
215240
} else {
216241
LOGGER.debug(String.format("Using existing configured CPU frequency %s", this.cpuSpeed));
217242
}
243+
this.cpuMaxSpeed = getCpuMaxSpeedFromCommandLscpu();
218244

219245
/*
220246
* Some CPUs report a single socket and multiple NUMA cells.

‎plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.mockito.Mockito;
2828

2929
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
30+
import com.cloud.utils.script.Script;
3031
import org.mockito.junit.MockitoJUnitRunner;
3132

3233
@RunWith(MockitoJUnitRunner.class)
@@ -102,4 +103,49 @@ public void reservedCpuCoresTest() throws Exception {
102103
Assert.assertEquals("Misconfigured/too large CPU reserve", 0, kvmHostInfo.getAllocatableCpus());
103104
}
104105
}
106+
107+
private void mockLscpuOutput(MockedStatic<Script> script, String modelNameGhz, String cpuMaxMhz) {
108+
script.when(() -> Script.runSimpleBashScript(Mockito.contains("Model name"))).thenReturn(modelNameGhz);
109+
script.when(() -> Script.runSimpleBashScript(Mockito.contains("CPU max MHz"))).thenReturn(cpuMaxMhz);
110+
}
111+
112+
@Test
113+
public void getCpuSpeedFromCommandLscpuTestPrefersModelNameFrequencyOverCpuMaxMhz() {
114+
try (MockedStatic<Script> script = Mockito.mockStatic(Script.class)) {
115+
mockLscpuOutput(script, "2.40", "3400.0000");
116+
Assert.assertEquals(2400L, KVMHostInfo.getCpuSpeedFromCommandLscpu());
117+
}
118+
}
119+
120+
@Test
121+
public void getCpuSpeedFromCommandLscpuTestFallsBackToCpuMaxMhzWhenModelNameHasNoFrequency() {
122+
try (MockedStatic<Script> script = Mockito.mockStatic(Script.class)) {
123+
mockLscpuOutput(script, null, "3400.0000");
124+
Assert.assertEquals(3400L, KVMHostInfo.getCpuSpeedFromCommandLscpu());
125+
}
126+
}
127+
128+
@Test
129+
public void getCpuSpeedFromCommandLscpuTestReturnsZeroWhenLscpuHasNoFrequency() {
130+
try (MockedStatic<Script> script = Mockito.mockStatic(Script.class)) {
131+
mockLscpuOutput(script, null, null);
132+
Assert.assertEquals(0L, KVMHostInfo.getCpuSpeedFromCommandLscpu());
133+
}
134+
}
135+
136+
@Test
137+
public void getCpuMaxSpeedFromCommandLscpuTestReturnsCpuMaxMhz() {
138+
try (MockedStatic<Script> script = Mockito.mockStatic(Script.class)) {
139+
mockLscpuOutput(script, "2.40", "3400.0000");
140+
Assert.assertEquals(3400L, KVMHostInfo.getCpuMaxSpeedFromCommandLscpu());
141+
}
142+
}
143+
144+
@Test
145+
public void getCpuMaxSpeedFromCommandLscpuTestReturnsZeroWhenLscpuHasNoCpuMaxMhz() {
146+
try (MockedStatic<Script> script = Mockito.mockStatic(Script.class)) {
147+
mockLscpuOutput(script, "2.40", null);
148+
Assert.assertEquals(0L, KVMHostInfo.getCpuMaxSpeedFromCommandLscpu());
149+
}
150+
}
105151
}

‎ui/public/locales/en.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1215,6 +1215,7 @@
12151215
"label.home": "Home",
12161216
"label.host": "IP address",
12171217
"label.host.alerts": "Hosts in alert state",
1218+
"label.host.cpu.max.speed": "CPU maximum frequency",
12181219
"label.host.name": "Host name",
12191220
"label.host.ovftool.version": "OVFTool Version",
12201221
"label.host.vddk.support": "VDDK Support",

‎ui/src/components/view/InfoCard.vue‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,12 @@
213213
</a-tooltip>
214214
</span>
215215
<span v-else>{{ resource.cputotal }}</span>
216-
<a-tag v-if="resource.arch" style="margin-left: 10px">
216+
<a-tooltip v-if="resource.details && resource.details['host.cpu.max.speed.mhz']" :title="$t('label.host.cpu.max.speed')">
217+
<a-tag>
218+
<RocketOutlined /> {{ (resource.details['host.cpu.max.speed.mhz'] / 1000.0).toFixed(2) }} GHz
219+
</a-tag>
220+
</a-tooltip>
221+
<a-tag v-if="resource.arch">
217222
{{ resource.arch }}
218223
</a-tag>
219224
</div>

0 commit comments

Comments
 (0)