Skip to content

Commit 70d43cd

Browse files
committed
linstor: use the target host's CPU shares for live migration with storage
LinstorDataMotionStrategy set the MigrateCommand CPU shares to the raw cpus * speed value, which is only valid on cgroup v1. On cgroup v2 hosts the source agent's updateVmSharesIfNeeded() sees that it differs from its own scaled value and writes it into the migration XML, so libvirt on the target rejects VMs above 10000 with "shares '<n>' must be in range [1, 10000]", and smaller VMs end up with an unscaled CPU weight. Use the value the target host calculated and returned in the PrepareForMigrationAnswer, as StorageSystemDataMotionStrategy and VirtualMachineManagerImpl do. A unit test checks that the target host's value is sent, not cpus * speed.
1 parent 9c70c3d commit 70d43cd

3 files changed

Lines changed: 24 additions & 4 deletions

File tree

‎plugins/storage/volume/linstor/CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3131
- Live migration with storage into Linstor failed with "cannot precreate storage for disk type 'block'"
3232
if the auto-placement didn't put a copy of the new resource on the target host; the resource is
3333
now made available on the target host before the migration starts.
34+
- Live migration with storage into Linstor failed on cgroup v2 hosts with
35+
"shares '<n>' must be in range [1, 10000]" for VMs with more than 10000 cpus * MHz, and smaller
36+
VMs ended up with an unscaled CPU weight; the CPU shares calculated by the target host are now used.
3437

3538
## [2026-06-24]
3639

‎plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/motion/LinstorDataMotionStrategy.java‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.cloud.agent.api.Answer;
3939
import com.cloud.agent.api.MigrateAnswer;
4040
import com.cloud.agent.api.MigrateCommand;
41+
import com.cloud.agent.api.PrepareForMigrationAnswer;
4142
import com.cloud.agent.api.PrepareForMigrationCommand;
4243
import com.cloud.agent.api.to.DataObjectType;
4344
import com.cloud.agent.api.to.VirtualMachineTO;
@@ -75,7 +76,6 @@
7576
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
7677
import org.apache.commons.collections.CollectionUtils;
7778
import org.apache.commons.collections.MapUtils;
78-
import org.apache.commons.lang3.ObjectUtils;
7979
import org.apache.logging.log4j.LogManager;
8080
import org.apache.logging.log4j.Logger;
8181
import org.springframework.stereotype.Component;
@@ -407,8 +407,9 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
407407
}
408408

409409
PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(vmTO);
410+
Answer pfma;
410411
try {
411-
Answer pfma = _agentManager.send(destHost.getId(), pfmc);
412+
pfma = _agentManager.send(destHost.getId(), pfmc);
412413

413414
if (pfma == null || !pfma.getResult()) {
414415
String details = pfma != null ? pfma.getDetails() : "null answer returned";
@@ -430,8 +431,12 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
430431
migrateCommand.setWait(StorageManager.KvmStorageOnlineMigrationWait.value());
431432
migrateCommand.setMigrateStorage(migrateStorage);
432433
migrateCommand.setMigrateStorageManaged(true);
433-
migrateCommand.setNewVmCpuShares(
434-
vmTO.getCpus() * ObjectUtils.defaultIfNull(vmTO.getMinSpeed(), vmTO.getSpeed()));
434+
// the target host scales the shares to its cgroup version (v2 only accepts 1-10000),
435+
// the raw cpus * speed value is only valid on cgroup v1
436+
Integer newVmCpuShares = ((PrepareForMigrationAnswer) pfma).getNewVmCpuShares();
437+
if (newVmCpuShares != null) {
438+
migrateCommand.setNewVmCpuShares(newVmCpuShares);
439+
}
435440
migrateCommand.setMigrateDiskInfoList(migrateDiskInfoList);
436441

437442
boolean kvmAutoConvergence = StorageManager.KvmAutoConvergence.value();

‎plugins/storage/volume/linstor/src/test/java/org/apache/cloudstack/storage/motion/LinstorDataMotionStrategyTest.java‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ public void setUp() throws Exception {
140140
when(destHost.getName()).thenReturn(DEST_HOST_NAME);
141141
when(destHost.getPrivateIpAddress()).thenReturn("192.168.0.3");
142142

143+
// 2 vCPUs * 5001 MHz: raw shares 10002, more than cgroup v2 accepts
143144
vmTO = mock(VirtualMachineTO.class);
144145
when(vmTO.getId()).thenReturn(100L);
145146
when(vmTO.getName()).thenReturn("i-2-100-VM");
@@ -249,6 +250,17 @@ public void copyAsyncMakesNewResourceAvailableOnTargetHostBeforeMigrating() thro
249250
Assert.assertTrue(completedResult().isSuccess());
250251
}
251252

253+
@Test
254+
public void copyAsyncUsesCpuSharesCalculatedByTargetHost() throws Exception {
255+
prepareForMigrationAnswers(8335);
256+
migrateSucceeds();
257+
258+
strategy.copyAsync(volumeMap(), vmTO, srcHost, destHost, callback);
259+
260+
// not the raw cpus * speed (10002), which cgroup v2 hosts reject
261+
Assert.assertEquals(8335, sentMigrateCommand().getNewVmCpuShares());
262+
}
263+
252264
@Test
253265
public void copyAsyncRemovesDestinationVolumeWhenMakeAvailableFails() throws Exception {
254266
when(api.resourceMakeAvailableOnNode(eq(DEST_RSC_NAME), eq(DEST_HOST_NAME), any(ResourceMakeAvailable.class)))

0 commit comments

Comments
 (0)