This is an automated email from the ASF dual-hosted git repository.
nvazquez pushed a commit to branch 4.22
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/4.22 by this push:
new 9872bfd61fc kvm: use the base CPU frequency as the host speed and
expose the maximum frequency as a host detail (#14383)
9872bfd61fc is described below
commit 9872bfd61fc7f2da312a433d7ddbd5c8eae783a4
Author: Fabricio Duarte <[email protected]>
AuthorDate: Sat Oct 24 15:08:34 2026 -0300
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 <[email protected]>
---
api/src/main/java/com/cloud/host/Host.java | 1 +
.../kvm/resource/LibvirtComputingResource.java | 4 ++
.../apache/cloudstack/utils/linux/KVMHostInfo.java | 42 ++++++++++++++++----
.../cloudstack/utils/linux/KVMHostInfoTest.java | 46 ++++++++++++++++++++++
ui/public/locales/en.json | 1 +
ui/src/components/view/InfoCard.vue | 7 +++-
6 files changed, 92 insertions(+), 9 deletions(-)
diff --git a/api/src/main/java/com/cloud/host/Host.java
b/api/src/main/java/com/cloud/host/Host.java
index a7b89b8a2b8..c2c43aee137 100644
--- a/api/src/main/java/com/cloud/host/Host.java
+++ b/api/src/main/java/com/cloud/host/Host.java
@@ -64,6 +64,7 @@ public interface Host extends StateObject<Status>, Identity,
Partition, HAResour
String HOST_OVFTOOL_VERSION = "host.ovftool.version";
String HOST_VIRTV2V_VERSION = "host.virtv2v.version";
String HOST_SSH_PORT = "host.ssh.port";
+ String HOST_CPU_MAX_SPEED_MHZ = "host.cpu.max.speed.mhz";
int DEFAULT_SSH_PORT = 22;
diff --git
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 9409cab00b4..1f90a625755 100644
---
a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++
b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -16,6 +16,7 @@
// under the License.
package com.cloud.hypervisor.kvm.resource;
+import static com.cloud.host.Host.HOST_CPU_MAX_SPEED_MHZ;
import static com.cloud.host.Host.HOST_INSTANCE_CONVERSION;
import static com.cloud.host.Host.HOST_KVM_DISK_ONLY_VM_SNAPSHOT_NVRAM;
import static com.cloud.host.Host.HOST_OVFTOOL_VERSION;
@@ -4265,6 +4266,9 @@ public class LibvirtComputingResource extends
ServerResourceBase implements Serv
new StartupRoutingCommand(info.getAllocatableCpus(),
info.getCpuSpeed(), info.getTotalMemory(), info.getReservedMemory(),
capabilities, hypervisorType,
RouterPrivateIpStrategy.HostLocal);
cmd.setCpuSockets(info.getCpuSockets());
+ if (info.getCpuMaxSpeed() > 0) {
+ cmd.getHostDetails().put(HOST_CPU_MAX_SPEED_MHZ,
String.valueOf(info.getCpuMaxSpeed()));
+ }
fillNetworkInformation(cmd);
privateIp = cmd.getPrivateIpAddress();
cmd.getHostDetails().putAll(getVersionStrings());
diff --git
a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
index d8e5bed2ef0..510a526be66 100644
---
a/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
+++
b/plugins/hypervisors/kvm/src/main/java/org/apache/cloudstack/utils/linux/KVMHostInfo.java
@@ -53,11 +53,13 @@ public class KVMHostInfo {
private int allocatableCpus;
private int cpusockets;
private long cpuSpeed;
+ private long cpuMaxSpeed;
private String cpuArch;
private long totalMemory;
private long reservedMemory;
private long overCommitMemory;
private List<String> capabilities = new ArrayList<>();
+ private static final String LSCPU_CPU_MAX_MHZ_COMMAND = "lscpu | grep -i
'CPU max MHz' | head -n 1 | sed 's/^.*: //' | xargs";
private static String cpuArchRetrieveExecutable = "arch";
private static List<String> cpuInfoFreqFileNames =
List.of("/sys/devices/system/cpu/cpu0/cpufreq/base_frequency","/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq");
@@ -90,6 +92,14 @@ public class KVMHostInfo {
return this.cpuSpeed;
}
+ /**
+ * @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
+ * informative; {@link #getCpuSpeed()} is the one used to calculate the
capacity of the host.
+ */
+ public long getCpuMaxSpeed() {
+ return this.cpuMaxSpeed;
+ }
+
public long getTotalMemory() {
return this.totalMemory;
}
@@ -136,25 +146,30 @@ public class KVMHostInfo {
return speed;
}
- private static long getCpuSpeedFromCommandLscpu() {
+ /**
+ * 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
+ * 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.
+ * "CPU max MHz" may include the turbo frequency; therefore, it is only
used when the model name does not advertise a frequency.
+ */
+ protected static long getCpuSpeedFromCommandLscpu() {
long speed = 0L;
LOGGER.info("Fetching CPU speed from command \"lscpu\".");
try {
- String command = "lscpu | grep -i 'CPU max MHz' | head -n 1 | sed
's/^.*: //' | xargs";
+ String command = "lscpu | grep -i 'Model name' | head -n 1 | egrep
-o '[[:digit:]].[[:digit:]]+GHz' | sed 's/GHz//g'";
+ if (isHostS390x()) {
+ command = "lscpu | grep 'CPU dynamic MHz' | cut -d ':' -f 2 |
tr -d ' ' | awk '{printf \"%.1f\\n\", $1 / 1000}'";
+ }
String result = Script.runSimpleBashScript(command);
- speed = (long) (Float.parseFloat(result));
+ speed = (long) (Float.parseFloat(result) * 1000);
LOGGER.info(String.format("Command [%s] resulted in the value [%s]
for CPU speed.", command, speed));
return speed;
} catch (NullPointerException | NumberFormatException e) {
LOGGER.error(String.format("Unable to retrieve the CPU speed from
lscpu."), e);
}
try {
- String command = "lscpu | grep -i 'Model name' | head -n 1 | egrep
-o '[[:digit:]].[[:digit:]]+GHz' | sed 's/GHz//g'";
- if(isHostS390x()) {
- command = "lscpu | grep 'CPU dynamic MHz' | cut -d ':' -f 2 |
tr -d ' ' | awk '{printf \"%.1f\\n\", $1 / 1000}'";
- }
+ String command = LSCPU_CPU_MAX_MHZ_COMMAND;
String result = Script.runSimpleBashScript(command);
- speed = (long) (Float.parseFloat(result) * 1000);
+ speed = (long) (Float.parseFloat(result));
LOGGER.info(String.format("Command [%s] resulted in the value [%s]
for CPU speed.", command, speed));
return speed;
} catch (NullPointerException | NumberFormatException e) {
@@ -163,6 +178,16 @@ public class KVMHostInfo {
return speed;
}
+ protected static long getCpuMaxSpeedFromCommandLscpu() {
+ String result = Script.runSimpleBashScript(LSCPU_CPU_MAX_MHZ_COMMAND);
+ try {
+ return (long) Float.parseFloat(result);
+ } catch (NullPointerException | NumberFormatException e) {
+ 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));
+ return 0L;
+ }
+ }
+
private static long getCpuSpeedFromFile() {
for (final String cpuInfoFreqFileName: cpuInfoFreqFileNames) {
LOGGER.info(String.format("Fetching CPU speed from file [%s].",
cpuInfoFreqFileName));
@@ -215,6 +240,7 @@ public class KVMHostInfo {
} else {
LOGGER.debug(String.format("Using existing configured CPU
frequency %s", this.cpuSpeed));
}
+ this.cpuMaxSpeed = getCpuMaxSpeedFromCommandLscpu();
/*
* Some CPUs report a single socket and multiple NUMA cells.
diff --git
a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
index b41e2bf214a..782d5e8270b 100644
---
a/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
+++
b/plugins/hypervisors/kvm/src/test/java/org/apache/cloudstack/utils/linux/KVMHostInfoTest.java
@@ -27,6 +27,7 @@ import org.mockito.MockedStatic;
import org.mockito.Mockito;
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
+import com.cloud.utils.script.Script;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
@@ -102,4 +103,49 @@ public class KVMHostInfoTest {
Assert.assertEquals("Misconfigured/too large CPU reserve", 0,
kvmHostInfo.getAllocatableCpus());
}
}
+
+ private void mockLscpuOutput(MockedStatic<Script> script, String
modelNameGhz, String cpuMaxMhz) {
+ script.when(() -> Script.runSimpleBashScript(Mockito.contains("Model
name"))).thenReturn(modelNameGhz);
+ script.when(() -> Script.runSimpleBashScript(Mockito.contains("CPU max
MHz"))).thenReturn(cpuMaxMhz);
+ }
+
+ @Test
+ public void
getCpuSpeedFromCommandLscpuTestPrefersModelNameFrequencyOverCpuMaxMhz() {
+ try (MockedStatic<Script> script = Mockito.mockStatic(Script.class)) {
+ mockLscpuOutput(script, "2.40", "3400.0000");
+ Assert.assertEquals(2400L,
KVMHostInfo.getCpuSpeedFromCommandLscpu());
+ }
+ }
+
+ @Test
+ public void
getCpuSpeedFromCommandLscpuTestFallsBackToCpuMaxMhzWhenModelNameHasNoFrequency()
{
+ try (MockedStatic<Script> script = Mockito.mockStatic(Script.class)) {
+ mockLscpuOutput(script, null, "3400.0000");
+ Assert.assertEquals(3400L,
KVMHostInfo.getCpuSpeedFromCommandLscpu());
+ }
+ }
+
+ @Test
+ public void
getCpuSpeedFromCommandLscpuTestReturnsZeroWhenLscpuHasNoFrequency() {
+ try (MockedStatic<Script> script = Mockito.mockStatic(Script.class)) {
+ mockLscpuOutput(script, null, null);
+ Assert.assertEquals(0L, KVMHostInfo.getCpuSpeedFromCommandLscpu());
+ }
+ }
+
+ @Test
+ public void getCpuMaxSpeedFromCommandLscpuTestReturnsCpuMaxMhz() {
+ try (MockedStatic<Script> script = Mockito.mockStatic(Script.class)) {
+ mockLscpuOutput(script, "2.40", "3400.0000");
+ Assert.assertEquals(3400L,
KVMHostInfo.getCpuMaxSpeedFromCommandLscpu());
+ }
+ }
+
+ @Test
+ public void
getCpuMaxSpeedFromCommandLscpuTestReturnsZeroWhenLscpuHasNoCpuMaxMhz() {
+ try (MockedStatic<Script> script = Mockito.mockStatic(Script.class)) {
+ mockLscpuOutput(script, "2.40", null);
+ Assert.assertEquals(0L,
KVMHostInfo.getCpuMaxSpeedFromCommandLscpu());
+ }
+ }
}
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index 382eb785db0..4f06153320b 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1215,6 +1215,7 @@
"label.home": "Home",
"label.host": "IP address",
"label.host.alerts": "Hosts in alert state",
+"label.host.cpu.max.speed": "CPU maximum frequency",
"label.host.name": "Host name",
"label.host.ovftool.version": "OVFTool Version",
"label.host.vddk.support": "VDDK Support",
diff --git a/ui/src/components/view/InfoCard.vue
b/ui/src/components/view/InfoCard.vue
index 51f182b6f61..dce12533799 100644
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@ -213,7 +213,12 @@
</a-tooltip>
</span>
<span v-else>{{ resource.cputotal }}</span>
- <a-tag v-if="resource.arch" style="margin-left: 10px">
+ <a-tooltip v-if="resource.details &&
resource.details['host.cpu.max.speed.mhz']"
:title="$t('label.host.cpu.max.speed')">
+ <a-tag>
+ <RocketOutlined /> {{
(resource.details['host.cpu.max.speed.mhz'] / 1000.0).toFixed(2) }} GHz
+ </a-tag>
+ </a-tooltip>
+ <a-tag v-if="resource.arch">
{{ resource.arch }}
</a-tag>
</div>