mirror of https://github.com/apache/cloudstack.git
Add ability to set cpu.threadspercore similar to existing cpu.corespersocket (#411)
* Add ability to set cpu.threadspercore similar to existing cpu.corespersocket * Add license to new test file * Add tests to handle some edge cases * Add some edge test cases to CPU topology * Rework logic on KVM CPU topology, handle more cases * Add more test cases * Add more test cases * Update plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java Co-authored-by: Suresh Kumar Anaparti <suresh.anaparti@shapeblue.com> * Added cpu.threadspercore detail in listDetailOptions response (for KVM hypervisor) --------- Co-authored-by: Marcus Sorensen <mls@apple.com> Co-authored-by: Suresh Kumar Anaparti <suresh.anaparti@shapeblue.com>
This commit is contained in:
parent
631b0960f3
commit
227dc5e86a
|
|
@ -19,6 +19,7 @@ package com.cloud.vm;
|
|||
public interface VmDetailConstants {
|
||||
String KEYBOARD = "keyboard";
|
||||
String CPU_CORE_PER_SOCKET = "cpu.corespersocket";
|
||||
String CPU_THREAD_PER_CORE = "cpu.threadspercore";
|
||||
String ROOT_DISK_SIZE = "rootdisksize";
|
||||
String BOOT_MODE = "boot.mode";
|
||||
String NAME_ON_HYPERVISOR= "nameonhypervisor";
|
||||
|
|
|
|||
|
|
@ -5024,31 +5024,48 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
|
|||
return false;
|
||||
}
|
||||
|
||||
private void setCpuTopology(CpuModeDef cmd, int vCpusInDef, Map<String, String> details) {
|
||||
protected void setCpuTopology(CpuModeDef cmd, int vCpusInDef, Map<String, String> details) {
|
||||
if (!enableManuallySettingCpuTopologyOnKvmVm) {
|
||||
s_logger.debug(String.format("Skipping manually setting CPU topology on VM's XML due to it is disabled in agent.properties {\"property\": \"%s\", \"value\": %s}.",
|
||||
AgentProperties.ENABLE_MANUALLY_SETTING_CPU_TOPOLOGY_ON_KVM_VM.getName(), enableManuallySettingCpuTopologyOnKvmVm));
|
||||
return;
|
||||
}
|
||||
// multi cores per socket, for larger core configs
|
||||
int numCoresPerSocket = -1;
|
||||
|
||||
int numCoresPerSocket = 1;
|
||||
int numThreadsPerCore = 1;
|
||||
|
||||
if (details != null) {
|
||||
final String coresPerSocket = details.get(VmDetailConstants.CPU_CORE_PER_SOCKET);
|
||||
final int intCoresPerSocket = NumbersUtil.parseInt(coresPerSocket, numCoresPerSocket);
|
||||
if (intCoresPerSocket > 0 && vCpusInDef % intCoresPerSocket == 0) {
|
||||
numCoresPerSocket = intCoresPerSocket;
|
||||
}
|
||||
numCoresPerSocket = NumbersUtil.parseInt(details.get(VmDetailConstants.CPU_CORE_PER_SOCKET), 1);
|
||||
numThreadsPerCore = NumbersUtil.parseInt(details.get(VmDetailConstants.CPU_THREAD_PER_CORE), 1);
|
||||
}
|
||||
if (numCoresPerSocket <= 0) {
|
||||
|
||||
if ((numCoresPerSocket * numThreadsPerCore) > vCpusInDef) {
|
||||
s_logger.warn(String.format("cores per socket (%d) * threads per core (%d) exceeds total VM cores. Ignoring extra topology", numCoresPerSocket, numThreadsPerCore));
|
||||
numCoresPerSocket = 1;
|
||||
numThreadsPerCore = 1;
|
||||
}
|
||||
|
||||
if (vCpusInDef % (numCoresPerSocket * numThreadsPerCore) != 0) {
|
||||
s_logger.warn(String.format("cores per socket(%d) * threads per core(%d) doesn't divide evenly into total VM cores(%d). Ignoring extra topology", numCoresPerSocket, numThreadsPerCore, vCpusInDef));
|
||||
numCoresPerSocket = 1;
|
||||
numThreadsPerCore = 1;
|
||||
}
|
||||
|
||||
// Set default coupling (makes 4 or 6 core sockets for larger core configs)
|
||||
int numTotalSockets = 1;
|
||||
if (numCoresPerSocket == 1 && numThreadsPerCore == 1) {
|
||||
if (vCpusInDef % 6 == 0) {
|
||||
numCoresPerSocket = 6;
|
||||
} else if (vCpusInDef % 4 == 0) {
|
||||
numCoresPerSocket = 4;
|
||||
}
|
||||
numTotalSockets = vCpusInDef / numCoresPerSocket;
|
||||
} else {
|
||||
int nTotalCores = vCpusInDef / numThreadsPerCore;
|
||||
numTotalSockets = nTotalCores / numCoresPerSocket;
|
||||
}
|
||||
if (numCoresPerSocket > 0) {
|
||||
cmd.setTopology(numCoresPerSocket, vCpusInDef / numCoresPerSocket);
|
||||
}
|
||||
|
||||
cmd.setTopology(numCoresPerSocket, numThreadsPerCore, numTotalSockets);
|
||||
}
|
||||
|
||||
public void setBackingFileFormat(String volPath) {
|
||||
|
|
|
|||
|
|
@ -1716,6 +1716,7 @@ public class LibvirtVMDef {
|
|||
private String _model;
|
||||
private List<String> _features;
|
||||
private int _coresPerSocket = -1;
|
||||
private int _threadsPerCore = -1;
|
||||
private int _sockets = -1;
|
||||
|
||||
public void setMode(String mode) {
|
||||
|
|
@ -1732,8 +1733,9 @@ public class LibvirtVMDef {
|
|||
_model = model;
|
||||
}
|
||||
|
||||
public void setTopology(int coresPerSocket, int sockets) {
|
||||
public void setTopology(int coresPerSocket, int threadsPerCore, int sockets) {
|
||||
_coresPerSocket = coresPerSocket;
|
||||
_threadsPerCore = threadsPerCore;
|
||||
_sockets = sockets;
|
||||
}
|
||||
|
||||
|
|
@ -1762,9 +1764,9 @@ public class LibvirtVMDef {
|
|||
}
|
||||
}
|
||||
|
||||
// add topology
|
||||
if (_sockets > 0 && _coresPerSocket > 0) {
|
||||
modeBuilder.append("<topology sockets='" + _sockets + "' cores='" + _coresPerSocket + "' threads='1' />");
|
||||
// add topology. Note we require sockets, cores, and threads defined
|
||||
if (_sockets > 0 && _coresPerSocket > 0 && _threadsPerCore > 0) {
|
||||
modeBuilder.append("<topology sockets='" + _sockets + "' cores='" + _coresPerSocket + "' threads='" + _threadsPerCore + "' />");
|
||||
}
|
||||
|
||||
// close cpu def
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.cloud.hypervisor.kvm.resource;
|
||||
|
||||
import com.cloud.vm.VmDetailConstants;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RunWith(value = Parameterized.class)
|
||||
public class LibvirtCpuTopologyTest {
|
||||
private final LibvirtComputingResource libvirtComputingResource = Mockito.spy(new LibvirtComputingResource());
|
||||
|
||||
private final String desc;
|
||||
private final Integer coresPerSocket;
|
||||
private final Integer threadsPerCore;
|
||||
private final Integer totalVmCores;
|
||||
private final String expectedXml;
|
||||
|
||||
public LibvirtCpuTopologyTest(String desc, Integer coresPerSocket, Integer threadsPerCore, Integer totalVmCores, String expectedXml) {
|
||||
this.desc = desc;
|
||||
this.coresPerSocket = coresPerSocket;
|
||||
this.threadsPerCore = threadsPerCore;
|
||||
this.totalVmCores = totalVmCores;
|
||||
this.expectedXml = expectedXml;
|
||||
}
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> data() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
createTestData("8 cores, 2 per socket",2, null, 8, "<cpu><topology sockets='4' cores='2' threads='1' /></cpu>"),
|
||||
createTestData("8 cores, 4 per socket",4, null, 8, "<cpu><topology sockets='2' cores='4' threads='1' /></cpu>"),
|
||||
createTestData("8 cores, nothing specified",null, null, 8, "<cpu><topology sockets='2' cores='4' threads='1' /></cpu>"),
|
||||
createTestData("12 cores, nothing specified",null, null, 12, "<cpu><topology sockets='2' cores='6' threads='1' /></cpu>"),
|
||||
createTestData("8 cores, 2C per socket, 2TPC",2, 2, 8, "<cpu><topology sockets='2' cores='2' threads='2' /></cpu>"),
|
||||
createTestData("8 cores, 1C per socket, 2TPC",1, 2, 8, "<cpu><topology sockets='4' cores='1' threads='2' /></cpu>"),
|
||||
createTestData("8 cores, default CPS, 2TPC",null, 2, 8, "<cpu><topology sockets='4' cores='1' threads='2' /></cpu>"),
|
||||
createTestData("6 cores, default CPS, 2TPC",null, 2, 6, "<cpu><topology sockets='3' cores='1' threads='2' /></cpu>"),
|
||||
createTestData("12 cores, 2CPS, 2TPC",2, 2, 12, "<cpu><topology sockets='3' cores='2' threads='2' /></cpu>"),
|
||||
createTestData("6 cores, misconfigured cores, CPS, TPC, use default topology",2, 2, 6, "<cpu><topology sockets='1' cores='6' threads='1' /></cpu>"),
|
||||
createTestData("odd cores, nothing specified use default topology",null, null, 3, "<cpu><topology sockets='3' cores='1' threads='1' /></cpu>"),
|
||||
createTestData("odd cores, uneven CPS use default topology",2, null, 3, "<cpu><topology sockets='3' cores='1' threads='1' /></cpu>"),
|
||||
createTestData("8 cores, 2 CPS, odd threads use default topology", 2, 3, 8, "<cpu><topology sockets='2' cores='4' threads='1' /></cpu>"),
|
||||
createTestData("1 core, 2 CPS, odd threads use default topology", 2, 1, 1, "<cpu><topology sockets='1' cores='1' threads='1' /></cpu>")
|
||||
});
|
||||
}
|
||||
|
||||
private static Object[] createTestData(String desc, Integer coresPerSocket, Integer threadsPerCore, int totalVmCores, String expected) {
|
||||
return new Object[] {desc, coresPerSocket, threadsPerCore, totalVmCores, expected};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void topologyTest() {
|
||||
LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef();
|
||||
Map<String, String> details = new HashMap<>();
|
||||
|
||||
if (coresPerSocket != null) {
|
||||
details.put(VmDetailConstants.CPU_CORE_PER_SOCKET, coresPerSocket.toString());
|
||||
}
|
||||
|
||||
if (threadsPerCore != null) {
|
||||
details.put(VmDetailConstants.CPU_THREAD_PER_CORE, threadsPerCore.toString());
|
||||
}
|
||||
|
||||
if (coresPerSocket == null && threadsPerCore == null) {
|
||||
details = null;
|
||||
}
|
||||
|
||||
libvirtComputingResource.setCpuTopology(cpuModeDef, totalVmCores, details);
|
||||
Assert.assertEquals(desc, expectedXml, cpuModeDef.toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -506,4 +506,17 @@ public class LibvirtVMDefTest extends TestCase {
|
|||
"</controller>\n";
|
||||
assertEquals(expected, str);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTopology() {
|
||||
LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef();
|
||||
cpuModeDef.setTopology(2, 1, 4);
|
||||
assertEquals("<cpu><topology sockets='4' cores='2' threads='1' /></cpu>", cpuModeDef.toString());
|
||||
}
|
||||
|
||||
public void testTopologyNoInfo() {
|
||||
LibvirtVMDef.CpuModeDef cpuModeDef = new LibvirtVMDef.CpuModeDef();
|
||||
cpuModeDef.setTopology(-1, -1, 4);
|
||||
assertEquals("<cpu></cpu>", cpuModeDef.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4641,6 +4641,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
|
|||
options.put(VmDetailConstants.VIDEO_RAM, Collections.emptyList());
|
||||
options.put(VmDetailConstants.IO_POLICY, Arrays.asList("threads", "native", "io_uring", "storage_specific"));
|
||||
options.put(VmDetailConstants.IOTHREADS, Arrays.asList("enabled"));
|
||||
options.put(VmDetailConstants.CPU_THREAD_PER_CORE, Collections.emptyList());
|
||||
}
|
||||
|
||||
if (HypervisorType.VMware.equals(hypervisorType)) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue