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

vishesh pushed a commit to branch fixup-limits-exceed-scaleio
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit a73654bda4f53b01b603832496450e4129d1df97
Author: Vishesh <vishes...@gmail.com>
AuthorDate: Tue Apr 30 01:41:58 2024 +0530

    Add e2e tests
---
 .../subsystem/api/storage/DataStoreDriver.java     |   1 -
 .../engine/orchestration/VolumeOrchestrator.java   |  35 ++++---
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  | 112 ++++++++++-----------
 .../java/com/cloud/vm/UserVmManagerImplTest.java   |   9 +-
 .../component/test_resource_limit_tags.py          |  44 ++++++++
 test/integration/smoke/test_restore_vm.py          |  86 ++++++++++++++--
 tools/marvin/marvin/lib/base.py                    |  20 +++-
 7 files changed, 222 insertions(+), 85 deletions(-)

diff --git 
a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java
 
b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java
index 6ddf4a2e5e0..b197afad863 100644
--- 
a/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java
+++ 
b/engine/api/src/main/java/org/apache/cloudstack/engine/subsystem/api/storage/DataStoreDriver.java
@@ -45,5 +45,4 @@ public interface DataStoreDriver {
     boolean canCopy(DataObject srcData, DataObject destData);
 
     void resize(DataObject data, AsyncCompletionCallback<CreateCmdResult> 
callback);
-
 }
diff --git 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
index 54f634e701d..a2921452e28 100644
--- 
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
+++ 
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/VolumeOrchestrator.java
@@ -39,6 +39,7 @@ import javax.inject.Inject;
 import javax.naming.ConfigurationException;
 
 import com.cloud.exception.ResourceAllocationException;
+import com.cloud.storage.DiskOfferingVO;
 import com.cloud.storage.VMTemplateVO;
 import com.cloud.storage.dao.VMTemplateDao;
 import com.cloud.user.AccountManager;
@@ -1855,19 +1856,29 @@ public class VolumeOrchestrator extends ManagerBase 
implements VolumeOrchestrati
      * If it's different it verifies the resource limits and updates the 
volume's size
      */
     protected void updateVolumeSize(DataStore store, VolumeVO vol) throws 
ResourceAllocationException {
-        if (store.getDriver() instanceof PrimaryDataStoreDriver) {
-            VMTemplateVO template = vol.getTemplateId() != null ? 
_templateDao.findById(vol.getTemplateId()) : null;
-            PrimaryDataStoreDriver driver = (PrimaryDataStoreDriver) 
store.getDriver();
-            long newSize = driver.getVolumeSizeRequiredOnPool(vol.getSize(), 
template == null ? null : template.getSize(), vol.getPassphraseId() != null);
-            if (newSize != vol.getSize()) {
-                if (newSize > vol.getSize()) {
-                    _resourceLimitMgr.checkPrimaryStorageResourceLimit(
-                            
_accountMgr.getActiveAccountById(vol.getAccountId()), vol.isDisplay(), newSize 
- vol.getSize(),
-                            
diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId()));
-                }
-                vol.setSize(newSize);
-                _volsDao.persist(vol);
+        if (store == null || !(store.getDriver() instanceof 
PrimaryDataStoreDriver)) {
+            return;
+        }
+
+        VMTemplateVO template = vol.getTemplateId() != null ? 
_templateDao.findById(vol.getTemplateId()) : null;
+        PrimaryDataStoreDriver driver = (PrimaryDataStoreDriver) 
store.getDriver();
+        long newSize = driver.getVolumeSizeRequiredOnPool(vol.getSize(),
+                template == null ? null : template.getSize(),
+                vol.getPassphraseId() != null);
+
+        if (newSize != vol.getSize()) {
+            DiskOfferingVO diskOffering = 
diskOfferingDao.findByIdIncludingRemoved(vol.getDiskOfferingId());
+            if (newSize > vol.getSize()) {
+                
_resourceLimitMgr.checkPrimaryStorageResourceLimit(_accountMgr.getActiveAccountById(vol.getAccountId()),
+                        vol.isDisplay(), newSize - vol.getSize(), 
diskOffering);
+                
_resourceLimitMgr.incrementVolumePrimaryStorageResourceCount(vol.getAccountId(),
 vol.isDisplay(),
+                        newSize - vol.getSize(), diskOffering);
+            } else {
+                
_resourceLimitMgr.decrementVolumePrimaryStorageResourceCount(vol.getAccountId(),
 vol.isDisplay(),
+                        vol.getSize() - newSize, diskOffering);
             }
+            vol.setSize(newSize);
+            _volsDao.persist(vol);
         }
     }
 
diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java 
b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 1bb1f22b9a5..96202fd59c7 100644
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@ -8013,7 +8013,7 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
                             newVol = volumeMgr.allocateDuplicateVolume(root, 
diskOffering, null);
                         }
 
-                        updateVolume(newVol, template, userVm, diskOffering, 
details);
+                        getRootVolumeSizeForVmRestore(newVol, template, 
userVm, diskOffering, details, true);
                         
volumeMgr.saveVolumeDetails(newVol.getDiskOfferingId(), newVol.getId());
 
                         // 1. Save usage event and update resource count for 
user vm volumes
@@ -8112,62 +8112,87 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
 
     }
 
-    private void updateVolume(Volume vol, VMTemplateVO template, UserVmVO 
userVm, DiskOffering diskOffering, Map<String, String> details) {
+    Long getRootVolumeSizeForVmRestore(Volume vol, VMTemplateVO template, 
UserVmVO userVm, DiskOffering diskOffering, Map<String, String> details, 
boolean update) {
         VolumeVO resizedVolume = (VolumeVO) vol;
 
+        Long size = null;
         if (template != null && template.getSize() != null) {
             UserVmDetailVO vmRootDiskSizeDetail = 
userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE);
             if (vmRootDiskSizeDetail == null) {
-                resizedVolume.setSize(template.getSize());
+                size = template.getSize();
             } else {
                 long rootDiskSize = 
Long.parseLong(vmRootDiskSizeDetail.getValue()) * GiB_TO_BYTES;
                 if (template.getSize() >= rootDiskSize) {
-                    resizedVolume.setSize(template.getSize());
-                    userVmDetailsDao.remove(vmRootDiskSizeDetail.getId());
+                    size = template.getSize();
+                    if (update) {
+                        userVmDetailsDao.remove(vmRootDiskSizeDetail.getId());
+                    }
                 } else {
-                    resizedVolume.setSize(rootDiskSize);
+                    size = rootDiskSize;
                 }
             }
+            if (update) {
+                resizedVolume.setSize(size);
+            }
         }
 
         if (diskOffering != null) {
-            resizedVolume.setDiskOfferingId(diskOffering.getId());
-            if (!diskOffering.isCustomized()) {
-                resizedVolume.setSize(diskOffering.getDiskSize());
+            if (update) {
+                resizedVolume.setDiskOfferingId(diskOffering.getId());
             }
-            if (diskOffering.getMinIops() != null) {
-                resizedVolume.setMinIops(diskOffering.getMinIops());
+            // Size of disk offering should be greater than or equal to the 
template's size and this should be validated before this
+            if (!diskOffering.isCustomized()) {
+                size = diskOffering.getDiskSize();
+                if (update) {
+                    resizedVolume.setSize(diskOffering.getDiskSize());
+                }
             }
-            if (diskOffering.getMaxIops() != null) {
-                resizedVolume.setMaxIops(diskOffering.getMaxIops());
+
+            if (update) {
+                if (diskOffering.getMinIops() != null) {
+                    resizedVolume.setMinIops(diskOffering.getMinIops());
+                }
+                if (diskOffering.getMaxIops() != null) {
+                    resizedVolume.setMaxIops(diskOffering.getMaxIops());
+                }
             }
         }
 
+        // Size of disk should be greater than or equal to the template's size 
and this should be validated before this
         if (MapUtils.isNotEmpty(details)) {
             if 
(StringUtils.isNumeric(details.get(VmDetailConstants.ROOT_DISK_SIZE))) {
                 Long rootDiskSize = 
Long.parseLong(details.get(VmDetailConstants.ROOT_DISK_SIZE)) * GiB_TO_BYTES;
-                resizedVolume.setSize(rootDiskSize);
+                size = rootDiskSize;
+                if (update) {
+                    resizedVolume.setSize(rootDiskSize);
+                }
                 UserVmDetailVO vmRootDiskSizeDetail = 
userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE);
-                if (vmRootDiskSizeDetail != null) {
-                    
vmRootDiskSizeDetail.setValue(details.get(VmDetailConstants.ROOT_DISK_SIZE));
-                    userVmDetailsDao.update(vmRootDiskSizeDetail.getId(), 
vmRootDiskSizeDetail);
-                } else {
-                    userVmDetailsDao.persist(new 
UserVmDetailVO(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE,
-                            details.get(VmDetailConstants.ROOT_DISK_SIZE), 
true));
+                if (update) {
+                    if (vmRootDiskSizeDetail != null) {
+                        
vmRootDiskSizeDetail.setValue(details.get(VmDetailConstants.ROOT_DISK_SIZE));
+                        userVmDetailsDao.update(vmRootDiskSizeDetail.getId(), 
vmRootDiskSizeDetail);
+                    } else {
+                        userVmDetailsDao.persist(new 
UserVmDetailVO(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE,
+                                details.get(VmDetailConstants.ROOT_DISK_SIZE), 
true));
+                    }
                 }
             }
+            if (update) {
+                String minIops = details.get(MIN_IOPS);
+                String maxIops = details.get(MAX_IOPS);
 
-            String minIops = details.get(MIN_IOPS);
-            String maxIops = details.get(MAX_IOPS);
-
-            if (StringUtils.isNumeric(minIops)) {
-                resizedVolume.setMinIops(Long.parseLong(minIops));
-            }
-            if (StringUtils.isNumeric(maxIops)) {
-                resizedVolume.setMinIops(Long.parseLong(maxIops));
+                if (StringUtils.isNumeric(minIops)) {
+                    resizedVolume.setMinIops(Long.parseLong(minIops));
+                }
+                if (StringUtils.isNumeric(maxIops)) {
+                    resizedVolume.setMinIops(Long.parseLong(maxIops));
+                }
             }
         }
-        _volsDao.update(resizedVolume.getId(), resizedVolume);
+        if (update) {
+            _volsDao.update(resizedVolume.getId(), resizedVolume);
+        }
+        return size;
     }
 
     private void updateVMDynamicallyScalabilityUsingTemplate(UserVmVO vm, Long 
newTemplateId) {
@@ -8206,8 +8231,8 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
             _resourceLimitMgr.checkVmResourceLimitsForTemplateChange(owner, 
vm.isDisplay(), serviceOffering, currentTemplate, template);
         }
 
-        Long newSize = getRootVolumeSizeForVmTemplateRestore(vm, template, 
newDiskOffering, details);
         for (Volume vol : rootVolumes) {
+            Long newSize = getRootVolumeSizeForVmRestore(vol, template, vm, 
newDiskOffering, details, false);
             if (newSize == null) {
                 newSize = vol.getSize();
             }
@@ -8219,33 +8244,6 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
         }
     }
 
-    protected Long getRootVolumeSizeForVmTemplateRestore(
-            UserVmVO userVm, VMTemplateVO template, DiskOffering diskOffering, 
Map<String, String> details
-    ) {
-        Long size = null;
-        if (template != null && template.getSize() != null) {
-            UserVmDetailVO vmRootDiskSizeDetail = 
userVmDetailsDao.findDetail(userVm.getId(), VmDetailConstants.ROOT_DISK_SIZE);
-            if (vmRootDiskSizeDetail == null) {
-                size = template.getSize();
-            } else {
-                long rootDiskSize = 
Long.parseLong(vmRootDiskSizeDetail.getValue()) * GiB_TO_BYTES;
-                if (template.getSize() >= rootDiskSize) {
-                    size = template.getSize();
-                } else {
-                    size = rootDiskSize;
-                }
-            }
-        }
-
-        if (diskOffering != null && !diskOffering.isCustomized()) {
-            size = diskOffering.getDiskSize();
-        }
-
-        if (MapUtils.isNotEmpty(details) && 
StringUtils.isNumeric(details.get(VmDetailConstants.ROOT_DISK_SIZE))) {
-            size = 
Long.parseLong(details.get(VmDetailConstants.ROOT_DISK_SIZE)) * GiB_TO_BYTES;
-        }
-        return size;
-    }
 
     private void handleManagedStorage(UserVmVO vm, VolumeVO root) {
         if (Volume.State.Allocated.equals(root.getState())) {
diff --git a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java 
b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
index f97334d87c1..323a1ed9416 100644
--- a/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
+++ b/server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java
@@ -40,6 +40,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import com.cloud.offering.DiskOffering;
 import org.apache.cloudstack.api.BaseCmd.HTTPMethod;
 import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
 import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd;
@@ -1565,7 +1566,7 @@ public class UserVmManagerImplTest {
     }
 
     @Test
-    public void testGetRootVolumeSizeForVmTemplateRestore() {
+    public void testGetRootVolumeSizeForVmRestore() {
         VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
         Mockito.when(template.getSize()).thenReturn(10L * GiB_TO_BYTES);
         UserVmVO userVm = Mockito.mock(UserVmVO.class);
@@ -1578,12 +1579,12 @@ public class UserVmManagerImplTest {
         UserVmDetailVO vmRootDiskSizeDetail = 
Mockito.mock(UserVmDetailVO.class);
         Mockito.when(vmRootDiskSizeDetail.getValue()).thenReturn("20");
         Mockito.when(userVmDetailsDao.findDetail(1L, 
VmDetailConstants.ROOT_DISK_SIZE)).thenReturn(vmRootDiskSizeDetail);
-        Long actualSize = 
userVmManagerImpl.getRootVolumeSizeForVmTemplateRestore(userVm, template, 
diskOffering, details);
+        Long actualSize = 
userVmManagerImpl.getRootVolumeSizeForVmRestore(null, template, userVm, 
diskOffering, details, false);
         Assert.assertEquals(16 * GiB_TO_BYTES, actualSize.longValue());
     }
 
     @Test
-    public void 
testGetRootVolumeSizeForVmTemplateRestoreNullDiskOfferingAndEmptyDetails() {
+    public void 
testGetRootVolumeSizeForVmRestoreNullDiskOfferingAndEmptyDetails() {
         VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
         Mockito.when(template.getSize()).thenReturn(10L * GiB_TO_BYTES);
         UserVmVO userVm = Mockito.mock(UserVmVO.class);
@@ -1593,7 +1594,7 @@ public class UserVmManagerImplTest {
         UserVmDetailVO vmRootDiskSizeDetail = 
Mockito.mock(UserVmDetailVO.class);
         Mockito.when(vmRootDiskSizeDetail.getValue()).thenReturn("20");
         Mockito.when(userVmDetailsDao.findDetail(1L, 
VmDetailConstants.ROOT_DISK_SIZE)).thenReturn(vmRootDiskSizeDetail);
-        Long actualSize = 
userVmManagerImpl.getRootVolumeSizeForVmTemplateRestore(userVm, template, 
diskOffering, details);
+        Long actualSize = 
userVmManagerImpl.getRootVolumeSizeForVmRestore(null, template, userVm, 
diskOffering, details, false);
         Assert.assertEquals(20 * GiB_TO_BYTES, actualSize.longValue());
     }
 }
diff --git a/test/integration/component/test_resource_limit_tags.py 
b/test/integration/component/test_resource_limit_tags.py
index feb5c7820e2..916abff4db8 100644
--- a/test/integration/component/test_resource_limit_tags.py
+++ b/test/integration/component/test_resource_limit_tags.py
@@ -28,6 +28,7 @@ from marvin.lib.base import (Host,
                              Domain,
                              Zone,
                              ServiceOffering,
+                             Template,
                              DiskOffering,
                              VirtualMachine,
                              Volume,
@@ -56,6 +57,7 @@ class TestResourceLimitTags(cloudstackTestCase):
     def setUpClass(cls):
         testClient = super(TestResourceLimitTags, cls).getClsTestClient()
         cls.apiclient = testClient.getApiClient()
+        cls.hypervisor = testClient.getHypervisorInfo()
         cls.services = testClient.getParsedTestDataConfig()
 
         # Get Zone, Domain and templates
@@ -646,3 +648,45 @@ class TestResourceLimitTags(cloudstackTestCase):
                 expected_usage_total = 2 * expected_usage_total
             self.assertTrue(usage.total == expected_usage_total, "Usage for %s 
with tag %s is not matching for target account" % (usage.resourcetypename, 
usage.tag))
         return
+
+    @attr(tags=["devcloud", "advanced", "advancedns", "smoke", "basic", "sg"], 
required_hardware="false")
+    def test_13_verify_restore_vm_limit(self):
+        """Test to verify limits are updated on restoring VM
+        """
+        hypervisor = self.hypervisor.lower()
+        restore_template_service = self.services["test_templates"][
+            hypervisor if hypervisor != 'simulator' else 'xenserver'].copy()
+        restore_template = Template.register(self.apiclient, 
restore_template_service, zoneid=self.zone.id, hypervisor=hypervisor, 
templatetag=self.host_tags[1])
+        restore_template.download(self.apiclient)
+        self.cleanup.append(restore_template)
+
+        self.vm = VirtualMachine.create(
+            self.userapiclient,
+            self.services["virtual_machine"],
+            templateid=restore_template.id,
+            serviceofferingid=self.host_storage_tagged_compute_offering.id,
+            mode=self.services["mode"]
+        )
+        self.cleanup.append(self.vm)
+        old_root_vol = Volume.list(self.userapiclient, 
virtualmachineid=self.vm.id)[0]
+
+        acc = Account.list(
+            self.userapiclient,
+            id=self.account.id
+        )[0]
+        tags = [self.host_storage_tagged_compute_offering.hosttags, 
self.host_storage_tagged_compute_offering.storagetags]
+        account_usage_before = list(filter(lambda x: x.tag in tags, 
acc['taggedresources']))
+
+        self.vm.restore(self.userapiclient, restore_template.id, 
rootdisksize=16, expunge=True)
+        acc = Account.list(
+            self.userapiclient,
+            id=self.account.id
+        )[0]
+
+        account_usage_after = list(filter(lambda x: x.tag in tags, 
acc['taggedresources']))
+        for idx, usage in enumerate(account_usage_after):
+            expected_usage_total = account_usage_before[idx].total
+            if usage.resourcetype in [10]:
+                expected_usage_total = expected_usage_total - 
old_root_vol.size + 16 * 1024 * 1024 * 1024
+            self.assertTrue(usage.total == expected_usage_total, "Usage for %s 
with tag %s is not matching for target account" % (usage.resourcetypename, 
usage.tag))
+        return
diff --git a/test/integration/smoke/test_restore_vm.py 
b/test/integration/smoke/test_restore_vm.py
index dd33346ed9e..57b8ec04174 100644
--- a/test/integration/smoke/test_restore_vm.py
+++ b/test/integration/smoke/test_restore_vm.py
@@ -18,7 +18,7 @@
 """
 # Import Local Modules
 from marvin.cloudstackTestCase import cloudstackTestCase
-from marvin.lib.base import (VirtualMachine, Volume, ServiceOffering, Template)
+from marvin.lib.base import (VirtualMachine, Volume, DiskOffering, 
ServiceOffering, Template)
 from marvin.lib.common import (get_zone, get_domain)
 from nose.plugins.attrib import attr
 
@@ -45,6 +45,9 @@ class TestRestoreVM(cloudstackTestCase):
         cls.service_offering = ServiceOffering.create(cls.apiclient, 
cls.services["service_offering"])
         cls._cleanup.append(cls.service_offering)
 
+        cls.disk_offering = DiskOffering.create(cls.apiclient, 
cls.services["disk_offering"], disksize='8')
+        cls._cleanup.append(cls.disk_offering)
+
         template_t1 = Template.register(cls.apiclient, 
cls.services["test_templates"][
             cls.hypervisor.lower() if cls.hypervisor.lower() != 'simulator' 
else 'xenserver'],
                                             zoneid=cls.zone.id, 
hypervisor=cls.hypervisor.lower())
@@ -74,20 +77,82 @@ class TestRestoreVM(cloudstackTestCase):
                                                 
serviceofferingid=self.service_offering.id)
         self._cleanup.append(virtual_machine)
 
-        root_vol = Volume.list(self.apiclient, 
virtualmachineid=virtual_machine.id)[0]
-        self.assertEqual(root_vol.state, 'Ready', "Volume should be in Ready 
state")
-        self.assertEqual(root_vol.size, self.template_t1.size, "Size of volume 
and template should match")
+        old_root_vol = Volume.list(self.apiclient, 
virtualmachineid=virtual_machine.id)[0]
+        self.assertEqual(old_root_vol.state, 'Ready', "Volume should be in 
Ready state")
+        self.assertEqual(old_root_vol.size, self.template_t1.size, "Size of 
volume and template should match")
+
+        virtual_machine.restore(self.apiclient, self.template_t2.id, 
expunge=True)
 
-        virtual_machine.restore(self.apiclient, self.template_t2.id)
         restored_vm = VirtualMachine.list(self.apiclient, 
id=virtual_machine.id)[0]
         self.assertEqual(restored_vm.state, 'Running', "VM should be in a 
running state")
         self.assertEqual(restored_vm.templateid, self.template_t2.id, "VM's 
template after restore is incorrect")
+
         root_vol = Volume.list(self.apiclient, 
virtualmachineid=restored_vm.id)[0]
         self.assertEqual(root_vol.state, 'Ready', "Volume should be in Ready 
state")
         self.assertEqual(root_vol.size, self.template_t2.size, "Size of volume 
and template should match")
 
+        old_root_vol = Volume.list(self.apiclient, id=old_root_vol.id)
+        self.assertEqual(old_root_vol, None, "Old volume should be deleted")
+
+    @attr(tags=["advanced", "basic"], required_hardware="false")
+    def test_02_restore_vm_with_disk_offering(self):
+        """Test restore virtual machine
+        """
+        # create a virtual machine
+        virtual_machine = VirtualMachine.create(self.apiclient, 
self.services["virtual_machine"], zoneid=self.zone.id,
+                                                templateid=self.template_t1.id,
+                                                
serviceofferingid=self.service_offering.id)
+        self._cleanup.append(virtual_machine)
+
+        old_root_vol = Volume.list(self.apiclient, 
virtualmachineid=virtual_machine.id)[0]
+        self.assertEqual(old_root_vol.state, 'Ready', "Volume should be in 
Ready state")
+        self.assertEqual(old_root_vol.size, self.template_t1.size, "Size of 
volume and template should match")
+
+        virtual_machine.restore(self.apiclient, self.template_t2.id, 
self.disk_offering.id, expunge=True)
+
+        restored_vm = VirtualMachine.list(self.apiclient, 
id=virtual_machine.id)[0]
+        self.assertEqual(restored_vm.state, 'Running', "VM should be in a 
running state")
+        self.assertEqual(restored_vm.templateid, self.template_t2.id, "VM's 
template after restore is incorrect")
+
+        root_vol = Volume.list(self.apiclient, 
virtualmachineid=restored_vm.id)[0]
+        self.assertEqual(root_vol.diskofferingid, self.disk_offering.id, "Disk 
offering id should match")
+        self.assertEqual(root_vol.state, 'Ready', "Volume should be in Ready 
state")
+        self.assertEqual(root_vol.size, self.disk_offering.disksize * 1024 * 
1024 * 1024, "Size of volume and disk offering should match")
+
+        old_root_vol = Volume.list(self.apiclient, id=old_root_vol.id)
+        self.assertEqual(old_root_vol, None, "Old volume should be deleted")
+
     @attr(tags=["advanced", "basic"], required_hardware="false")
-    def test_02_restore_vm_allocated_root(self):
+    def test_03_restore_vm_with_disk_offering_custom_size(self):
+        """Test restore virtual machine
+        """
+        # create a virtual machine
+        virtual_machine = VirtualMachine.create(self.apiclient, 
self.services["virtual_machine"], zoneid=self.zone.id,
+                                                templateid=self.template_t1.id,
+                                                
serviceofferingid=self.service_offering.id)
+        self._cleanup.append(virtual_machine)
+
+        old_root_vol = Volume.list(self.apiclient, 
virtualmachineid=virtual_machine.id)[0]
+        self.assertEqual(old_root_vol.state, 'Ready', "Volume should be in 
Ready state")
+        self.assertEqual(old_root_vol.size, self.template_t1.size, "Size of 
volume and template should match")
+
+        virtual_machine.restore(self.apiclient, self.template_t2.id, 
self.disk_offering.id, rootdisksize=16)
+
+        restored_vm = VirtualMachine.list(self.apiclient, 
id=virtual_machine.id)[0]
+        self.assertEqual(restored_vm.state, 'Running', "VM should be in a 
running state")
+        self.assertEqual(restored_vm.templateid, self.template_t2.id, "VM's 
template after restore is incorrect")
+
+        root_vol = Volume.list(self.apiclient, 
virtualmachineid=restored_vm.id)[0]
+        self.assertEqual(root_vol.diskofferingid, self.disk_offering.id, "Disk 
offering id should match")
+        self.assertEqual(root_vol.state, 'Ready', "Volume should be in Ready 
state")
+        self.assertEqual(root_vol.size, 16 * 1024 * 1024 * 1024, "Size of 
volume and custom disk size should match")
+
+        old_root_vol = Volume.list(self.apiclient, id=old_root_vol.id)[0]
+        self.assertEqual(old_root_vol.state, "Destroy", "Old volume should be 
in Destroy state")
+        Volume.delete(old_root_vol, self.apiclient)
+
+    @attr(tags=["advanced", "basic"], required_hardware="false")
+    def test_04_restore_vm_allocated_root(self):
         """Test restore virtual machine with root disk in allocated state
         """
         # create a virtual machine with allocated root disk by setting 
startvm=False
@@ -96,9 +161,9 @@ class TestRestoreVM(cloudstackTestCase):
                                                 
serviceofferingid=self.service_offering.id,
                                                 startvm=False)
         self._cleanup.append(virtual_machine)
-        root_vol = Volume.list(self.apiclient, 
virtualmachineid=virtual_machine.id)[0]
-        self.assertEqual(root_vol.state, 'Allocated', "Volume should be in 
Allocated state")
-        self.assertEqual(root_vol.size, self.template_t1.size, "Size of volume 
and template should match")
+        old_root_vol = Volume.list(self.apiclient, 
virtualmachineid=virtual_machine.id)[0]
+        self.assertEqual(old_root_vol.state, 'Allocated', "Volume should be in 
Allocated state")
+        self.assertEqual(old_root_vol.size, self.template_t1.size, "Size of 
volume and template should match")
 
         virtual_machine.restore(self.apiclient, self.template_t2.id)
         restored_vm = VirtualMachine.list(self.apiclient, 
id=virtual_machine.id)[0]
@@ -112,3 +177,6 @@ class TestRestoreVM(cloudstackTestCase):
         virtual_machine.start(self.apiclient)
         root_vol = Volume.list(self.apiclient, 
virtualmachineid=restored_vm.id)[0]
         self.assertEqual(root_vol.state, 'Ready', "Volume should be in Ready 
state")
+
+        old_root_vol = Volume.list(self.apiclient, id=old_root_vol.id)
+        self.assertEqual(old_root_vol, None, "Old volume should be deleted")
diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py
index 04d4e6810c4..a855908eb0d 100755
--- a/tools/marvin/marvin/lib/base.py
+++ b/tools/marvin/marvin/lib/base.py
@@ -776,12 +776,25 @@ class VirtualMachine:
         if response[0] == FAIL:
             raise Exception(response[1])
 
-    def restore(self, apiclient, templateid=None):
+    def restore(self, apiclient, templateid=None, diskofferingid=None, 
rootdisksize=None, expunge=None, details=None):
         """Restore the instance"""
         cmd = restoreVirtualMachine.restoreVirtualMachineCmd()
         cmd.virtualmachineid = self.id
         if templateid:
             cmd.templateid = templateid
+        if diskofferingid:
+            cmd.diskofferingid = diskofferingid
+        if rootdisksize:
+            cmd.rootdisksize = rootdisksize
+        if expunge is not None:
+            cmd.expunge = expunge
+        if details:
+            for key, value in list(details.items()):
+                cmd.details.append({
+                    'key': key,
+                    'value': value
+                })
+
         return apiclient.restoreVirtualMachine(cmd)
 
     def get_ssh_client(
@@ -1457,7 +1470,7 @@ class Template:
     @classmethod
     def register(cls, apiclient, services, zoneid=None,
                  account=None, domainid=None, hypervisor=None,
-                 projectid=None, details=None, randomize_name=True):
+                 projectid=None, details=None, randomize_name=True, 
templatetag=None):
         """Create template from URL"""
 
         # Create template from Virtual machine and Volume ID
@@ -1522,6 +1535,9 @@ class Template:
         if details:
             cmd.details = details
 
+        if templatetag:
+            cmd.templatetag = templatetag
+
         if "directdownload" in services:
             cmd.directdownload = services["directdownload"]
         if "checksum" in services:

Reply via email to