This is an automated email from the ASF dual-hosted git repository.

dahn pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/main by this push:
     new f5b3cb59ee [Veeam] enable volume attach/detach in VMs with Backup 
Offerings (#6581)
f5b3cb59ee is described below

commit f5b3cb59ee9ded6a423c10ea21cf5215ac77a877
Author: SadiJr <[email protected]>
AuthorDate: Mon Jan 23 05:34:46 2023 -0300

    [Veeam] enable volume attach/detach in VMs with Backup Offerings (#6581)
---
 .../apache/cloudstack/backup/BackupManager.java    |  5 ++
 .../java/com/cloud/hypervisor/guru/VMwareGuru.java | 50 +++++++++++++++--
 .../com/cloud/hypervisor/guru/VMwareGuruTest.java  | 33 ++++++++++++
 .../com/cloud/storage/VolumeApiServiceImpl.java    | 48 ++++++++++++++---
 .../cloudstack/backup/BackupManagerImpl.java       |  6 ++-
 .../cloud/storage/VolumeApiServiceImplTest.java    | 63 +++++++++++++++++++++-
 6 files changed, 192 insertions(+), 13 deletions(-)

diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java 
b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java
index 1285be3b7b..43b70fa766 100644
--- a/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java
+++ b/api/src/main/java/org/apache/cloudstack/backup/BackupManager.java
@@ -51,6 +51,11 @@ public interface BackupManager extends BackupService, 
Configurable, PluggableSer
             "300",
             "The backup and recovery background sync task polling interval in 
seconds.", true);
 
+    ConfigKey<Boolean> BackupEnableAttachDetachVolumes = new 
ConfigKey<>("Advanced", Boolean.class,
+            "backup.enable.attach.detach.of.volumes",
+            "false",
+            "Enable volume attach/detach operations for VMs that are assigned 
to Backup Offerings.", true);
+
     /**
      * List backup provider offerings
      * @param zoneId zone id
diff --git 
a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
 
b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
index eb66bc0130..0b67074e93 100644
--- 
a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
+++ 
b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
@@ -106,6 +106,7 @@ import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO;
 import com.cloud.network.dao.PhysicalNetworkVO;
 import com.cloud.secstorage.CommandExecLogDao;
 import com.cloud.secstorage.CommandExecLogVO;
+import com.cloud.serializer.GsonHelper;
 import com.cloud.service.ServiceOfferingVO;
 import com.cloud.storage.DataStoreRole;
 import com.cloud.storage.DiskOfferingVO;
@@ -139,6 +140,7 @@ import com.cloud.vm.VirtualMachine.Type;
 import com.cloud.vm.VirtualMachineManager;
 import com.cloud.vm.VirtualMachineProfile;
 import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.VMInstanceDao;
 import com.google.gson.Gson;
 import com.vmware.vim25.ManagedObjectReference;
 import com.vmware.vim25.VirtualDevice;
@@ -153,6 +155,7 @@ import com.vmware.vim25.VirtualMachineRuntimeInfo;
 
 public class VMwareGuru extends HypervisorGuruBase implements HypervisorGuru, 
Configurable {
     private static final Logger s_logger = Logger.getLogger(VMwareGuru.class);
+    private static final Gson GSON = GsonHelper.getGson();
 
 
     @Inject VmwareVmImplementer vmwareVmImplementer;
@@ -161,6 +164,7 @@ public class VMwareGuru extends HypervisorGuruBase 
implements HypervisorGuru, Co
     @Inject ClusterDetailsDao _clusterDetailsDao;
     @Inject CommandExecLogDao _cmdExecLogDao;
     @Inject VmwareManager _vmwareMgr;
+    @Inject VMInstanceDao vmDao;
     @Inject SecondaryStorageVmManager _secStorageMgr;
     @Inject PhysicalNetworkTrafficTypeDao _physicalNetworkTrafficTypeDao;
     @Inject VirtualMachineManager vmManager;
@@ -697,7 +701,7 @@ public class VMwareGuru extends HypervisorGuruBase 
implements HypervisorGuru, Co
         volumeVO.setTemplateId(templateId);
         volumeVO.setAttached(new Date());
         volumeVO.setRemoved(null);
-        volumeVO.setChainInfo(new Gson().toJson(diskInfo));
+        volumeVO.setChainInfo(GSON.toJson(diskInfo));
         if (unitNumber != null) {
             volumeVO.setDeviceId(unitNumber.longValue());
         }
@@ -736,12 +740,14 @@ public class VMwareGuru extends HypervisorGuruBase 
implements HypervisorGuru, Co
         volume.setPath(volumeName);
         volume.setPoolId(poolId);
         VirtualMachineDiskInfo diskInfo = getDiskInfo(vmToImport, poolId, 
volumeName);
-        volume.setChainInfo(new Gson().toJson(diskInfo));
+        volume.setChainInfo(GSON.toJson(diskInfo));
         volume.setInstanceId(vm.getId());
         volume.setState(Volume.State.Ready);
         volume.setAttached(new Date());
         _volumeDao.update(volume.getId(), volume);
         if (volume.getRemoved() != null) {
+            s_logger.debug(String.format("Marking volume [uuid: %s] of 
restored VM [%s] as non removed.", volume.getUuid(),
+                    
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(vm, "uuid", 
"instanceName")));
             _volumeDao.unremove(volume.getId());
             if (vm.getType() == Type.User) {
                 
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_CREATE, 
volume.getAccountId(), volume.getDataCenterId(), volume.getId(), 
volume.getName(), volume.getDiskOfferingId(), null, volume.getSize(),
@@ -812,6 +818,23 @@ public class VMwareGuru extends HypervisorGuruBase 
implements HypervisorGuru, Co
         return createVolumeRecord(type, volumeName, zoneId, domainId, 
accountId, diskOfferingId, provisioningType, size, instanceId, poolId, 
templateId, unitNumber, diskInfo);
     }
 
+    protected String createVolumeInfoFromVolumes(List<VolumeVO> vmVolumes) {
+        try {
+            List<Backup.VolumeInfo> list = new ArrayList<>();
+            for (VolumeVO vol : vmVolumes) {
+                list.add(new Backup.VolumeInfo(vol.getUuid(), vol.getPath(), 
vol.getVolumeType(), vol.getSize()));
+            }
+            return GSON.toJson(list.toArray(), Backup.VolumeInfo[].class);
+        } catch (Exception e) {
+            if (CollectionUtils.isEmpty(vmVolumes) || 
vmVolumes.get(0).getInstanceId() == null) {
+                s_logger.error(String.format("Failed to create VolumeInfo of 
VM [id: null] volumes due to: [%s].", e.getMessage()), e);
+            } else {
+                s_logger.error(String.format("Failed to create VolumeInfo of 
VM [id: %s] volumes due to: [%s].", vmVolumes.get(0).getInstanceId(), 
e.getMessage()), e);
+            }
+            throw e;
+        }
+    }
+
     /**
      * Get physical network ID from zoneId and Vmware label
      */
@@ -919,11 +942,25 @@ public class VMwareGuru extends HypervisorGuruBase 
implements HypervisorGuru, Co
         if (vm == null) {
             throw new CloudRuntimeException("Failed to find the volumes 
details from the VM backup");
         }
+
         List<Backup.VolumeInfo> backedUpVolumes = vm.getBackupVolumeList();
         Map<String, Boolean> usedVols = new HashMap<>();
         Map<VirtualDisk, VolumeVO> map = new HashMap<>();
 
         for (Backup.VolumeInfo backedUpVol : backedUpVolumes) {
+            VolumeVO volumeExtra = 
_volumeDao.findByUuid(backedUpVol.getUuid());
+            if (volumeExtra != null) {
+                s_logger.debug(String.format("Marking volume [id: %s] of VM 
[%s] as removed for the backup process.", backedUpVol.getUuid(), 
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(vm, "uuid", 
"instanceName")));
+                _volumeDao.remove(volumeExtra.getId());
+
+                if (vm.getType() == Type.User) {
+                    
UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DETACH, 
volumeExtra.getAccountId(), volumeExtra.getDataCenterId(), volumeExtra.getId(),
+                            volumeExtra.getName(), 
volumeExtra.getDiskOfferingId(), null, volumeExtra.getSize(), 
Volume.class.getName(),
+                            volumeExtra.getUuid(), 
volumeExtra.isDisplayVolume());
+                    
_resourceLimitService.decrementResourceCount(vm.getAccountId(), 
Resource.ResourceType.volume, volumeExtra.isDisplayVolume());
+                    
_resourceLimitService.decrementResourceCount(vm.getAccountId(), 
Resource.ResourceType.primary_storage, volumeExtra.isDisplayVolume(), 
volumeExtra.getSize());
+                }
+            }
             for (VirtualDisk disk : virtualDisks) {
                 if (!map.containsKey(disk) && 
backedUpVol.getSize().equals(disk.getCapacityInBytes()) && 
!usedVols.containsKey(backedUpVol.getUuid())) {
                     String volId = backedUpVol.getUuid();
@@ -1022,7 +1059,8 @@ public class VMwareGuru extends HypervisorGuruBase 
implements HypervisorGuru, Co
         return vm;
     }
 
-    @Override public boolean attachRestoredVolumeToVirtualMachine(long zoneId, 
String location, Backup.VolumeInfo volumeInfo, VirtualMachine vm, long poolId, 
Backup backup)
+    @Override
+    public boolean attachRestoredVolumeToVirtualMachine(long zoneId, String 
location, Backup.VolumeInfo volumeInfo, VirtualMachine vm, long poolId, Backup 
backup)
             throws Exception {
         DatacenterMO dcMo = getDatacenterMO(zoneId);
         VirtualMachineMO vmRestored = findVM(dcMo, location);
@@ -1061,6 +1099,12 @@ public class VMwareGuru extends HypervisorGuruBase 
implements HypervisorGuru, Co
         }
         createVolume(attachedDisk, vmMo, vm.getDomainId(), 
vm.getDataCenterId(), vm.getAccountId(), vm.getId(), poolId, 
vm.getTemplateId(), backup, false);
 
+        if (vm.getBackupOfferingId() == null) {
+            return true;
+        }
+        VMInstanceVO vmVO = (VMInstanceVO)vm;
+        
vmVO.setBackupVolumes(createVolumeInfoFromVolumes(_volumeDao.findByInstance(vm.getId())));
+        vmDao.update(vmVO.getId(), vmVO);
         return true;
     }
 
diff --git 
a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java
 
b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java
index ca618ac1b0..0db8271c8e 100644
--- 
a/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java
+++ 
b/plugins/hypervisors/vmware/src/test/java/com/cloud/hypervisor/guru/VMwareGuruTest.java
@@ -21,9 +21,11 @@ import com.cloud.agent.api.MigrateVmToPoolCommand;
 import com.cloud.dc.ClusterDetailsDao;
 import com.cloud.host.HostVO;
 import com.cloud.host.dao.HostDao;
+import com.cloud.storage.Storage.ProvisioningType;
 import com.cloud.storage.StoragePool;
 import com.cloud.storage.StoragePoolHostVO;
 import com.cloud.storage.Volume;
+import com.cloud.storage.VolumeVO;
 import com.cloud.storage.dao.StoragePoolHostDao;
 import com.cloud.utils.Pair;
 import com.cloud.vm.VirtualMachine;
@@ -44,6 +46,8 @@ import org.powermock.modules.junit4.PowerMockRunner;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.support.AnnotationConfigContextLoader;
 
+import static org.junit.Assert.assertEquals;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -116,4 +120,33 @@ public class VMwareGuruTest {
         Assert.assertEquals("HostSystem:[email protected]", 
migrateVmToPoolCommand.getHostGuidInTargetCluster());
     }
 
+    @Test
+    public void 
createVolumeInfoFromVolumesTestEmptyVolumeListReturnEmptyArray() {
+        String volumeInfo = vMwareGuru.createVolumeInfoFromVolumes(new 
ArrayList<>());
+        assertEquals("[]", volumeInfo);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void createVolumeInfoFromVolumesTestNullVolume() {
+        vMwareGuru.createVolumeInfoFromVolumes(null);
+    }
+
+    @Test
+    public void createVolumeInfoFromVolumesTestCorrectlyConvertOfVolumes() {
+        List<VolumeVO> volumesToTest = new ArrayList<>();
+
+        VolumeVO root = new VolumeVO("test", 1l, 1l, 1l, 1l, 1l, "test", 
"/root/dir", ProvisioningType.THIN, 555l, Volume.Type.ROOT);
+        String rootUuid = root.getUuid();
+
+        VolumeVO data = new VolumeVO("test", 1l, 1l, 1l, 1l, 1l, "test", 
"/root/dir/data", ProvisioningType.THIN, 1111000l, Volume.Type.DATADISK);
+        String dataUuid = data.getUuid();
+
+        volumesToTest.add(root);
+        volumesToTest.add(data);
+
+        String result = vMwareGuru.createVolumeInfoFromVolumes(volumesToTest);
+        String expected = 
String.format("[{\"uuid\":\"%s\",\"type\":\"ROOT\",\"size\":555,\"path\":\"/root/dir\"},{\"uuid\":\"%s\",\"type\":\"DATADISK\",\"size\":1111000,\"path\":\"/root/dir/data\"}]",
 rootUuid, dataUuid);
+
+        assertEquals(expected, result);
+    }
 }
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java 
b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index 735a8b2930..7b32fdee97 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -45,6 +45,8 @@ import 
org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
 import org.apache.cloudstack.api.command.user.volume.UploadVolumeCmd;
 import org.apache.cloudstack.api.response.GetUploadParamsResponse;
+import org.apache.cloudstack.backup.Backup;
+import org.apache.cloudstack.backup.BackupManager;
 import org.apache.cloudstack.context.CallContext;
 import 
org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
 import org.apache.cloudstack.engine.subsystem.api.storage.ChapInfo;
@@ -97,9 +99,8 @@ import 
org.apache.cloudstack.utils.volume.VirtualMachineDiskInfo;
 import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.ObjectUtils;
+import org.apache.commons.lang3.BooleanUtils;
 import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
 import org.apache.log4j.Logger;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
@@ -2477,6 +2478,36 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
         return volumeToAttach;
     }
 
+    protected void validateIfVmHasBackups(UserVmVO vm, boolean attach) {
+        if ((vm.getBackupOfferingId() == null || 
CollectionUtils.isEmpty(vm.getBackupVolumeList())) || 
BooleanUtils.isTrue(BackupManager.BackupEnableAttachDetachVolumes.value())) {
+            return;
+        }
+        String errorMsg = String.format("Unable to detach volume, cannot 
detach volume from a VM that has backups. First remove the VM from the backup 
offering or "
+                + "set the global configuration '%s' to true.", 
BackupManager.BackupEnableAttachDetachVolumes.key());
+        if (attach) {
+            errorMsg = String.format("Unable to attach volume, please specify 
a VM that does not have any backups or set the global configuration "
+                    + "'%s' to true.", 
BackupManager.BackupEnableAttachDetachVolumes.key());
+        }
+        throw new InvalidParameterValueException(errorMsg);
+    }
+
+    protected String createVolumeInfoFromVolumes(List<VolumeVO> vmVolumes) {
+        try {
+            List<Backup.VolumeInfo> list = new ArrayList<>();
+            for (VolumeVO vol : vmVolumes) {
+                list.add(new Backup.VolumeInfo(vol.getUuid(), vol.getPath(), 
vol.getVolumeType(), vol.getSize()));
+            }
+            return GsonHelper.getGson().toJson(list.toArray(), 
Backup.VolumeInfo[].class);
+        } catch (Exception e) {
+            if (CollectionUtils.isEmpty(vmVolumes) || 
vmVolumes.get(0).getInstanceId() == null) {
+                s_logger.error(String.format("Failed to create VolumeInfo of 
VM [id: null] volumes due to: [%s].", e.getMessage()), e);
+            } else {
+                s_logger.error(String.format("Failed to create VolumeInfo of 
VM [id: %s] volumes due to: [%s].", vmVolumes.get(0).getInstanceId(), 
e.getMessage()), e);
+            }
+            throw e;
+        }
+    }
+
     @Override
     @ActionEvent(eventType = EventTypes.EVENT_VOLUME_UPDATE, eventDescription 
= "updating volume", async = true)
     public Volume updateVolume(long volumeId, String path, String state, Long 
storageId, Boolean displayVolume,
@@ -2649,13 +2680,11 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
 
         // Don't allow detach if target VM has associated VM snapshots
         List<VMSnapshotVO> vmSnapshots = _vmSnapshotDao.findByVm(vmId);
-        if (vmSnapshots.size() > 0) {
+        if (CollectionUtils.isNotEmpty(vmSnapshots)) {
             throw new InvalidParameterValueException("Unable to detach volume, 
please specify a VM that does not have VM snapshots");
         }
 
-        if (vm.getBackupOfferingId() != null || 
vm.getBackupVolumeList().size() > 0) {
-            throw new InvalidParameterValueException("Unable to detach volume, 
cannot detach volume from a VM that has backups. First remove the VM from the 
backup offering.");
-        }
+        validateIfVmHasBackups(vm, false);
 
         AsyncJobExecutionContext asyncExecutionContext = 
AsyncJobExecutionContext.getCurrentExecutionContext();
         if (asyncExecutionContext != null) {
@@ -2705,6 +2734,10 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
                     vol = _volsDao.findById((Long)jobResult);
                 }
             }
+            if (vm.getBackupOfferingId() != null) {
+                
vm.setBackupVolumes(createVolumeInfoFromVolumes(_volsDao.findByInstance(vm.getId())));
+                _vmInstanceDao.update(vm.getId(), vm);
+            }
             return vol;
         }
     }
@@ -2846,6 +2879,7 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
             if (volumePool != null && hostId != null) {
                 handleTargetsForVMware(hostId, volumePool.getHostAddress(), 
volumePool.getPort(), volume.get_iScsiName());
             }
+
             return _volsDao.findById(volumeId);
         } else {
 
@@ -3025,7 +3059,7 @@ public class VolumeApiServiceImpl extends ManagerBase 
implements VolumeApiServic
             snapshotHelper.checkKvmVolumeSnapshotsOnlyInPrimaryStorage(vol, 
_volsDao.getHypervisorType(vol.getId()));
         } catch (CloudRuntimeException ex) {
             throw new CloudRuntimeException(String.format("Unable to migrate 
%s to the destination storage pool [%s] due to [%s]", vol,
-                    new ToStringBuilder(destPool, 
ToStringStyle.JSON_STYLE).append("uuid", destPool.getUuid()).append("name", 
destPool.getName()).toString(), ex.getMessage()), ex);
+                    
ReflectionToStringBuilderUtils.reflectOnlySelectedFields(destPool, "uuid", 
"name"), ex.getMessage()), ex);
         }
 
         DiskOfferingVO diskOffering = 
_diskOfferingDao.findById(vol.getDiskOfferingId());
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 1978cca687..314d263f75 100644
--- a/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/backup/BackupManagerImpl.java
@@ -587,6 +587,7 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
         if (offering == null) {
             throw new CloudRuntimeException("Failed to find backup offering of 
the VM backup");
         }
+
         final BackupProvider backupProvider = 
getBackupProvider(offering.getProvider());
         if (!backupProvider.restoreVMFromBackup(vm, backup)) {
             throw new CloudRuntimeException("Error restoring VM from backup ID 
" + backup.getId());
@@ -619,7 +620,7 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
         final VMInstanceVO vm = findVmById(vmId);
         accountManager.checkAccess(CallContext.current().getCallingAccount(), 
null, true, vm);
 
-        if (vm.getBackupOfferingId() != null) {
+        if (vm.getBackupOfferingId() != null && 
!BackupEnableAttachDetachVolumes.value()) {
             throw new CloudRuntimeException("The selected VM has backups, 
cannot restore and attach volume to the VM.");
         }
 
@@ -841,7 +842,8 @@ public class BackupManagerImpl extends ManagerBase 
implements BackupManager {
         return new ConfigKey[]{
                 BackupFrameworkEnabled,
                 BackupProviderPlugin,
-                BackupSyncPollingInterval
+                BackupSyncPollingInterval,
+                BackupEnableAttachDetachVolumes
         };
     }
 
diff --git 
a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java 
b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java
index 4c4bbf789b..45adc84d82 100644
--- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java
+++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java
@@ -16,6 +16,7 @@
 // under the License.
 package com.cloud.storage;
 
+import static org.junit.Assert.assertEquals;
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyLong;
 import static org.mockito.Matchers.anyObject;
@@ -90,6 +91,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
 import com.cloud.org.Grouping;
 import com.cloud.serializer.GsonHelper;
 import com.cloud.server.TaggedResourceService;
+import com.cloud.storage.Storage.ProvisioningType;
 import com.cloud.storage.Volume.Type;
 import com.cloud.storage.dao.DiskOfferingDao;
 import com.cloud.storage.dao.StoragePoolTagsDao;
@@ -1164,6 +1166,65 @@ public class VolumeApiServiceImplTest {
         Assert.assertTrue(result);
     }
 
+    @Test
+    public void 
validateIfVmHaveBackupsTestExceptionWhenTryToDetachVolumeFromVMWhichBackupOffering()
 {
+        try {
+            UserVmVO vm = Mockito.mock(UserVmVO.class);
+            when(vm.getBackupOfferingId()).thenReturn(1l);
+            volumeApiServiceImpl.validateIfVmHasBackups(vm, false);
+        } catch (Exception e) {
+            Assert.assertEquals("Unable to detach volume, cannot detach volume 
from a VM that has backups. First remove the VM from the backup offering or set 
the global configuration 'backup.enable.attach.detach.of.volumes' to true.", 
e.getMessage());
+        }
+    }
+
+    @Test
+    public void 
validateIfVmHaveBackupsTestExceptionWhenTryToAttachVolumeFromVMWhichBackupOffering()
 {
+        try {
+            UserVmVO vm = Mockito.mock(UserVmVO.class);
+            when(vm.getBackupOfferingId()).thenReturn(1l);
+            volumeApiServiceImpl.validateIfVmHasBackups(vm, true);
+        } catch (Exception e) {
+            Assert.assertEquals("Unable to attach volume, please specify a VM 
that does not have any backups or set the global configuration 
'backup.enable.attach.detach.of.volumes' to true.", e.getMessage());
+        }
+    }
+
+    @Test
+    public void 
validateIfVmHaveBackupsTestSuccessWhenVMDontHaveBackupOffering() {
+        UserVmVO vm = Mockito.mock(UserVmVO.class);
+        when(vm.getBackupOfferingId()).thenReturn(null);
+        volumeApiServiceImpl.validateIfVmHasBackups(vm, true);
+    }
+
+    @Test
+    public void 
createVolumeInfoFromVolumesTestEmptyVolumeListReturnEmptyArray() {
+        String volumeInfo = 
volumeApiServiceImpl.createVolumeInfoFromVolumes(new ArrayList<>());
+        assertEquals("[]", volumeInfo);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void createVolumeInfoFromVolumesTestNullVolume() {
+        volumeApiServiceImpl.createVolumeInfoFromVolumes(null);
+    }
+
+    @Test
+    public void createVolumeInfoFromVolumesTestCorrectlyConvertOfVolumes() {
+        List<VolumeVO> volumesToTest = new ArrayList<>();
+
+        VolumeVO root = new VolumeVO("test", 1l, 1l, 1l, 1l, 1l, "test", 
"/root/dir", ProvisioningType.THIN, 555l, Type.ROOT);
+        String rootUuid = root.getUuid();
+
+        VolumeVO data = new VolumeVO("test", 1l, 1l, 1l, 1l, 1l, "test", 
"/root/dir/data", ProvisioningType.THIN, 1111000l, Type.DATADISK);
+        String dataUuid = data.getUuid();
+
+        volumesToTest.add(root);
+        volumesToTest.add(data);
+
+        String result = 
volumeApiServiceImpl.createVolumeInfoFromVolumes(volumesToTest);
+        String expected = 
String.format("[{\"uuid\":\"%s\",\"type\":\"ROOT\",\"size\":555,\"path\":\"/root/dir\"},{\"uuid\":\"%s\",\"type\":\"DATADISK\",\"size\":1111000,\"path\":\"/root/dir/data\"}]",
 rootUuid, dataUuid);
+
+        assertEquals(expected, result);
+    }
+
     @Test
     public void isNotPossibleToResizeTestAllFormats() {
         Storage.ImageFormat[] imageFormat = Storage.ImageFormat.values();
@@ -1319,4 +1380,4 @@ public class VolumeApiServiceImplTest {
                 offeringMockId, volumeVoMock.getTemplateId(), 
volumeVoMock.getSize(), Volume.class.getName(), volumeVoMock.getUuid(), 
volumeVoMock.isDisplay());
 
     }
-}
+}
\ No newline at end of file

Reply via email to