Skip to content

Commit b831f23

Browse files
authored
kvm: add libvirt host capabilities method for cpu speed retrieval (#6696)
Fixes #6680 While finding CPU speed for KVM host following methods will be used in the same order: 1. lscpu 2. value in /sys/devices/system/cpu/cpu0/cpufreq/base_frequency 3. virsh capabilities 4. libvirt nodeinfo This will allow correct value for AMD based hosts when first two methods doesn't give a value Signed-off-by: Abhishek Kumar <abhishek.mrt22@gmail.com>
1 parent f18eebf commit b831f23

File tree

2 files changed

+77
-18
lines changed

2 files changed

+77
-18
lines changed

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

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,33 @@
1616
// under the License.
1717
package org.apache.cloudstack.utils.linux;
1818

19-
import com.cloud.hypervisor.kvm.resource.LibvirtCapXMLParser;
20-
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
21-
import com.cloud.utils.script.Script;
19+
import java.io.FileReader;
20+
import java.io.IOException;
21+
import java.io.Reader;
22+
import java.io.StringReader;
23+
import java.util.ArrayList;
24+
import java.util.List;
2225

26+
import javax.xml.parsers.DocumentBuilder;
27+
import javax.xml.parsers.DocumentBuilderFactory;
28+
29+
import org.apache.cloudstack.utils.security.ParserUtils;
2330
import org.apache.commons.io.IOUtils;
31+
import org.apache.commons.lang3.StringUtils;
2432
import org.apache.log4j.Logger;
2533
import org.libvirt.Connect;
2634
import org.libvirt.LibvirtException;
2735
import org.libvirt.NodeInfo;
36+
import org.w3c.dom.Document;
37+
import org.w3c.dom.Element;
38+
import org.w3c.dom.NamedNodeMap;
39+
import org.w3c.dom.Node;
40+
import org.w3c.dom.NodeList;
41+
import org.xml.sax.InputSource;
2842

29-
import java.io.FileReader;
30-
import java.io.IOException;
31-
import java.io.Reader;
32-
import java.util.ArrayList;
33-
import java.util.List;
43+
import com.cloud.hypervisor.kvm.resource.LibvirtCapXMLParser;
44+
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
45+
import com.cloud.utils.script.Script;
3446

3547
public class KVMHostInfo {
3648

@@ -82,7 +94,7 @@ public List<String> getCapabilities() {
8294
return this.capabilities;
8395
}
8496

85-
protected static long getCpuSpeed(final NodeInfo nodeInfo) {
97+
protected static long getCpuSpeed(final String cpabilities, final NodeInfo nodeInfo) {
8698
long speed = 0L;
8799
speed = getCpuSpeedFromCommandLscpu();
88100
if(speed > 0L) {
@@ -94,6 +106,11 @@ protected static long getCpuSpeed(final NodeInfo nodeInfo) {
94106
return speed;
95107
}
96108

109+
speed = getCpuSpeedFromHostCapabilities(cpabilities);
110+
if(speed > 0L) {
111+
return speed;
112+
}
113+
97114
LOGGER.info(String.format("Using the value [%s] provided by Libvirt.", nodeInfo.mhz));
98115
speed = nodeInfo.mhz;
99116
return speed;
@@ -125,12 +142,41 @@ private static long getCpuSpeedFromFile() {
125142
}
126143
}
127144

145+
protected static long getCpuSpeedFromHostCapabilities(final String capabilities) {
146+
LOGGER.info("Fetching CPU speed from \"host capabilities\"");
147+
long speed = 0L;
148+
try {
149+
DocumentBuilderFactory docFactory = ParserUtils.getSaferDocumentBuilderFactory();
150+
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
151+
Document doc = docBuilder.parse(new InputSource(new StringReader(capabilities)));
152+
Element rootElement = doc.getDocumentElement();
153+
NodeList nodes = rootElement.getElementsByTagName("cpu");
154+
Node node = nodes.item(0);
155+
nodes = ((Element)node).getElementsByTagName("counter");
156+
for (int i = 0; i < nodes.getLength(); i++) {
157+
node = nodes.item(i);
158+
NamedNodeMap attributes = node.getAttributes();
159+
Node nameNode = attributes.getNamedItem("name");
160+
Node freqNode = attributes.getNamedItem("frequency");
161+
if (nameNode != null && "tsc".equals(nameNode.getNodeValue()) && freqNode != null && StringUtils.isNotEmpty(freqNode.getNodeValue())) {
162+
speed = Long.parseLong(freqNode.getNodeValue()) / 1000000;
163+
LOGGER.info(String.format("Retrieved value [%s] from \"host capabilities\". This corresponds to a CPU speed of [%s] MHz.", freqNode.getNodeValue(), speed));
164+
}
165+
}
166+
} catch (Exception ex) {
167+
LOGGER.error("Unable to fetch CPU speed from \"host capabilities\"", ex);
168+
speed = 0L;
169+
}
170+
return speed;
171+
}
172+
128173
private void getHostInfoFromLibvirt() {
129174
try {
130175
final Connect conn = LibvirtConnection.getConnection();
131176
final NodeInfo hosts = conn.nodeInfo();
177+
final String capabilities = conn.getCapabilities();
132178
if (this.cpuSpeed == 0) {
133-
this.cpuSpeed = getCpuSpeed(hosts);
179+
this.cpuSpeed = getCpuSpeed(capabilities, hosts);
134180
} else {
135181
LOGGER.debug(String.format("Using existing configured CPU frequency %s", this.cpuSpeed));
136182
}
@@ -146,7 +192,7 @@ private void getHostInfoFromLibvirt() {
146192
this.cpus = hosts.cpus;
147193

148194
final LibvirtCapXMLParser parser = new LibvirtCapXMLParser();
149-
parser.parseCapabilitiesXML(conn.getCapabilities());
195+
parser.parseCapabilitiesXML(capabilities);
150196
final ArrayList<String> oss = parser.getGuestOsType();
151197
for (final String s : oss) {
152198
/*

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

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,22 @@
1616
// under the License.
1717
package org.apache.cloudstack.utils.linux;
1818

19-
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
2019
import org.apache.commons.lang.SystemUtils;
21-
2220
import org.hamcrest.Matchers;
23-
import org.junit.Test;
24-
import org.junit.Assume;
2521
import org.junit.Assert;
22+
import org.junit.Assume;
23+
import org.junit.Test;
2624
import org.junit.runner.RunWith;
2725
import org.libvirt.Connect;
28-
import org.mockito.Mockito;
29-
3026
import org.libvirt.NodeInfo;
27+
import org.mockito.Mockito;
3128
import org.powermock.api.mockito.PowerMockito;
3229
import org.powermock.core.classloader.annotations.PowerMockIgnore;
3330
import org.powermock.core.classloader.annotations.PrepareForTest;
3431
import org.powermock.modules.junit4.PowerMockRunner;
3532

33+
import com.cloud.hypervisor.kvm.resource.LibvirtConnection;
34+
3635
@RunWith(PowerMockRunner.class)
3736
@PrepareForTest(value = {LibvirtConnection.class})
3837
@PowerMockIgnore({"javax.xml.*", "org.w3c.dom.*", "org.apache.xerces.*", "org.xml.*"})
@@ -45,7 +44,21 @@ public void getCpuSpeed() {
4544
Assume.assumeTrue(SystemUtils.IS_OS_LINUX);
4645
NodeInfo nodeInfo = Mockito.mock(NodeInfo.class);
4746
nodeInfo.mhz = 1000;
48-
Assert.assertThat(KVMHostInfo.getCpuSpeed(nodeInfo), Matchers.greaterThan(0l));
47+
Assert.assertThat(KVMHostInfo.getCpuSpeed(null, nodeInfo), Matchers.greaterThan(0l));
48+
}
49+
50+
@Test
51+
public void getCpuSpeedFromHostCapabilities() {
52+
String capabilities = "<host>\n" +
53+
"<uuid>8a330742-345f-b0df-7954-c9960b88116c</uuid>\n" +
54+
" <cpu>\n" +
55+
" <arch>x86_64</arch>\n" +
56+
" <model>Opteron_G2</model>\n" +
57+
" <vendor>AMD</vendor>\n" +
58+
" <counter name='tsc' frequency='2350000000' scaling='no'/>\n" +
59+
" </cpu>\n" +
60+
"</host>\n";;
61+
Assert.assertEquals(2350L, KVMHostInfo.getCpuSpeedFromHostCapabilities(capabilities));
4962
}
5063

5164
@Test

0 commit comments

Comments
 (0)