Skip to content

Commit bc12833

Browse files
server: Failed to scale between Service Offerings with the same root disk size (#5095)
* Cover a case where resizing root disk failed; add isNotPossibleToResize method. * remove format from resize validation * Revert if-conditional changes that removed ImageFormat.ISO validation * Add JUnit tests for VolumeApiServiceImpl.isNotPossibleToResize * Fix checkstyle of test Class * Use _templateDao.findByIdIncludingRemoved instead of _templateDao.findById * Prevent null serviceOfferingView and Mock findByIdIncludingRemoved instead of findById
1 parent 9dd0acf commit bc12833

2 files changed

Lines changed: 86 additions & 15 deletions

File tree

server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
import javax.inject.Inject;
3333

34+
import com.cloud.api.query.dao.ServiceOfferingJoinDao;
35+
import com.cloud.api.query.vo.ServiceOfferingJoinVO;
3436
import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd;
3537
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
3638
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
@@ -218,6 +220,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
218220
@Inject
219221
private ServiceOfferingDetailsDao _serviceOfferingDetailsDao;
220222
@Inject
223+
private ServiceOfferingJoinDao serviceOfferingJoinDao;
224+
@Inject
221225
private UserVmDao _userVmDao;
222226
@Inject
223227
private UserVmDetailsDao userVmDetailsDao;
@@ -920,20 +924,7 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep
920924
}
921925

922926
// if we are to use the existing disk offering
923-
ImageFormat format = null;
924927
if (newDiskOffering == null) {
925-
Long templateId = volume.getTemplateId();
926-
if (templateId != null) {
927-
VMTemplateVO template = _templateDao.findById(templateId);
928-
format = template.getFormat();
929-
}
930-
931-
if (volume.getVolumeType().equals(Volume.Type.ROOT) && diskOffering.getDiskSize() > 0 && format != null && format != ImageFormat.ISO) {
932-
throw new InvalidParameterValueException(
933-
"Failed to resize Root volume. The service offering of this Volume has been configured with a root disk size; "
934-
+ "on such case a Root Volume can only be resized when changing to another Service Offering with a Root disk size. "
935-
+ "For more details please check out the Official Resizing Volumes documentation.");
936-
}
937928
newSize = cmd.getSize();
938929
newHypervisorSnapshotReserve = volume.getHypervisorSnapshotReserve();
939930

@@ -944,6 +935,13 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep
944935
+ "customizable or it must be a root volume (if providing a disk offering, make sure it is different from the current disk offering).");
945936
}
946937

938+
if (isNotPossibleToResize(volume, diskOffering)) {
939+
throw new InvalidParameterValueException(
940+
"Failed to resize Root volume. The service offering of this Volume has been configured with a root disk size; "
941+
+ "on such case a Root Volume can only be resized when changing to another Service Offering with a Root disk size. "
942+
+ "For more details please check out the Official Resizing Volumes documentation.");
943+
}
944+
947945
// convert from bytes to GiB
948946
newSize = newSize << 30;
949947
} else {
@@ -1167,6 +1165,27 @@ public VolumeVO resizeVolume(ResizeVolumeCmd cmd) throws ResourceAllocationExcep
11671165
shrinkOk);
11681166
}
11691167

1168+
/**
1169+
* A volume should not be resized if it covers ALL the following scenarios: <br>
1170+
* 1 - Root volume <br>
1171+
* 2 - && Current Disk Offering enforces a root disk size (in this case one can resize only by changing the Service Offering)
1172+
*/
1173+
protected boolean isNotPossibleToResize(VolumeVO volume, DiskOfferingVO diskOffering) {
1174+
Long templateId = volume.getTemplateId();
1175+
ImageFormat format = null;
1176+
if (templateId != null) {
1177+
VMTemplateVO template = _templateDao.findByIdIncludingRemoved(templateId);
1178+
format = template.getFormat();
1179+
}
1180+
boolean isNotIso = format != null && format != ImageFormat.ISO;
1181+
boolean isRoot = Volume.Type.ROOT.equals(volume.getVolumeType());
1182+
1183+
ServiceOfferingJoinVO serviceOfferingView = serviceOfferingJoinDao.findById(diskOffering.getId());
1184+
boolean isOfferingEnforcingRootDiskSize = serviceOfferingView != null && serviceOfferingView.getRootDiskSize() > 0;
1185+
1186+
return isOfferingEnforcingRootDiskSize && isRoot && isNotIso;
1187+
}
1188+
11701189
private void checkIfVolumeIsRootAndVmIsRunning(Long newSize, VolumeVO volume, VMInstanceVO vmInstanceVO) {
11711190
if (!volume.getSize().equals(newSize) && volume.getVolumeType().equals(Volume.Type.ROOT) && !State.Stopped.equals(vmInstanceVO.getState())) {
11721191
throw new InvalidParameterValueException(String.format("Cannot resize ROOT volume [%s] when VM is not on Stopped State. VM %s is in state %s", volume.getName(), vmInstanceVO

server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@
3535
import java.util.UUID;
3636
import java.util.concurrent.ExecutionException;
3737

38+
import com.cloud.api.query.dao.ServiceOfferingJoinDao;
39+
import com.cloud.api.query.vo.ServiceOfferingJoinVO;
40+
import com.cloud.storage.dao.VMTemplateDao;
3841
import org.apache.cloudstack.acl.ControlledEntity;
3942
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
4043
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
@@ -78,7 +81,6 @@
7881
import com.cloud.exception.ResourceAllocationException;
7982
import com.cloud.host.dao.HostDao;
8083
import com.cloud.hypervisor.Hypervisor.HypervisorType;
81-
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
8284
import com.cloud.org.Grouping;
8385
import com.cloud.serializer.GsonHelper;
8486
import com.cloud.server.TaggedResourceService;
@@ -153,7 +155,9 @@ public class VolumeApiServiceImplTest {
153155
@Mock
154156
private StoragePoolTagsDao storagePoolTagsDao;
155157
@Mock
156-
private HypervisorCapabilitiesDao hypervisorCapabilitiesDao;
158+
private VMTemplateDao templateDao;
159+
@Mock
160+
private ServiceOfferingJoinDao serviceOfferingJoinDao;
157161

158162
private DetachVolumeCmd detachCmd = new DetachVolumeCmd();
159163
private Class<?> _detachCmdClass = detachCmd.getClass();
@@ -1079,4 +1083,52 @@ public void doesTargetStorageSupportDiskOfferingTestDiskOfferingTagsEqualsStorag
10791083

10801084
Assert.assertTrue(result);
10811085
}
1086+
1087+
@Test
1088+
public void isNotPossibleToResizeTestAllFormats() {
1089+
Storage.ImageFormat[] imageFormat = Storage.ImageFormat.values();
1090+
for (int i = 0; i < imageFormat.length - 1; i++) {
1091+
if (imageFormat[i] != Storage.ImageFormat.ISO) {
1092+
prepareAndRunTestOfIsNotPossibleToResize(Type.ROOT, 10l, imageFormat[i], true);
1093+
} else {
1094+
prepareAndRunTestOfIsNotPossibleToResize(Type.ROOT, 10l, imageFormat[i], false);
1095+
}
1096+
}
1097+
}
1098+
1099+
@Test
1100+
public void isNotPossibleToResizeTestAllTypes() {
1101+
Type[] types = Type.values();
1102+
for (int i = 0; i < types.length - 1; i++) {
1103+
if (types[i] != Type.ROOT) {
1104+
prepareAndRunTestOfIsNotPossibleToResize(types[i], 10l, Storage.ImageFormat.QCOW2, false);
1105+
} else {
1106+
prepareAndRunTestOfIsNotPossibleToResize(types[i], 10l, Storage.ImageFormat.QCOW2, true);
1107+
}
1108+
}
1109+
}
1110+
1111+
@Test
1112+
public void isNotPossibleToResizeTestNoRootDiskSize() {
1113+
prepareAndRunTestOfIsNotPossibleToResize(Type.ROOT, 0l, Storage.ImageFormat.QCOW2, false);
1114+
}
1115+
1116+
private void prepareAndRunTestOfIsNotPossibleToResize(Type volumeType, Long rootDisk, Storage.ImageFormat imageFormat, boolean expectedIsNotPossibleToResize) {
1117+
VolumeVO volume = Mockito.mock(VolumeVO.class);
1118+
when(volume.getVolumeType()).thenReturn(volumeType);
1119+
1120+
when(volume.getTemplateId()).thenReturn(1l);
1121+
DiskOfferingVO diskOffering = Mockito.mock(DiskOfferingVO.class);
1122+
1123+
ServiceOfferingJoinVO serviceOfferingJoinVO = Mockito.mock(ServiceOfferingJoinVO.class);
1124+
when(serviceOfferingJoinVO.getRootDiskSize()).thenReturn(rootDisk);
1125+
when(serviceOfferingJoinDao.findById(Mockito.anyLong())).thenReturn(serviceOfferingJoinVO);
1126+
1127+
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
1128+
when(template.getFormat()).thenReturn(imageFormat);
1129+
when(templateDao.findByIdIncludingRemoved(Mockito.anyLong())).thenReturn(template);
1130+
1131+
boolean result = volumeApiServiceImpl.isNotPossibleToResize(volume, diskOffering);
1132+
Assert.assertEquals(expectedIsNotPossibleToResize, result);
1133+
}
10821134
}

0 commit comments

Comments
 (0)