Skip to content

Commit 44e85c5

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 0fa85dc commit 44e85c5

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-07-30]
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
@@ -40,6 +40,7 @@
4040
import com.cloud.agent.api.CheckVirtualMachineCommand;
4141
import com.cloud.agent.api.MigrateAnswer;
4242
import com.cloud.agent.api.MigrateCommand;
43+
import com.cloud.agent.api.PrepareForMigrationAnswer;
4344
import com.cloud.agent.api.PrepareForMigrationCommand;
4445
import com.cloud.agent.api.to.DataObjectType;
4546
import com.cloud.agent.api.to.VirtualMachineTO;
@@ -78,7 +79,6 @@
7879
import org.apache.cloudstack.storage.datastore.util.LinstorUtil;
7980
import org.apache.commons.collections.CollectionUtils;
8081
import org.apache.commons.collections.MapUtils;
81-
import org.apache.commons.lang3.ObjectUtils;
8282
import org.apache.logging.log4j.LogManager;
8383
import org.apache.logging.log4j.Logger;
8484
import org.springframework.stereotype.Component;
@@ -426,8 +426,9 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
426426
}
427427

428428
PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(vmTO);
429+
Answer pfma;
429430
try {
430-
Answer pfma = _agentManager.send(destHost.getId(), pfmc);
431+
pfma = _agentManager.send(destHost.getId(), pfmc);
431432

432433
if (pfma == null || !pfma.getResult()) {
433434
String details = pfma != null ? pfma.getDetails() : "null answer returned";
@@ -449,8 +450,12 @@ public void copyAsync(Map<VolumeInfo, DataStore> volumeDataStoreMap, VirtualMach
449450
migrateCommand.setWait(StorageManager.KvmStorageOnlineMigrationWait.value());
450451
migrateCommand.setMigrateStorage(migrateStorage);
451452
migrateCommand.setMigrateStorageManaged(true);
452-
migrateCommand.setNewVmCpuShares(
453-
vmTO.getCpus() * ObjectUtils.defaultIfNull(vmTO.getMinSpeed(), vmTO.getSpeed()));
453+
// the target host scales the shares to its cgroup version (v2 only accepts 1-10000),
454+
// the raw cpus * speed value is only valid on cgroup v1
455+
Integer newVmCpuShares = ((PrepareForMigrationAnswer) pfma).getNewVmCpuShares();
456+
if (newVmCpuShares != null) {
457+
migrateCommand.setNewVmCpuShares(newVmCpuShares);
458+
}
454459
migrateCommand.setMigrateDiskInfoList(migrateDiskInfoList);
455460

456461
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
@@ -143,6 +143,7 @@ public void setUp() throws Exception {
143143
when(destHost.getName()).thenReturn(DEST_HOST_NAME);
144144
when(destHost.getPrivateIpAddress()).thenReturn("192.168.0.3");
145145

146+
// 2 vCPUs * 5001 MHz: raw shares 10002, more than cgroup v2 accepts
146147
vmTO = mock(VirtualMachineTO.class);
147148
when(vmTO.getId()).thenReturn(100L);
148149
when(vmTO.getName()).thenReturn("i-2-100-VM");
@@ -252,6 +253,17 @@ public void copyAsyncMakesNewResourceAvailableOnTargetHostBeforeMigrating() thro
252253
Assert.assertTrue(completedResult().isSuccess());
253254
}
254255

256+
@Test
257+
public void copyAsyncUsesCpuSharesCalculatedByTargetHost() throws Exception {
258+
prepareForMigrationAnswers(8335);
259+
migrateSucceeds();
260+
261+
strategy.copyAsync(volumeMap(), vmTO, srcHost, destHost, callback);
262+
263+
// not the raw cpus * speed (10002), which cgroup v2 hosts reject
264+
Assert.assertEquals(8335, sentMigrateCommand().getNewVmCpuShares());
265+
}
266+
255267
@Test
256268
public void copyAsyncRemovesDestinationVolumeWhenCreateResourceFails() throws Exception {
257269
linstorUtil.when(() -> LinstorUtil.createResource(any(), any(), any(), anyBoolean()))

0 commit comments

Comments
 (0)