Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/src/main/java/com/cloud/storage/Volume.java
Original file line number Diff line number Diff line change
Expand Up @@ -279,5 +279,7 @@ enum Event {

void setEncryptFormat(String encryptFormat);

boolean isEncrypted();

boolean isDeleteProtection();
}
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ public VolumeInfo moveVolume(VolumeInfo volumeInfo, long destPoolDcId, Long dest
// Find a destination storage pool with the specified criteria
DiskOffering diskOffering = _entityMgr.findById(DiskOffering.class, volumeInfo.getDiskOfferingId());
DiskProfile dskCh = new DiskProfile(volumeInfo.getId(), volumeInfo.getVolumeType(), volumeInfo.getName(), diskOffering.getId(), diskOffering.getDiskSize(), diskOffering.getTagsArray(),
diskOffering.isUseLocalStorage(), diskOffering.isRecreatable(), null, (diskOffering.getEncrypt() || volumeInfo.getPassphraseId() != null));
diskOffering.isUseLocalStorage(), diskOffering.isRecreatable(), null, (diskOffering.getEncrypt() || volumeInfo.isEncrypted()));

dskCh.setHyperType(dataDiskHyperType);
storageMgr.setDiskProfileThrottling(dskCh, null, diskOffering);
Expand Down Expand Up @@ -352,7 +352,7 @@ public VolumeVO allocateDuplicateVolumeVO(Volume oldVol, DiskOffering diskOfferi
newVol.setInstanceId(oldVol.getInstanceId());
newVol.setRecreatable(oldVol.isRecreatable());
newVol.setFormat(oldVol.getFormat());
if ((diskOffering == null || diskOffering.getEncrypt()) && oldVol.getPassphraseId() != null) {
if ((diskOffering == null || diskOffering.getEncrypt()) && oldVol.isEncrypted()) {
PassphraseVO passphrase = passphraseDao.persist(new PassphraseVO(true));
newVol.setPassphraseId(passphrase.getId());
}
Expand Down Expand Up @@ -646,7 +646,7 @@ public VolumeInfo createVolumeFromSnapshot(Volume volume, Snapshot snapshot, Use
}

protected DiskProfile createDiskCharacteristics(VolumeInfo volumeInfo, VirtualMachineTemplate template, DataCenter dc, DiskOffering diskOffering) {
boolean requiresEncryption = diskOffering.getEncrypt() || volumeInfo.getPassphraseId() != null;
boolean requiresEncryption = diskOffering.getEncrypt() || volumeInfo.isEncrypted();
if (volumeInfo.getVolumeType() == Type.ROOT && Storage.ImageFormat.ISO != template.getFormat()) {
String templateToString = getReflectOnlySelectedFields(template);
String zoneToString = getReflectOnlySelectedFields(dc);
Expand Down Expand Up @@ -1905,7 +1905,7 @@ private Pair<VolumeVO, DataStore> recreateVolume(VolumeVO vol, VirtualMachinePro
}

private VolumeVO setPassphraseForVolumeEncryption(VolumeVO volume) {
if (volume.getPassphraseId() != null) {
if (volume.isEncrypted()) {
return volume;
}
logger.debug("Creating passphrase for the volume: " + volume.getName());
Expand Down Expand Up @@ -1942,7 +1942,7 @@ protected void updateVolumeSize(DataStore store, VolumeVO vol) throws ResourceAl
PrimaryDataStoreDriver driver = (PrimaryDataStoreDriver) store.getDriver();
long newSize = driver.getVolumeSizeRequiredOnPool(vol.getSize(),
template == null ? null : template.getSize(),
vol.getPassphraseId() != null);
vol.isEncrypted());

if (newSize == vol.getSize()) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,7 @@ public void testAllocateDuplicateVolumeVOBasic() {
Mockito.when(oldVol.getInstanceId()).thenReturn(6L);
Mockito.when(oldVol.isRecreatable()).thenReturn(false);
Mockito.when(oldVol.getFormat()).thenReturn(Storage.ImageFormat.QCOW2);
Mockito.when(oldVol.getPassphraseId()).thenReturn(null); // no encryption
Mockito.when(oldVol.isEncrypted()).thenReturn(false); // no encryption

VolumeVO persistedVol = Mockito.mock(VolumeVO.class);
Mockito.when(volumeDao.persist(Mockito.any(VolumeVO.class))).thenReturn(persistedVol);
Expand Down Expand Up @@ -303,7 +303,7 @@ public void testAllocateDuplicateVolumeVOWithEncryption() {
Mockito.when(oldVol.getInstanceId()).thenReturn(7L);
Mockito.when(oldVol.isRecreatable()).thenReturn(true);
Mockito.when(oldVol.getFormat()).thenReturn(Storage.ImageFormat.RAW);
Mockito.when(oldVol.getPassphraseId()).thenReturn(42L);
Mockito.when(oldVol.isEncrypted()).thenReturn(true);

PassphraseVO passphrase = Mockito.mock(PassphraseVO.class);
Mockito.when(passphrase.getId()).thenReturn(999L);
Expand Down Expand Up @@ -336,9 +336,6 @@ public void testAllocateDuplicateVolumeVOWithTemplateOverride() {
VolumeVO persistedVol = Mockito.mock(VolumeVO.class);
Mockito.when(volumeDao.persist(Mockito.any())).thenReturn(persistedVol);

PassphraseVO mockPassPhrase = Mockito.mock(PassphraseVO.class);
Mockito.when(passphraseDao.persist(Mockito.any())).thenReturn(mockPassPhrase);

VolumeVO result = volumeOrchestrator.allocateDuplicateVolumeVO(oldVol, null, 222L);
assertNotNull(result);
}
Expand Down
5 changes: 5 additions & 0 deletions engine/schema/src/main/java/com/cloud/storage/VolumeVO.java
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,11 @@ public void setExternalUuid(String externalUuid) {

public void setEncryptFormat(String encryptFormat) { this.encryptFormat = encryptFormat; }

@Override
public boolean isEncrypted() {
return this.passphraseId != null || this.encryptFormat != null;
}

@Override
public boolean isDeleteProtection() {
return deleteProtection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ public StrategyPriority canHandle(Snapshot snapshot, Long zoneId, SnapshotOperat
return StrategyPriority.CANT_HANDLE;
}
if (zoneId != null && SnapshotOperation.DELETE.equals(op)) {
logger.debug(String.format("canHandle for zone ID: %d, operation: %s - %s", zoneId, op, StrategyPriority.DEFAULT));
logger.debug("canHandle for zone ID: {}, operation: {} - {}", zoneId, op, StrategyPriority.DEFAULT);
}
return StrategyPriority.DEFAULT;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,6 @@ public SnapshotInfo getReadySnapshotOnCache(long snapshotId) {
} else {
return null;
}

}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -324,9 +324,13 @@ public StrategyPriority canHandle(Long vmId, Long rootPoolId, boolean snapshotMe

List<VolumeVO> volumes = volumeDao.findByInstance(vmId);
for (VolumeVO volume : volumes) {
if (volume.isEncrypted()) {
logger.debug("{} as the VM has a volume that is encrypted.", cantHandleLog);
return StrategyPriority.CANT_HANDLE;
}
StoragePoolVO storagePoolVO = storagePool.findById(volume.getPoolId());
if (!supportedStoragePoolTypes.contains(storagePoolVO.getPoolType())) {
logger.debug(String.format("%s as the VM has a volume that is in a storage with unsupported type [%s].", cantHandleLog, storagePoolVO.getPoolType()));
logger.debug("{} as the VM has a volume that is in a storage with unsupported type [{}].", cantHandleLog, storagePoolVO.getPoolType());
return StrategyPriority.CANT_HANDLE;
}
List<SnapshotVO> snapshots = snapshotDao.listByVolumeIdAndTypeNotInAndStateNotRemoved(volume.getId(), Snapshot.Type.GROUP);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -947,6 +947,11 @@ public void setEncryptFormat(String encryptFormat) {
volumeVO.setEncryptFormat(encryptFormat);
}

@Override
public boolean isEncrypted() {
return volumeVO.isEncrypted();
}

@Override
public boolean isDeleteProtection() {
return volumeVO.isDeleteProtection();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
package com.cloud.hypervisor.kvm.resource.wrapper;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;
Expand Down Expand Up @@ -174,12 +176,13 @@ protected void revertVolumeToSnapshot(KVMStoragePool kvmStoragePoolSecondary, Sn
storagePoolSet = resource.connectToAllVolumeSnapshotSecondaryStorages(volumeObjectTo);
}

logger.debug(String.format("Reverting volume [%s] to snapshot [%s].", volumeObjectTo, snapshotToPrint));
logger.debug("Reverting volume [{}] to snapshot [{}].", volumeObjectTo, snapshotToPrint);

try {
replaceVolumeWithSnapshot(volumePath, snapshotPath);
logger.debug(String.format("Successfully reverted volume [%s] to snapshot [%s].", volumeObjectTo, snapshotToPrint));
} catch (LibvirtException | QemuImgException ex) {
boolean isVolumeEncrypted = volumeObjectTo.getPassphrase() != null || volumeObjectTo.getEncryptFormat() != null;
replaceVolumeWithSnapshot(volumePath, snapshotPath, isVolumeEncrypted);
logger.debug("Successfully reverted volume [{}] to snapshot [{}].", volumeObjectTo, snapshotToPrint);
} catch (LibvirtException | QemuImgException | IOException ex) {
throw new CloudRuntimeException(String.format("Unable to revert volume [%s] to snapshot [%s] due to [%s].", volumeObjectTo, snapshotToPrint, ex.getMessage()), ex);
} finally {
if (storagePoolSet != null) {
Expand Down Expand Up @@ -228,8 +231,14 @@ protected Pair<String, SnapshotObjectTO> getSnapshot(SnapshotObjectTO snapshotOn
* @throws LibvirtException If can't replace the current volume with the snapshot.
* @throws QemuImgException If can't replace the current volume with the snapshot.
*/
protected void replaceVolumeWithSnapshot(String volumePath, String snapshotPath) throws LibvirtException, QemuImgException {
logger.debug(String.format("Replacing volume at [%s] with snapshot that is at [%s].", volumePath, snapshotPath));
protected void replaceVolumeWithSnapshot(String volumePath, String snapshotPath, boolean isVolumeEncrypted) throws LibvirtException, QemuImgException, IOException {
if (isVolumeEncrypted) {
logger.debug("Replacing encrypted volume at [{}] with snapshot that is at [{}] using file copy.", volumePath, snapshotPath);
Files.copy(Paths.get(snapshotPath), Paths.get(volumePath), StandardCopyOption.REPLACE_EXISTING);
return;
}

logger.debug("Replacing volume at [{}] with snapshot that is at [{}] using qemu-img.", volumePath, snapshotPath);
QemuImg qemuImg = new QemuImg(AgentPropertiesFileHandler.getPropertyValue(AgentProperties.REVERT_SNAPSHOT_TIMEOUT) * 1000);
QemuImgFile volumeImg = new QemuImgFile(volumePath, QemuImg.PhysicalDiskFormat.QCOW2);
QemuImgFile snapshotImg = new QemuImgFile(snapshotPath, QemuImg.PhysicalDiskFormat.QCOW2);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1261,7 +1261,7 @@ public KVMPhysicalDisk createDiskFromTemplate(KVMPhysicalDisk template,
if (destPool.getType() == StoragePoolType.RBD) {
disk = createDiskFromTemplateOnRBD(template, name, format, provisioningType, size, destPool, timeout);
} else {
try (KeyFile keyFile = new KeyFile(passphrase)){
try (KeyFile keyFile = new KeyFile(passphrase)) {
String newUuid = name;
List<QemuObject> passphraseObjects = new ArrayList<>();
disk = destPool.createPhysicalDisk(newUuid, format, provisioningType, template.getVirtualSize(), passphrase);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Qemu
return null;
}

if(!connectPhysicalDisk(name, pool, null, false)) {
if (!connectPhysicalDisk(name, pool, null, false)) {
throw new CloudRuntimeException(String.format("Failed to ensure disk %s was present", name));
}

Expand All @@ -261,7 +261,7 @@ public KVMPhysicalDisk createPhysicalDisk(String name, KVMStoragePool pool, Qemu
if (provisioningType.equals(Storage.ProvisioningType.THIN)) {
disk.setFormat(QemuImg.PhysicalDiskFormat.QCOW2);
disk.setQemuEncryptFormat(QemuObject.EncryptFormat.LUKS);
try (KeyFile keyFile = new KeyFile(passphrase)){
try (KeyFile keyFile = new KeyFile(passphrase)) {
QemuImg qemuImg = new QemuImg(0, true, false);
Map<String, String> options = new HashMap<>();
List<QemuObject> qemuObjects = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package com.cloud.hypervisor.kvm.resource.wrapper;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand Down Expand Up @@ -144,28 +145,45 @@ public void validateGetSnapshotPathDoesNotExistsOnSecondaryStorageThrows() {
}

@Test
public void validateRevertVolumeToSnapshotReplaceSuccessfully() throws LibvirtException, QemuImgException {
public void validateRevertVolumeToSnapshotReplaceSuccessfully() throws LibvirtException, QemuImgException, IOException {
Mockito.doReturn(volumeObjectToMock).when(snapshotObjectToSecondaryMock).getVolume();
Mockito.doReturn(null).when(volumeObjectToMock).getPassphrase();
Mockito.doReturn(null).when(volumeObjectToMock).getEncryptFormat();
Mockito.doReturn(pairStringSnapshotObjectToMock).when(libvirtRevertSnapshotCommandWrapperSpy).getSnapshot(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
Mockito.doNothing().when(libvirtRevertSnapshotCommandWrapperSpy).replaceVolumeWithSnapshot(Mockito.any(), Mockito.any());
Mockito.doNothing().when(libvirtRevertSnapshotCommandWrapperSpy).replaceVolumeWithSnapshot(Mockito.any(), Mockito.any(), Mockito.anyBoolean());
libvirtRevertSnapshotCommandWrapperSpy.revertVolumeToSnapshot(kvmStoragePoolSecondaryMock, snapshotObjectToPrimaryMock, snapshotObjectToSecondaryMock, kvmStoragePoolPrimaryMock, resourceMock
);
}

@Test (expected = CloudRuntimeException.class)
public void validateRevertVolumeToSnapshotReplaceVolumeThrowsQemuImgException() throws LibvirtException, QemuImgException {
public void validateRevertVolumeToSnapshotReplaceVolumeThrowsQemuImgException() throws LibvirtException, QemuImgException, IOException {
Mockito.doReturn(volumeObjectToMock).when(snapshotObjectToSecondaryMock).getVolume();
Mockito.doReturn(null).when(volumeObjectToMock).getPassphrase();
Mockito.doReturn(null).when(volumeObjectToMock).getEncryptFormat();
Mockito.doReturn(pairStringSnapshotObjectToMock).when(libvirtRevertSnapshotCommandWrapperSpy).getSnapshot(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
Mockito.doThrow(QemuImgException.class).when(libvirtRevertSnapshotCommandWrapperSpy).replaceVolumeWithSnapshot(Mockito.any(), Mockito.any());
Mockito.doThrow(QemuImgException.class).when(libvirtRevertSnapshotCommandWrapperSpy).replaceVolumeWithSnapshot(Mockito.any(), Mockito.any(), Mockito.anyBoolean());
libvirtRevertSnapshotCommandWrapperSpy.revertVolumeToSnapshot(kvmStoragePoolSecondaryMock, snapshotObjectToPrimaryMock, snapshotObjectToSecondaryMock, kvmStoragePoolPrimaryMock, resourceMock
);
}

@Test (expected = CloudRuntimeException.class)
public void validateRevertVolumeToSnapshotReplaceVolumeThrowsLibvirtException() throws LibvirtException, QemuImgException {
public void validateRevertVolumeToSnapshotReplaceVolumeThrowsLibvirtException() throws LibvirtException, QemuImgException, IOException {
Mockito.doReturn(volumeObjectToMock).when(snapshotObjectToSecondaryMock).getVolume();
Mockito.doReturn(null).when(volumeObjectToMock).getPassphrase();
Mockito.doReturn(null).when(volumeObjectToMock).getEncryptFormat();
Mockito.doReturn(pairStringSnapshotObjectToMock).when(libvirtRevertSnapshotCommandWrapperSpy).getSnapshot(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
Mockito.doThrow(LibvirtException.class).when(libvirtRevertSnapshotCommandWrapperSpy).replaceVolumeWithSnapshot(Mockito.any(), Mockito.any());
Mockito.doThrow(LibvirtException.class).when(libvirtRevertSnapshotCommandWrapperSpy).replaceVolumeWithSnapshot(Mockito.any(), Mockito.any(), Mockito.anyBoolean());
libvirtRevertSnapshotCommandWrapperSpy.revertVolumeToSnapshot(kvmStoragePoolSecondaryMock, snapshotObjectToPrimaryMock, snapshotObjectToSecondaryMock, kvmStoragePoolPrimaryMock, resourceMock
);
}

@Test
public void validateRevertEncryptedVolumeToSnapshotReplaceSuccessfully() throws LibvirtException, QemuImgException, IOException {
Mockito.doReturn(volumeObjectToMock).when(snapshotObjectToSecondaryMock).getVolume();
Mockito.doReturn(null).when(volumeObjectToMock).getPassphrase();
Mockito.doReturn("luks").when(volumeObjectToMock).getEncryptFormat();
Mockito.doReturn(pairStringSnapshotObjectToMock).when(libvirtRevertSnapshotCommandWrapperSpy).getSnapshot(Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any());
Mockito.doNothing().when(libvirtRevertSnapshotCommandWrapperSpy).replaceVolumeWithSnapshot(Mockito.any(), Mockito.any(), Mockito.anyBoolean());
libvirtRevertSnapshotCommandWrapperSpy.revertVolumeToSnapshot(kvmStoragePoolSecondaryMock, snapshotObjectToPrimaryMock, snapshotObjectToSecondaryMock, kvmStoragePoolPrimaryMock, resourceMock
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -903,7 +903,7 @@ public VolumeVO allocVolume(CreateVolumeCmd cmd) throws ResourceAllocationExcept
parentVolume = _volsDao.findByIdIncludingRemoved(snapshotCheck.getVolumeId());

// Don't support creating templates from encrypted volumes (yet)
if (parentVolume.getPassphraseId() != null) {
if (parentVolume.isEncrypted()) {
throw new UnsupportedOperationException("Cannot create new volumes from encrypted volume snapshots");
}

Expand Down Expand Up @@ -4045,7 +4045,7 @@ private Snapshot orchestrateTakeVolumeSnapshot(Long volumeId, Long policyId, Lon
}

boolean isSnapshotOnStorPoolOnly = volume.getStoragePoolType() == StoragePoolType.StorPool && SnapshotInfo.BackupSnapshotAfterTakingSnapshot.value();
if (volume.getEncryptFormat() != null && volume.getAttachedVM() != null && volume.getAttachedVM().getState() != State.Stopped && !isSnapshotOnStorPoolOnly) {
if (volume.isEncrypted() && volume.getAttachedVM() != null && volume.getAttachedVM().getState() != State.Stopped && !isSnapshotOnStorPoolOnly) {
logger.debug(String.format("Refusing to take snapshot of encrypted volume (%s) on running VM (%s)", volume, volume.getAttachedVM()));
throw new UnsupportedOperationException("Volume snapshots for encrypted volumes are not supported if VM is running");
}
Expand Down Expand Up @@ -4160,7 +4160,6 @@ public Snapshot allocSnapshot(Long volumeId, Long policyId, String snapshotName,
}
}


return snapshotMgr.allocSnapshot(volumeId, policyId, snapshotName, locationType, false, zoneIds);
}

Expand Down Expand Up @@ -4286,7 +4285,7 @@ public String extractVolume(ExtractVolumeCmd cmd) {
throw ex;
}

if (volume.getPassphraseId() != null) {
if (volume.isEncrypted()) {
throw new InvalidParameterValueException("Extraction of encrypted volumes is unsupported");
}

Expand Down
Loading
Loading