Skip to content

Commit 40364da

Browse files
author
Pearl Dsilva
committed
add proper support for resizing of clvm_ng which calculated PE correctly for qcow2 metadata
1 parent c8c30e3 commit 40364da

4 files changed

Lines changed: 483 additions & 3 deletions

File tree

plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2537,11 +2537,12 @@ public String getResizeScriptType(final KVMStoragePool pool, final KVMPhysicalDi
25372537

25382538
if (pool.getType() == StoragePoolType.CLVM && volFormat == PhysicalDiskFormat.RAW) {
25392539
return "CLVM";
2540+
} else if (poolType == StoragePoolType.CLVM_NG) {
2541+
return "CLVM_NG";
25402542
} else if ((poolType == StoragePoolType.NetworkFilesystem
25412543
|| poolType == StoragePoolType.SharedMountPoint
25422544
|| poolType == StoragePoolType.Filesystem
2543-
|| poolType == StoragePoolType.Gluster
2544-
|| poolType == StoragePoolType.CLVM_NG)
2545+
|| poolType == StoragePoolType.Gluster)
25452546
&& volFormat == PhysicalDiskFormat.QCOW2 ) {
25462547
return "QCOW2";
25472548
} else if (poolType == StoragePoolType.Linstor) {

plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/driver/CloudStackPrimaryDataStoreDriverImpl.java

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,13 @@
9090
import com.cloud.vm.VirtualMachine;
9191
import com.cloud.vm.dao.VMInstanceDao;
9292

93+
import com.cloud.agent.AgentManager;
94+
import com.cloud.exception.AgentUnavailableException;
95+
import com.cloud.exception.OperationTimedoutException;
96+
import com.cloud.storage.clvm.ClvmPoolManager;
97+
import com.cloud.host.HostVO;
98+
import com.cloud.host.Status;
99+
93100
public class CloudStackPrimaryDataStoreDriverImpl implements PrimaryDataStoreDriver {
94101
@Override
95102
public Map<String, String> getCapabilities() {
@@ -133,6 +140,12 @@ public Map<String, String> getCapabilities() {
133140
@Inject
134141
private VolumeOrchestrationService volumeOrchestrationService;
135142

143+
@Inject
144+
private ClvmPoolManager clvmPoolManager;
145+
146+
@Inject
147+
private AgentManager agentMgr;
148+
136149
@Override
137150
public DataTO getTO(DataObject data) {
138151
return null;
@@ -463,6 +476,29 @@ public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> cal
463476

464477
CreateCmdResult result = new CreateCmdResult(null, null);
465478

479+
if (ClvmPoolManager.isClvmPoolType(pool.getPoolType())) {
480+
VirtualMachine attachedVm = vol.getAttachedVM();
481+
boolean vmIsRunning = attachedVm != null && attachedVm.getHostId() != null;
482+
if (!vmIsRunning) {
483+
Long lockHostId = clvmPoolManager.getClvmLockHostId(vol.getId(), vol.getUuid(),
484+
vol.getPath(), pool, true);
485+
if (lockHostId != null) {
486+
HostVO lockHost = hostDao.findById(lockHostId);
487+
if (lockHost != null && lockHost.getStatus() == Status.Up) {
488+
logger.debug("CLVM resize: routing to lock-holding host {} for volume {}",
489+
lockHostId, vol.getUuid());
490+
endpointsToRunResize = new long[]{lockHostId};
491+
} else {
492+
logger.warn("CLVM resize: lock host {} for volume {} is down or missing, " +
493+
"keeping caller-provided hosts", lockHostId, vol.getUuid());
494+
}
495+
} else {
496+
logger.warn("CLVM resize: no lock holder found for volume {}, " +
497+
"keeping caller-provided hosts or epSelector", vol.getUuid());
498+
}
499+
}
500+
}
501+
466502
// if hosts are provided, they are where the VM last ran. We can use that.
467503
if (endpointsToRunResize == null || endpointsToRunResize.length == 0) {
468504
EndPoint ep = epSelector.select(data, encryptionRequired);
@@ -481,7 +517,21 @@ public void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> cal
481517
resizeCmd.setContextParam(DiskTO.PROTOCOL_TYPE, Storage.StoragePoolType.DatastoreCluster.toString());
482518
}
483519
try {
484-
ResizeVolumeAnswer answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, endpointsToRunResize, resizeCmd);
520+
ResizeVolumeAnswer answer;
521+
if (ClvmPoolManager.isClvmPoolType(pool.getPoolType())) {
522+
// For CLVM, send only to the determined host, without falling through to other hosts
523+
try {
524+
answer = (ResizeVolumeAnswer) agentMgr.send(endpointsToRunResize[0], resizeCmd);
525+
} catch (AgentUnavailableException | OperationTimedoutException e) {
526+
logger.error("CLVM resize failed to reach host {} for volume {}: {}",
527+
endpointsToRunResize[0], vol.getUuid(), e.getMessage());
528+
result.setResult(e.getMessage());
529+
callback.complete(result);
530+
return;
531+
}
532+
} else {
533+
answer = (ResizeVolumeAnswer) storageMgr.sendToPool(pool, endpointsToRunResize, resizeCmd);
534+
}
485535
if (answer != null && answer.getResult()) {
486536
long finalSize = answer.getNewSize();
487537
logger.debug("Resize: volume started at size: " + toHumanReadableSize(vol.getSize()) + " and ended at size: " + toHumanReadableSize(finalSize));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,286 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package org.apache.cloudstack.storage.datastore.driver;
19+
20+
import static org.mockito.ArgumentMatchers.any;
21+
import static org.mockito.ArgumentMatchers.anyBoolean;
22+
import static org.mockito.ArgumentMatchers.anyLong;
23+
import static org.mockito.ArgumentMatchers.anyString;
24+
import static org.mockito.ArgumentMatchers.eq;
25+
import static org.mockito.Mockito.never;
26+
import static org.mockito.Mockito.verify;
27+
import static org.mockito.Mockito.when;
28+
import static org.mockito.Mockito.withSettings;
29+
30+
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
31+
import org.apache.cloudstack.engine.subsystem.api.storage.EndPoint;
32+
import org.apache.cloudstack.engine.subsystem.api.storage.EndPointSelector;
33+
import org.apache.cloudstack.framework.async.AsyncCompletionCallback;
34+
import org.apache.cloudstack.engine.subsystem.api.storage.CreateCmdResult;
35+
import org.apache.cloudstack.storage.volume.VolumeObject;
36+
import org.junit.Before;
37+
import org.junit.Test;
38+
import org.junit.runner.RunWith;
39+
import org.mockito.InjectMocks;
40+
import org.mockito.Mock;
41+
import org.mockito.Mockito;
42+
import org.mockito.junit.MockitoJUnitRunner;
43+
44+
import com.cloud.agent.AgentManager;
45+
import com.cloud.agent.api.Answer;
46+
import com.cloud.agent.api.Command;
47+
import com.cloud.agent.api.storage.ResizeVolumeAnswer;
48+
import com.cloud.exception.AgentUnavailableException;
49+
import com.cloud.exception.OperationTimedoutException;
50+
import com.cloud.host.HostVO;
51+
import com.cloud.host.Status;
52+
import com.cloud.host.dao.HostDao;
53+
import com.cloud.storage.ResizeVolumePayload;
54+
import com.cloud.storage.Storage;
55+
import com.cloud.storage.StorageManager;
56+
import com.cloud.storage.StoragePool;
57+
import com.cloud.storage.clvm.ClvmPoolManager;
58+
import com.cloud.vm.VirtualMachine;
59+
60+
@RunWith(MockitoJUnitRunner.class)
61+
public class CloudStackPrimaryDataStoreDriverImplTest {
62+
63+
@InjectMocks
64+
private CloudStackPrimaryDataStoreDriverImpl driver;
65+
66+
@Mock
67+
private ClvmPoolManager clvmPoolManager;
68+
@Mock
69+
private AgentManager agentMgr;
70+
@Mock
71+
private HostDao hostDao;
72+
@Mock
73+
private EndPointSelector epSelector;
74+
@Mock
75+
private StorageManager storageMgr;
76+
77+
@Mock
78+
private VolumeObject vol;
79+
private StoragePool pool;
80+
@Mock
81+
private HostVO lockHost;
82+
@Mock
83+
private AsyncCompletionCallback<CreateCmdResult> callback;
84+
85+
private static final long LOCK_HOST_ID = 42L;
86+
private static final long OTHER_HOST_ID = 99L;
87+
private static final String VOLUME_UUID = "test-vol-uuid";
88+
private static final String VOLUME_PATH = "vm-1-disk-0";
89+
90+
@Before
91+
public void setUp() {
92+
pool = Mockito.mock(StoragePool.class, withSettings().extraInterfaces(DataStore.class));
93+
94+
when(vol.getUuid()).thenReturn(VOLUME_UUID);
95+
when(vol.getId()).thenReturn(1L);
96+
when(vol.getPath()).thenReturn(VOLUME_PATH);
97+
when(vol.getSize()).thenReturn(10L * 1024 * 1024 * 1024);
98+
99+
when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM_NG);
100+
when(pool.getParent()).thenReturn(0L);
101+
when(pool.getPath()).thenReturn("/vg-test");
102+
103+
when(vol.getDataStore()).thenReturn((DataStore) pool);
104+
105+
when(hostDao.findById(LOCK_HOST_ID)).thenReturn(lockHost);
106+
when(lockHost.getStatus()).thenReturn(Status.Up);
107+
}
108+
109+
private ResizeVolumeAnswer mockSuccessAnswer(long newSize) throws Exception {
110+
ResizeVolumeAnswer answer = Mockito.mock(ResizeVolumeAnswer.class);
111+
when(answer.getResult()).thenReturn(true);
112+
when(answer.getNewSize()).thenReturn(newSize);
113+
return answer;
114+
}
115+
116+
private void stubAgentSend(long hostId, Answer answer) throws Exception {
117+
Mockito.doReturn(answer).when(agentMgr).send(eq(hostId), any(Command.class));
118+
}
119+
120+
private void verifyAgentSend(long hostId) throws Exception {
121+
verify(agentMgr).send(eq(hostId), any(Command.class));
122+
}
123+
124+
private void verifyAgentSendNever(long hostId) throws Exception {
125+
verify(agentMgr, never()).send(eq(hostId), any(Command.class));
126+
}
127+
128+
private ResizeVolumePayload makePayload(long[] hosts) {
129+
ResizeVolumePayload p = new ResizeVolumePayload(
130+
20L * 1024 * 1024 * 1024, null, null, null, false, "test-vm", hosts, false);
131+
when(vol.getpayload()).thenReturn(p);
132+
return p;
133+
}
134+
135+
@Test
136+
public void testResize_clvmNg_vmRunning_usesCallerHost() throws Exception {
137+
VirtualMachine runningVm = Mockito.mock(VirtualMachine.class);
138+
when(runningVm.getHostId()).thenReturn(OTHER_HOST_ID);
139+
when(vol.getAttachedVM()).thenReturn(runningVm);
140+
makePayload(new long[]{OTHER_HOST_ID});
141+
142+
stubAgentSend(OTHER_HOST_ID, mockSuccessAnswer(20L * 1024 * 1024 * 1024));
143+
144+
driver.resize(vol, callback);
145+
146+
verify(clvmPoolManager, never()).getClvmLockHostId(anyLong(), anyString(), anyString(), any(), anyBoolean());
147+
verifyAgentSend(OTHER_HOST_ID);
148+
}
149+
150+
@Test
151+
public void testResize_clvmNg_vmStopped_routesToLockHost() throws Exception {
152+
VirtualMachine stoppedVm = Mockito.mock(VirtualMachine.class);
153+
when(stoppedVm.getHostId()).thenReturn(null);
154+
when(vol.getAttachedVM()).thenReturn(stoppedVm);
155+
makePayload(new long[]{OTHER_HOST_ID}); // stale lastHostId from caller
156+
157+
when(clvmPoolManager.getClvmLockHostId(anyLong(), anyString(), anyString(), any(), eq(true)))
158+
.thenReturn(LOCK_HOST_ID);
159+
stubAgentSend(LOCK_HOST_ID, mockSuccessAnswer(20L * 1024 * 1024 * 1024));
160+
161+
driver.resize(vol, callback);
162+
163+
// Must override stale lastHostId with actual lock host
164+
verifyAgentSend(LOCK_HOST_ID);
165+
verifyAgentSendNever(OTHER_HOST_ID);
166+
}
167+
168+
@Test
169+
public void testResize_clvmNg_vmStopped_lockHostDown_keepsCallerHosts() throws Exception {
170+
VirtualMachine stoppedVm = Mockito.mock(VirtualMachine.class);
171+
when(stoppedVm.getHostId()).thenReturn(null);
172+
when(vol.getAttachedVM()).thenReturn(stoppedVm);
173+
makePayload(new long[]{OTHER_HOST_ID});
174+
175+
when(clvmPoolManager.getClvmLockHostId(anyLong(), anyString(), anyString(), any(), eq(true)))
176+
.thenReturn(LOCK_HOST_ID);
177+
when(lockHost.getStatus()).thenReturn(Status.Disconnected);
178+
stubAgentSend(OTHER_HOST_ID, mockSuccessAnswer(20L * 1024 * 1024 * 1024));
179+
180+
driver.resize(vol, callback);
181+
182+
verifyAgentSend(OTHER_HOST_ID);
183+
}
184+
185+
@Test
186+
public void testResize_clvmNg_detached_routesToLockHost() throws Exception {
187+
when(vol.getAttachedVM()).thenReturn(null);
188+
makePayload(null); // no hosts from caller
189+
190+
when(clvmPoolManager.getClvmLockHostId(anyLong(), anyString(), anyString(), any(), eq(true)))
191+
.thenReturn(LOCK_HOST_ID);
192+
stubAgentSend(LOCK_HOST_ID, mockSuccessAnswer(20L * 1024 * 1024 * 1024));
193+
194+
driver.resize(vol, callback);
195+
196+
verifyAgentSend(LOCK_HOST_ID);
197+
}
198+
199+
@Test
200+
public void testResize_clvmNg_detached_noLockHolder_fallsToEpSelector() throws Exception {
201+
when(vol.getAttachedVM()).thenReturn(null);
202+
makePayload(null);
203+
204+
when(clvmPoolManager.getClvmLockHostId(anyLong(), anyString(), anyString(), any(), eq(true)))
205+
.thenReturn(null); // no lock holder found
206+
207+
EndPoint ep = Mockito.mock(EndPoint.class);
208+
when(ep.getId()).thenReturn(LOCK_HOST_ID);
209+
when(epSelector.select(any(), anyBoolean())).thenReturn(ep);
210+
stubAgentSend(LOCK_HOST_ID, mockSuccessAnswer(20L * 1024 * 1024 * 1024));
211+
212+
driver.resize(vol, callback);
213+
214+
verify(epSelector).select(any(), anyBoolean());
215+
verifyAgentSend(LOCK_HOST_ID);
216+
}
217+
218+
@Test
219+
public void testResize_clvmNg_agentUnavailable_failsFast() throws Exception {
220+
VirtualMachine runningVm = Mockito.mock(VirtualMachine.class);
221+
when(runningVm.getHostId()).thenReturn(OTHER_HOST_ID);
222+
when(vol.getAttachedVM()).thenReturn(runningVm);
223+
makePayload(new long[]{OTHER_HOST_ID});
224+
225+
Mockito.doThrow(new AgentUnavailableException("host down", OTHER_HOST_ID))
226+
.when(agentMgr).send(eq(OTHER_HOST_ID), any(Command.class));
227+
228+
driver.resize(vol, callback);
229+
230+
verify(callback).complete(any());
231+
verify(storageMgr, never()).sendToPool(any(StoragePool.class), any(long[].class), any());
232+
}
233+
234+
@Test
235+
public void testResize_clvmNg_operationTimedOut_failsFast() throws Exception {
236+
VirtualMachine runningVm = Mockito.mock(VirtualMachine.class);
237+
when(runningVm.getHostId()).thenReturn(OTHER_HOST_ID);
238+
when(vol.getAttachedVM()).thenReturn(runningVm);
239+
makePayload(new long[]{OTHER_HOST_ID});
240+
241+
Mockito.doThrow(new OperationTimedoutException(null, OTHER_HOST_ID, 0, 0, false))
242+
.when(agentMgr).send(eq(OTHER_HOST_ID), any(Command.class));
243+
244+
driver.resize(vol, callback);
245+
246+
verify(callback).complete(any());
247+
verify(storageMgr, never()).sendToPool(any(StoragePool.class), any(long[].class), any());
248+
}
249+
250+
@Test
251+
public void testResize_nonClvm_usesSendToPool() throws Exception {
252+
when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
253+
makePayload(null);
254+
255+
EndPoint ep = Mockito.mock(EndPoint.class);
256+
when(ep.getId()).thenReturn(OTHER_HOST_ID);
257+
when(epSelector.select(any(), anyBoolean())).thenReturn(ep);
258+
259+
ResizeVolumeAnswer answer = mockSuccessAnswer(20L * 1024 * 1024 * 1024);
260+
when(storageMgr.sendToPool(any(StoragePool.class), any(long[].class), any())).thenReturn(answer);
261+
262+
driver.resize(vol, callback);
263+
264+
verify(storageMgr).sendToPool(any(StoragePool.class), any(long[].class), any());
265+
verify(agentMgr, never()).send(anyLong(), any(Command.class));
266+
verify(clvmPoolManager, never()).getClvmLockHostId(anyLong(), anyString(), anyString(), any(), anyBoolean());
267+
}
268+
269+
@Test
270+
public void testResize_clvmLegacy_vmStopped_routesToLockHost() throws Exception {
271+
when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.CLVM);
272+
VirtualMachine stoppedVm = Mockito.mock(VirtualMachine.class);
273+
when(stoppedVm.getHostId()).thenReturn(null);
274+
when(vol.getAttachedVM()).thenReturn(stoppedVm);
275+
makePayload(new long[]{OTHER_HOST_ID});
276+
277+
when(clvmPoolManager.getClvmLockHostId(anyLong(), anyString(), anyString(), any(), eq(true)))
278+
.thenReturn(LOCK_HOST_ID);
279+
stubAgentSend(LOCK_HOST_ID, mockSuccessAnswer(20L * 1024 * 1024 * 1024));
280+
281+
driver.resize(vol, callback);
282+
283+
verifyAgentSend(LOCK_HOST_ID);
284+
verifyAgentSendNever(OTHER_HOST_ID);
285+
}
286+
}

0 commit comments

Comments
 (0)