Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions api/src/main/java/com/cloud/host/Host.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public static String[] toStrings(Host.Type... types) {
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;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -4262,6 +4263,9 @@ public StartupCommand[] initialize() {
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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");

Expand Down Expand Up @@ -90,6 +92,14 @@ public long getCpuSpeed() {
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;
}
Expand Down Expand Up @@ -136,25 +146,30 @@ protected static long getCpuSpeed(final String cpabilities, final NodeInfo nodeI
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()) {
Comment thread
sureshanaparti marked this conversation as resolved.
Outdated
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;
Comment thread
winterhazel marked this conversation as resolved.
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) {
Expand All @@ -163,6 +178,16 @@ private static long getCpuSpeedFromCommandLscpu() {
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));
Expand Down Expand Up @@ -215,6 +240,7 @@ private void getHostInfoFromLibvirt() {
} 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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
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)
Expand Down Expand Up @@ -102,4 +103,49 @@ public void reservedCpuCoresTest() throws Exception {
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());
}
}
}
1 change: 1 addition & 0 deletions ui/public/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
7 changes: 6 additions & 1 deletion ui/src/components/view/InfoCard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading