diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java index 64998a74954..7caa4ce710f 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java @@ -17,6 +17,7 @@ package org.apache.cloudstack.api.command.user.backup.repository; +import com.cloud.utils.StringUtils; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -100,7 +101,7 @@ public class AddBackupRepositoryCmd extends BaseCmd { } public String getMountOptions() { - return mountOptions == null ? "" : mountOptions; + return StringUtils.isBlank(mountOptions) ? "" : mountOptions; } public Long getZoneId() { diff --git a/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java index 327bbae0051..0d3c830950b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/BackupRepositoryResponse.java @@ -57,6 +57,10 @@ public class BackupRepositoryResponse extends BaseResponse { @Param(description = "backup type") private String type; + @SerializedName(ApiConstants.MOUNT_OPTIONS) + @Param(description = "mount options", since = "4.22.1") + private String mountOptions; + @SerializedName(ApiConstants.CAPACITY_BYTES) @Param(description = "capacity of the backup repository") private Long capacityBytes; @@ -128,6 +132,14 @@ public class BackupRepositoryResponse extends BaseResponse { this.type = type; } + public String getMountOptions() { + return mountOptions; + } + + public void setMountOptions(String mountOptions) { + this.mountOptions = mountOptions; + } + public Long getCapacityBytes() { return capacityBytes; } diff --git a/api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.java b/api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.java index 875fc3b3d90..cc8144ebe40 100644 --- a/api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.java +++ b/api/src/main/java/org/apache/cloudstack/backup/BackupRepositoryService.java @@ -32,5 +32,4 @@ public interface BackupRepositoryService { BackupRepository updateBackupRepository(UpdateBackupRepositoryCmd cmd); boolean deleteBackupRepository(DeleteBackupRepositoryCmd cmd); Pair, Integer> listBackupRepositories(ListBackupRepositoriesCmd cmd); - } diff --git a/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java b/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java index cf49217ef5b..6fe001de72c 100755 --- a/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java +++ b/core/src/main/java/com/cloud/storage/template/HttpTemplateDownloader.java @@ -151,7 +151,7 @@ public class HttpTemplateDownloader extends ManagedContextRunnable implements Te client.getParams().setAuthenticationPreemptive(true); Credentials defaultcreds = new UsernamePasswordCredentials(user, password); client.getState().setCredentials(new AuthScope(hostAndPort.first(), hostAndPort.second(), AuthScope.ANY_REALM), defaultcreds); - logger.info("Added username=" + user + ", password=" + password + "for host " + hostAndPort.first() + ":" + hostAndPort.second()); + logger.info("Added username={}, password=****** for host {}:{}", user, hostAndPort.first(), hostAndPort.second()); } else { logger.info("No credentials configured for host=" + hostAndPort.first() + ":" + hostAndPort.second()); } diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java index 0f47c9e7155..44c0be1cb59 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java @@ -163,5 +163,7 @@ public interface VolumeDao extends GenericDao, StateDao implements Vol protected GenericSearchBuilder primaryStorageSearch2; protected GenericSearchBuilder secondaryStorageSearch; private final SearchBuilder poolAndPathSearch; + final GenericSearchBuilder CountByOfferingId; @Inject ReservationDao reservationDao; @@ -506,6 +507,11 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol poolAndPathSearch.and("poolId", poolAndPathSearch.entity().getPoolId(), Op.EQ); poolAndPathSearch.and("path", poolAndPathSearch.entity().getPath(), Op.EQ); poolAndPathSearch.done(); + + CountByOfferingId = createSearchBuilder(Integer.class); + CountByOfferingId.select(null, Func.COUNT, CountByOfferingId.entity().getId()); + CountByOfferingId.and("diskOfferingId", CountByOfferingId.entity().getDiskOfferingId(), Op.EQ); + CountByOfferingId.done(); } @Override @@ -914,6 +920,14 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol return findOneIncludingRemovedBy(sc); } + @Override + public int getVolumeCountByOfferingId(long diskOfferingId) { + SearchCriteria sc = CountByOfferingId.create(); + sc.setParameters("diskOfferingId", diskOfferingId); + List results = customSearch(sc, null); + return results.get(0); + } + @Override public VolumeVO findByLastIdAndState(long lastVolumeId, State ...states) { QueryBuilder sc = QueryBuilder.create(VolumeVO.class); diff --git a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java index 384826227af..cccfbe8a006 100644 --- a/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java +++ b/engine/schema/src/main/java/com/cloud/upgrade/DatabaseCreator.java @@ -99,7 +99,7 @@ public class DatabaseCreator { String username = dbProperties.getProperty(String.format("db.%s.username", database)); String password = dbProperties.getProperty(String.format("db.%s.password", database)); String dbName = dbProperties.getProperty(String.format("db.%s.name", database)); - System.out.println(String.format("========> Initializing database=%s with host=%s port=%s username=%s password=%s", dbName, host, port, username, password)); + System.out.println(String.format("========> Initializing database=%s with host=%s port=%s username=%s password=******", dbName, host, port, username)); List queries = new ArrayList(); queries.add(String.format("drop database if exists `%s`", dbName)); diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java index f722b4c54e4..23541c2431e 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDao.java @@ -187,5 +187,9 @@ public interface VMInstanceDao extends GenericDao, StateDao< Map getNameIdMapForVmIds(Collection ids); + int getVmCountByOfferingId(Long serviceOfferingId); + + int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List domainIds); + List listByIdsIncludingRemoved(List ids); } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java index 5fe6e2e547e..b4ad7d2f42d 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -104,6 +104,8 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected SearchBuilder LastHostAndStatesSearch; protected SearchBuilder VmsNotInClusterUsingPool; protected SearchBuilder IdsPowerStateSelectSearch; + GenericSearchBuilder CountByOfferingId; + GenericSearchBuilder CountUserVmNotInDomain; @Inject ResourceTagDao tagsDao; @@ -354,6 +356,18 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem IdsPowerStateSelectSearch.entity().getPowerStateUpdateCount(), IdsPowerStateSelectSearch.entity().getPowerStateUpdateTime()); IdsPowerStateSelectSearch.done(); + + CountByOfferingId = createSearchBuilder(Integer.class); + CountByOfferingId.select(null, Func.COUNT, CountByOfferingId.entity().getId()); + CountByOfferingId.and("serviceOfferingId", CountByOfferingId.entity().getServiceOfferingId(), Op.EQ); + CountByOfferingId.done(); + + CountUserVmNotInDomain = createSearchBuilder(Integer.class); + CountUserVmNotInDomain.select(null, Func.COUNT, CountUserVmNotInDomain.entity().getId()); + CountUserVmNotInDomain.and("serviceOfferingId", CountUserVmNotInDomain.entity().getServiceOfferingId(), Op.EQ); + CountUserVmNotInDomain.and("domainIdsNotIn", CountUserVmNotInDomain.entity().getDomainId(), Op.NIN); + CountUserVmNotInDomain.done(); + } @Override @@ -1247,6 +1261,29 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem .collect(Collectors.toMap(VMInstanceVO::getInstanceName, VMInstanceVO::getId)); } + @Override + public int getVmCountByOfferingId(Long serviceOfferingId) { + if (serviceOfferingId == null) { + return 0; + } + SearchCriteria sc = CountByOfferingId.create(); + sc.setParameters("serviceOfferingId", serviceOfferingId); + List count = customSearch(sc, null); + return count.get(0); + } + + @Override + public int getVmCountByOfferingNotInDomain(Long serviceOfferingId, List domainIds) { + if (serviceOfferingId == null || CollectionUtils.isEmpty(domainIds)) { + return 0; + } + SearchCriteria sc = CountUserVmNotInDomain.create(); + sc.setParameters("serviceOfferingId", serviceOfferingId); + sc.setParameters("domainIdsNotIn", domainIds.toArray()); + List count = customSearch(sc, null); + return count.get(0); + } + @Override public List listByIdsIncludingRemoved(List ids) { SearchBuilder idsSearch = createSearchBuilder(); diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 8cc10ce4167..3c7eeaf3b8f 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -5530,6 +5530,9 @@ public class ApiResponseHelper implements ResponseGenerator { response.setAddress(backupRepository.getAddress()); response.setProviderName(backupRepository.getProvider()); response.setType(backupRepository.getType()); + if (StringUtils.isNotBlank(backupRepository.getMountOptions())) { + response.setMountOptions(backupRepository.getMountOptions()); + } response.setCapacityBytes(backupRepository.getCapacityBytes()); response.setCrossZoneInstanceCreation(backupRepository.crossZoneInstanceCreationEnabled()); response.setObjectName("backuprepository"); diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java index 21995d5ae65..db402780686 100644 --- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java @@ -49,10 +49,6 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.consoleproxy.ConsoleProxyManager; -import com.cloud.network.router.VirtualNetworkApplianceManager; -import com.cloud.storage.secondary.SecondaryStorageVmManager; -import com.cloud.vm.VirtualMachineManager; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.acl.SecurityChecker; import org.apache.cloudstack.affinity.AffinityGroup; @@ -154,6 +150,7 @@ import com.cloud.api.query.vo.NetworkOfferingJoinVO; import com.cloud.capacity.CapacityManager; import com.cloud.capacity.dao.CapacityDao; import com.cloud.configuration.Resource.ResourceType; +import com.cloud.consoleproxy.ConsoleProxyManager; import com.cloud.dc.AccountVlanMapVO; import com.cloud.dc.ClusterDetailsDao; import com.cloud.dc.ClusterDetailsVO; @@ -204,6 +201,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.exception.UnsupportedServiceException; import com.cloud.gpu.GPU; import com.cloud.gpu.VgpuProfileVO; import com.cloud.gpu.dao.VgpuProfileDao; @@ -247,6 +245,7 @@ import com.cloud.network.dao.UserIpv6AddressDao; import com.cloud.network.element.NetrisProviderVO; import com.cloud.network.element.NsxProviderVO; import com.cloud.network.netris.NetrisService; +import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.rules.LoadBalancerContainer.Scheme; import com.cloud.network.vpc.VpcManager; import com.cloud.offering.DiskOffering; @@ -281,6 +280,7 @@ import com.cloud.storage.dao.DiskOfferingDao; import com.cloud.storage.dao.StoragePoolTagsDao; import com.cloud.storage.dao.VMTemplateZoneDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.storage.secondary.SecondaryStorageVmManager; import com.cloud.test.IPRangeConfig; import com.cloud.user.Account; import com.cloud.user.AccountDetailVO; @@ -314,6 +314,7 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.NicIpAlias; import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineManager; import com.cloud.vm.VmDetailConstants; import com.cloud.vm.dao.NicIpAliasDao; import com.cloud.vm.dao.NicIpAliasVO; @@ -3910,6 +3911,12 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati List filteredDomainIds = filterChildSubDomains(domainIds); Collections.sort(filteredDomainIds); + // avoid domain update of service offering if any instance is associated to it + int instanceCount = _vmInstanceDao.getVmCountByOfferingNotInDomain(offeringHandle.getId(), filteredDomainIds); + if (instanceCount > 0) { + throw new UnsupportedServiceException("There are Instances associated to this service offering outside of the specified domains."); + } + List filteredZoneIds = new ArrayList<>(); if (CollectionUtils.isNotEmpty(zoneIds)) { filteredZoneIds.addAll(zoneIds); diff --git a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java index c17f80d2ad8..3303bc02933 100644 --- a/server/src/main/java/com/cloud/hypervisor/KVMGuru.java +++ b/server/src/main/java/com/cloud/hypervisor/KVMGuru.java @@ -134,7 +134,8 @@ public class KVMGuru extends HypervisorGuruBase implements HypervisorGuru { VirtualMachine vm = vmProfile.getVirtualMachine(); HostVO host = hostDao.findById(vm.getHostId()); if (host == null) { - throw new CloudRuntimeException("Host with id: " + vm.getHostId() + " not found"); + logger.warn("Host is not available. Skipping setting CPU quota percentage for VM: {}", vm); + return; } logger.debug("Limiting CPU usage for VM: {} on host: {}", vm, host); double hostMaxSpeed = getHostCPUSpeed(host); diff --git a/server/src/main/java/com/cloud/user/DomainManagerImpl.java b/server/src/main/java/com/cloud/user/DomainManagerImpl.java index 6fc9c6f5ef5..28f9bd3ab39 100644 --- a/server/src/main/java/com/cloud/user/DomainManagerImpl.java +++ b/server/src/main/java/com/cloud/user/DomainManagerImpl.java @@ -34,6 +34,8 @@ import com.cloud.api.query.vo.NetworkOfferingJoinVO; import com.cloud.api.query.vo.VpcOfferingJoinVO; import com.cloud.configuration.Resource; import com.cloud.domain.dao.DomainDetailsDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.vpc.dao.VpcDao; import com.cloud.network.vpc.dao.VpcOfferingDao; import com.cloud.network.vpc.dao.VpcOfferingDetailsDao; import com.cloud.offerings.dao.NetworkOfferingDao; @@ -85,6 +87,7 @@ import com.cloud.projects.dao.ProjectDao; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.service.dao.ServiceOfferingDetailsDao; import com.cloud.storage.dao.DiskOfferingDao; +import com.cloud.storage.dao.VolumeDao; import com.cloud.user.dao.AccountDao; import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; @@ -101,6 +104,8 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; import com.cloud.vm.ReservationContext; import com.cloud.vm.ReservationContextImpl; +import com.cloud.vm.dao.VMInstanceDao; + import org.apache.commons.lang3.StringUtils; @Component @@ -141,6 +146,14 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom @Inject private ProjectDao _projectDao; @Inject + private VMInstanceDao vmInstanceDao; + @Inject + private NetworkDao networkDao; + @Inject + private VolumeDao volumeDao; + @Inject + private VpcDao vpcDao; + @Inject private ProjectManager _projectMgr; @Inject private RegionManager _regionMgr; @@ -543,7 +556,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom List vpcOfferingsDetailsToRemove = new ArrayList<>(); List vpcOfferingsForThisDomain = vpcOfferingJoinDao.findByDomainId(domainId); for (VpcOfferingJoinVO vpcOffering : vpcOfferingsForThisDomain) { - if (domainIdString.equals(vpcOffering.getDomainId())) { + int vpcCount = vpcDao.getVpcCountByOfferingId(vpcOffering.getId()); + if (vpcCount == 0) { vpcOfferingDao.remove(vpcOffering.getId()); } else { vpcOfferingsDetailsToRemove.add(vpcOffering.getId()); @@ -558,7 +572,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom List networkOfferingsDetailsToRemove = new ArrayList<>(); List networkOfferingsForThisDomain = networkOfferingJoinDao.findByDomainId(domainId, false); for (NetworkOfferingJoinVO networkOffering : networkOfferingsForThisDomain) { - if (domainIdString.equals(networkOffering.getDomainId())) { + int networkCount = networkDao.getNetworkCountByNetworkOffId(networkOffering.getId()); + if (networkCount == 0) { networkOfferingDao.remove(networkOffering.getId()); } else { networkOfferingsDetailsToRemove.add(networkOffering.getId()); @@ -573,7 +588,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom List serviceOfferingsDetailsToRemove = new ArrayList<>(); List serviceOfferingsForThisDomain = serviceOfferingJoinDao.findByDomainId(domainId); for (ServiceOfferingJoinVO serviceOffering : serviceOfferingsForThisDomain) { - if (domainIdString.equals(serviceOffering.getDomainId())) { + int vmCount = vmInstanceDao.getVmCountByOfferingId(serviceOffering.getId()); + if (vmCount == 0) { serviceOfferingDao.remove(serviceOffering.getId()); } else { serviceOfferingsDetailsToRemove.add(serviceOffering.getId()); @@ -588,7 +604,8 @@ public class DomainManagerImpl extends ManagerBase implements DomainManager, Dom List diskOfferingsDetailsToRemove = new ArrayList<>(); List diskOfferingsForThisDomain = diskOfferingJoinDao.findByDomainId(domainId); for (DiskOfferingJoinVO diskOffering : diskOfferingsForThisDomain) { - if (domainIdString.equals(diskOffering.getDomainId())) { + int volumeCount = volumeDao.getVolumeCountByOfferingId(diskOffering.getId()); + if (volumeCount == 0) { diskOfferingDao.remove(diskOffering.getId()); } else { diskOfferingsDetailsToRemove.add(diskOffering.getId()); diff --git a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java index 17b4eed33d3..d8d62df1a72 100644 --- a/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java +++ b/server/src/test/java/com/cloud/hypervisor/KVMGuruTest.java @@ -32,7 +32,6 @@ import com.cloud.storage.GuestOSVO; import com.cloud.storage.dao.GuestOSDao; import com.cloud.storage.dao.GuestOSHypervisorDao; import com.cloud.utils.Pair; -import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachineProfile; import org.apache.cloudstack.api.ApiConstants; @@ -141,10 +140,11 @@ public class KVMGuruTest { Mockito.verify(vmTO).setCpuQuotaPercentage(Mockito.anyDouble()); } - @Test(expected = CloudRuntimeException.class) + @Test public void testSetVmQuotaPercentageNullHost() { Mockito.when(hostDao.findById(hostId)).thenReturn(null); guru.setVmQuotaPercentage(vmTO, vmProfile); + Mockito.verify(vmTO, Mockito.never()).setCpuQuotaPercentage(Mockito.anyDouble()); } @Test diff --git a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java index c6d1dae461c..288a108249e 100644 --- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java +++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java @@ -788,11 +788,8 @@ public class VirtualMachineMO extends BaseMO { cloneSpec.setMemory(false); cloneSpec.setConfig(vmConfigSpec); - ManagedObjectReference morTask = _context.getService().cloneVMTask(_mor, morFolder, cloneName, cloneSpec); - - boolean result = _context.getVimClient().waitForTask(morTask); + boolean result = cloneVM(cloneName, morFolder, cloneSpec); if (result) { - _context.waitForTaskProgressDone(morTask); VirtualMachineMO clonedVm = dcMo.findVm(cloneName); if (clonedVm == null) { logger.error(String.format("Failed to clone Instance %s", cloneName)); @@ -802,10 +799,9 @@ public class VirtualMachineMO extends BaseMO { clonedVm.tagAsWorkerVM(); makeSureVMHasOnlyRequiredDisk(clonedVm, requiredDisk, dsMo, dcMo); return clonedVm; - } else { - logger.error("VMware cloneVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask)); - return null; } + + return null; } private void makeSureVMHasOnlyRequiredDisk(VirtualMachineMO clonedVm, VirtualDisk requiredDisk, DatastoreMO dsMo, DatacenterMO dcMo) throws Exception { @@ -852,16 +848,42 @@ public class VirtualMachineMO extends BaseMO { setDiskProvisioningType(relocSpec, morDs, diskProvisioningType); - ManagedObjectReference morTask = _context.getService().cloneVMTask(_mor, morFolder, cloneName, cloneSpec); + return cloneVM(cloneName, morFolder, cloneSpec); + } + private boolean cloneVMTask(String cloneName, ManagedObjectReference morFolder, VirtualMachineCloneSpec cloneSpec) throws Exception { + ManagedObjectReference morTask = _context.getService().cloneVMTask(_mor, morFolder, cloneName, cloneSpec); boolean result = _context.getVimClient().waitForTask(morTask); if (result) { _context.waitForTaskProgressDone(morTask); return true; - } else { - logger.error("VMware cloneVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask)); } + logger.error("VMware cloneVM_Task failed due to {}", TaskMO.getTaskFailureInfo(_context, morTask)); + return false; + } + + private boolean cloneVM(final String cloneName, final ManagedObjectReference morFolder, final VirtualMachineCloneSpec cloneSpec) throws Exception { + final int retry = 20; + int retryAttempt = 0; + while (++retryAttempt <= retry) { + try { + logger.debug("Cloning VM {}, attempt #{}", cloneName, retryAttempt); + return cloneVMTask(cloneName, morFolder, cloneSpec); + } catch (Exception e) { + logger.info("Got exception while cloning VM {}", cloneName, e); + if (e.getMessage() != null && e.getMessage().contains("Unable to access file")) { + logger.debug("Failed to clone VM {}. Retrying", cloneName); + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + logger.debug("Waiting to clone VM {} been interrupted: ", cloneName); + } + } else { + throw e; + } + } + } return false; } @@ -925,17 +947,7 @@ public class VirtualMachineMO extends BaseMO { cloneSpec.setLocation(rSpec); cloneSpec.setSnapshot(morBaseSnapshot); - ManagedObjectReference morTask = _context.getService().cloneVMTask(_mor, morFolder, cloneName, cloneSpec); - - boolean result = _context.getVimClient().waitForTask(morTask); - if (result) { - _context.waitForTaskProgressDone(morTask); - return true; - } else { - logger.error("VMware cloneVM_Task failed due to " + TaskMO.getTaskFailureInfo(_context, morTask)); - } - - return false; + return cloneVM(cloneName, morFolder, cloneSpec); } public VirtualMachineRuntimeInfo getRuntimeInfo() throws Exception {