diff --git a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java index 387bd8a4e23..3956fe40fb1 100644 --- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java @@ -66,6 +66,7 @@ import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; @@ -634,9 +635,9 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager { } accountManager.checkAccess(CallContext.current().getCallingAccount(), null, true, vmFromBackup); - Pair restoreInfo = getRestoreVolumeHostAndDatastore(vm); - String hostIp = restoreInfo.first(); - String datastoreUuid = restoreInfo.second(); + Pair restoreInfo = getRestoreVolumeHostAndDatastore(vm); + HostVO host = restoreInfo.first(); + StoragePoolVO datastore = restoreInfo.second(); LOG.debug("Asking provider to restore volume " + backedUpVolumeUuid + " from backup " + backupId + " (with external ID " + backup.getExternalId() + ") and attach it to VM: " + vm.getUuid()); @@ -647,17 +648,47 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager { } BackupProvider backupProvider = getBackupProvider(offering.getProvider()); - Pair result = backupProvider.restoreBackedUpVolume(backup, backedUpVolumeUuid, hostIp, datastoreUuid); - if (!result.first()) { - throw new CloudRuntimeException("Error restoring volume " + backedUpVolumeUuid); + LOG.debug(String.format("Trying to restore volume using host private IP address: [%s].", host.getPrivateIpAddress())); + + String[] hostPossibleValues = {host.getPrivateIpAddress(), host.getName()}; + String[] datastoresPossibleValues = {datastore.getUuid(), datastore.getName()}; + + Pair result = restoreBackedUpVolume(backedUpVolumeUuid, backup, backupProvider, hostPossibleValues, datastoresPossibleValues); + + if (BooleanUtils.isFalse(result.first())) { + throw new CloudRuntimeException(String.format("Error restoring volume [%s] of VM [%s] to host [%s] using backup provider [%s] due to: [%s].", + backedUpVolumeUuid, vm.getUuid(), host.getUuid(), backupProvider.getName(), result.second())); } if (!attachVolumeToVM(vm.getDataCenterId(), result.second(), vmFromBackup.getBackupVolumeList(), - backedUpVolumeUuid, vm, datastoreUuid, backup)) { - throw new CloudRuntimeException("Error attaching volume " + backedUpVolumeUuid + " to VM " + vm.getUuid()); + backedUpVolumeUuid, vm, datastore.getUuid(), backup)) { + throw new CloudRuntimeException(String.format("Error attaching volume [%s] to VM [%s]." + backedUpVolumeUuid, vm.getUuid())); } return true; } + protected Pair restoreBackedUpVolume(final String backedUpVolumeUuid, final BackupVO backup, BackupProvider backupProvider, String[] hostPossibleValues, + String[] datastoresPossibleValues) { + Pair result = new Pair<>(false, ""); + for (String hostData : hostPossibleValues) { + for (String datastoreData : datastoresPossibleValues) { + LOG.debug(String.format("Trying to restore volume [UUID: %s], using host [%s] and datastore [%s].", + backedUpVolumeUuid, hostData, datastoreData)); + + try { + result = backupProvider.restoreBackedUpVolume(backup, backedUpVolumeUuid, hostData, datastoreData); + + if (BooleanUtils.isTrue(result.first())) { + return result; + } + } catch (Exception e) { + LOG.debug(String.format("Failed to restore volume [UUID: %s], using host [%s] and datastore [%s] due to: [%s].", + backedUpVolumeUuid, hostData, datastoreData, e.getMessage()), e); + } + } + } + return result; + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VM_BACKUP_DELETE, eventDescription = "deleting VM backup", async = true) public boolean deleteBackup(final Long backupId) { @@ -694,21 +725,20 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager { /** * Get the pair: hostIp, datastoreUuid in which to restore the volume, based on the VM to be attached information */ - private Pair getRestoreVolumeHostAndDatastore(VMInstanceVO vm) { + private Pair getRestoreVolumeHostAndDatastore(VMInstanceVO vm) { List rootVmVolume = volumeDao.findIncludingRemovedByInstanceAndType(vm.getId(), Volume.Type.ROOT); Long poolId = rootVmVolume.get(0).getPoolId(); StoragePoolVO storagePoolVO = primaryDataStoreDao.findById(poolId); - String datastoreUuid = storagePoolVO.getUuid(); - String hostIp = vm.getHostId() == null ? - getHostIp(storagePoolVO) : - hostDao.findById(vm.getHostId()).getPrivateIpAddress(); - return new Pair<>(hostIp, datastoreUuid); + HostVO hostVO = vm.getHostId() == null ? + getFirstHostFromStoragePool(storagePoolVO) : + hostDao.findById(vm.getHostId()); + return new Pair<>(hostVO, storagePoolVO); } /** - * Find a host IP from storage pool access + * Find a host from storage pool access */ - private String getHostIp(StoragePoolVO storagePoolVO) { + private HostVO getFirstHostFromStoragePool(StoragePoolVO storagePoolVO) { List hosts = null; if (storagePoolVO.getScope().equals(ScopeType.CLUSTER)) { hosts = hostDao.findByClusterId(storagePoolVO.getClusterId()); @@ -716,9 +746,10 @@ public class BackupManagerImpl extends ManagerBase implements BackupManager { } else if (storagePoolVO.getScope().equals(ScopeType.ZONE)) { hosts = hostDao.findByDataCenterId(storagePoolVO.getDataCenterId()); } - return hosts.get(0).getPrivateIpAddress(); + return hosts.get(0); } + /** * Attach volume to VM */ diff --git a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java index 42efe14a64d..304e6ddce91 100644 --- a/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java +++ b/server/src/test/java/org/apache/cloudstack/backup/BackupManagerTest.java @@ -16,6 +16,7 @@ // under the License. package org.apache.cloudstack.backup; import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; import org.apache.cloudstack.api.ServerApiException; @@ -30,6 +31,7 @@ import org.mockito.MockitoAnnotations; import org.mockito.Spy; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.Pair; public class BackupManagerTest { @Spy @@ -39,6 +41,12 @@ public class BackupManagerTest { @Mock BackupOfferingDao backupOfferingDao; + @Mock + BackupProvider backupProvider; + + private String[] hostPossibleValues = {"127.0.0.1", "hostname"}; + private String[] datastoresPossibleValues = {"e9804933-8609-4de3-bccc-6278072a496c", "datastore-name"}; + @Before public void setup() throws Exception { MockitoAnnotations.initMocks(this); @@ -117,4 +125,68 @@ public class BackupManagerTest { assertEquals("New description", updated.getDescription()); assertEquals(true, updated.isUserDrivenBackupAllowed()); } + + @Test + public void restoreBackedUpVolumeTestHostIpAndDatastoreUuid() { + BackupVO backupVO = new BackupVO(); + String volumeUuid = "5f4ed903-ac23-4f8a-b595-69c73c40593f"; + + Mockito.when(backupProvider.restoreBackedUpVolume(Mockito.any(), Mockito.eq(volumeUuid), + Mockito.eq("127.0.0.1"), Mockito.eq("e9804933-8609-4de3-bccc-6278072a496c"))).thenReturn(new Pair(Boolean.TRUE, "Success")); + Pair restoreBackedUpVolume = backupManager.restoreBackedUpVolume(volumeUuid, backupVO, backupProvider, hostPossibleValues, datastoresPossibleValues); + + assertEquals(Boolean.TRUE, restoreBackedUpVolume.first()); + assertEquals("Success", restoreBackedUpVolume.second()); + + Mockito.verify(backupProvider, times(1)).restoreBackedUpVolume(Mockito.any(), Mockito.anyString(), + Mockito.anyString(), Mockito.anyString()); + } + + @Test + public void restoreBackedUpVolumeTestHostIpAndDatastoreName() { + BackupVO backupVO = new BackupVO(); + String volumeUuid = "5f4ed903-ac23-4f8a-b595-69c73c40593f"; + + Mockito.when(backupProvider.restoreBackedUpVolume(Mockito.any(), Mockito.eq(volumeUuid), + Mockito.eq("127.0.0.1"), Mockito.eq("datastore-name"))).thenReturn(new Pair(Boolean.TRUE, "Success2")); + Pair restoreBackedUpVolume = backupManager.restoreBackedUpVolume(volumeUuid, backupVO, backupProvider, hostPossibleValues, datastoresPossibleValues); + + assertEquals(Boolean.TRUE, restoreBackedUpVolume.first()); + assertEquals("Success2", restoreBackedUpVolume.second()); + + Mockito.verify(backupProvider, times(2)).restoreBackedUpVolume(Mockito.any(), Mockito.anyString(), + Mockito.anyString(), Mockito.anyString()); + } + + @Test + public void restoreBackedUpVolumeTestHostNameAndDatastoreUuid() { + BackupVO backupVO = new BackupVO(); + String volumeUuid = "5f4ed903-ac23-4f8a-b595-69c73c40593f"; + + Mockito.when(backupProvider.restoreBackedUpVolume(Mockito.any(), Mockito.eq(volumeUuid), + Mockito.eq("hostname"), Mockito.eq("e9804933-8609-4de3-bccc-6278072a496c"))).thenReturn(new Pair(Boolean.TRUE, "Success3")); + Pair restoreBackedUpVolume = backupManager.restoreBackedUpVolume(volumeUuid, backupVO, backupProvider, hostPossibleValues, datastoresPossibleValues); + + assertEquals(Boolean.TRUE, restoreBackedUpVolume.first()); + assertEquals("Success3", restoreBackedUpVolume.second()); + + Mockito.verify(backupProvider, times(3)).restoreBackedUpVolume(Mockito.any(), Mockito.anyString(), + Mockito.anyString(), Mockito.anyString()); + } + + @Test + public void restoreBackedUpVolumeTestHostAndDatastoreName() { + BackupVO backupVO = new BackupVO(); + String volumeUuid = "5f4ed903-ac23-4f8a-b595-69c73c40593f"; + + Mockito.when(backupProvider.restoreBackedUpVolume(Mockito.any(), Mockito.eq(volumeUuid), + Mockito.eq("hostname"), Mockito.eq("datastore-name"))).thenReturn(new Pair(Boolean.TRUE, "Success4")); + Pair restoreBackedUpVolume = backupManager.restoreBackedUpVolume(volumeUuid, backupVO, backupProvider, hostPossibleValues, datastoresPossibleValues); + + assertEquals(Boolean.TRUE, restoreBackedUpVolume.first()); + assertEquals("Success4", restoreBackedUpVolume.second()); + + Mockito.verify(backupProvider, times(4)).restoreBackedUpVolume(Mockito.any(), Mockito.anyString(), + Mockito.anyString(), Mockito.anyString()); + } } \ No newline at end of file