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>

Reply via email to