You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by vi...@apache.org on 2024/06/28 22:05:40 UTC

(cloudstack) branch main updated (3a0c3125916 -> 90fe1d5fdcc)

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

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


    from 3a0c3125916 fix build error with FiberChannelAdapter after forward-merge
     add 4eb43651e2d Ability to specify NFS mount options while adding a primary storage and modify them on a pre-existing primary storage  (#8947)
     add 7f0d9a03045 [Veeam] Check for failures in the restore process (#7224)
     add 6d7c042bc1d Accept a role ID on linking an account to LDAP (#8236)
     add 2bf36ef9353 DRS: Ensure the destination host is part of the same cluster (#9245)
     add dc74d5ba888 Let network guru decide if ipv6 cidr size can't be equal to 64 (#9289)
     add 4f85c3fd231 Honour listall param for list templates api (#9300)
     add ae4b6d3b6c4 CKS/calico: set arp_ignore and arp_announce to 0 in k8s controller/nodes (#9186)
     add 121839277b9 UI: ignore error host is already in the database when create a zone (#9231)
     add 6c06e85c803 Temporarily backup StorPool volume before expunge (#8843)
     add f7f7013ed02 Added ostypeid in the snapshot response (#9150)
     add 52247c57bb9 agent: reconnect after waiting 5 seconds (#9258)
     add 7a8066d3cad server: fix resource count updated unmanage vm (#8169)
     add b22315db85e server: event for HA vm start (#9202)
     add 22cd00ffb17 veeam: fix issues with PreSetup and DVS and Solidfire (#9256)
     add 53faf0f66a5 xenserver: attach regular iso with configdrive (#9216)
     add 23f8856c7d4 Update AssignToLoadBalancerRuleCmd.java (#9306)
     add 46f672563eb Improve migration of external VMware VMs into KVM cluster (#8815)
     add a4e9d7f21a7 Change vm.stats.remove.batch.size to delete.batch.query.size & allow delete of volume_stats in batches (#9283)
     add a5c8bb3bfc3 server: apply rules when VR of Domain VPC is recreated (#8354)
     add d4446ee09dc Revert listAll behaviour for templates (#9309)
     add 646c894ec68 Fix for race when automatically assigning IP to Vms (#9240)
     add c7f1ba5b8ee server: refactor listNetworks api database retrievals (#9184)
     add 65753755379 extract host from websocket to add route (#8922)
     add 94503e687ca UI: improve ux and fix some minor bugs (#9307)
     add 351de5fabd9 engine/orchestration: Update overcommit ratio during live VM migration (#9178)
     add 6b25ed7a027 prevent an NPE on an uninitialised TemplateObject (#8898)
     add 644f3a3f48f Add, Delete Storage Pool commands should be able execute on a host in maintenance (#9301)
     add 983f164c57f Fixed src datastore on copy check for PowerFlex/ScaleIO storage driver (#9310)
     add bcbf152a052 Merge branch '4.18' into 4.19
     new 90fe1d5fdcc Merge branch '4.19'

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 agent/src/main/java/com/cloud/agent/Agent.java     |   6 +
 .../cloud/agent/properties/AgentProperties.java    |   2 +-
 .../com/cloud/agent/api/to/RemoteInstanceTO.java   |  29 +-
 api/src/main/java/com/cloud/host/Host.java         |   1 +
 .../java/com/cloud/hypervisor/HypervisorGuru.java  |  33 +-
 .../main/java/com/cloud/network/NetworkModel.java  |   2 +
 .../network/VirtualNetworkApplianceService.java    |   9 +
 .../java/com/cloud/network/guru/NetworkGuru.java   |   3 +
 api/src/main/java/com/cloud/vm/NicProfile.java     |   8 +
 api/src/main/java/com/cloud/vm/UserVmService.java  |   5 +
 .../java/com/cloud/vm/VirtualMachineProfile.java   |   4 +
 .../org/apache/cloudstack/api/ApiConstants.java    |   4 +
 .../api/command/admin/vm/ImportVmCmd.java          |  25 +-
 .../loadbalancer/AssignToLoadBalancerRuleCmd.java  |   2 +-
 .../cloudstack/api/response/HostResponse.java      |  20 +-
 .../api/response/StoragePoolResponse.java          |  12 +
 .../apache/cloudstack/vm/UnmanagedInstanceTO.java  |  21 +
 .../apache/cloudstack/vm/UnmanagedVMsManager.java  |  31 ++
 .../cloudstack/api/response/HostResponseTest.java  |   8 +-
 .../agent/api/CheckConvertInstanceAnswer.java      |  30 +-
 ...mmand.java => CheckConvertInstanceCommand.java} |  28 +-
 .../cloud/agent/api/ConvertInstanceCommand.java    |  30 +-
 .../cloud/agent/api/ModifyStoragePoolCommand.java  |   4 +
 .../java/com/cloud/resource/ResourceManager.java   |   2 +
 .../java/com/cloud/storage/StorageManager.java     |  13 +-
 .../com/cloud/vm/VirtualMachineProfileImpl.java    |   2 +
 .../java/com/cloud/agent/manager/AgentAttache.java |   7 +-
 .../com/cloud/vm/VirtualMachineManagerImpl.java    |  74 ++--
 .../engine/orchestration/NetworkOrchestrator.java  | 124 ++++--
 .../orchestration/NetworkOrchestratorTest.java     | 119 ++++++
 .../src/main/java/com/cloud/host/dao/HostDao.java  |   2 +
 .../main/java/com/cloud/host/dao/HostDaoImpl.java  |  31 ++
 .../com/cloud/storage/dao/SnapshotDetailsDao.java  |   3 +
 .../cloud/storage/dao/SnapshotDetailsDaoImpl.java  |  33 ++
 .../main/java/com/cloud/storage/dao/VolumeDao.java |   2 +
 .../java/com/cloud/storage/dao/VolumeDaoImpl.java  |   7 +
 .../java/com/cloud/storage/dao/VolumeStatsDao.java |   6 +-
 .../com/cloud/storage/dao/VolumeStatsDaoImpl.java  |  22 +-
 .../storage/datastore/db/PrimaryDataStoreDao.java  |   6 +
 .../datastore/db/PrimaryDataStoreDaoImpl.java      |  34 +-
 .../storage/image/TemplateDataFactoryImpl.java     |   3 +
 .../storage/image/store/TemplateObject.java        |   8 +
 .../datastore/provider/DefaultHostListener.java    |  10 +-
 .../storage/volume/VolumeServiceImpl.java          |   4 +-
 .../cloudstack/backup/veeam/VeeamClient.java       |  43 +-
 .../cloudstack/backup/veeam/VeeamClientTest.java   |  40 +-
 .../kvm/resource/LibvirtComputingResource.java     |  54 +++
 .../kvm/resource/LibvirtStoragePoolDef.java        | 131 ++++--
 .../kvm/resource/LibvirtStoragePoolXMLParser.java  |  21 +-
 .../LibvirtCheckConvertInstanceCommandWrapper.java |  57 +++
 .../LibvirtConvertInstanceCommandWrapper.java      | 161 +++++---
 .../kvm/storage/LibvirtStorageAdaptor.java         |  55 ++-
 .../kvm/resource/LibvirtStoragePoolDefTest.java    |  43 +-
 .../resource/LibvirtStoragePoolXMLParserTest.java  |  10 +-
 ...virtCheckConvertInstanceCommandWrapperTest.java |  67 +++
 .../LibvirtConvertInstanceCommandWrapperTest.java  |  43 +-
 .../kvm/storage/LibvirtStorageAdaptorTest.java     |  91 ++++
 .../java/com/cloud/hypervisor/guru/VMwareGuru.java | 331 +++++++++++++--
 .../com/cloud/hypervisor/guru/VMwareGuruTest.java  | 459 +++++++++++++++++++++
 .../xenserver/resource/CitrixResourceBase.java     |  26 +-
 .../wrapper/xenbase/CitrixStopCommandWrapper.java  |   4 +-
 .../main/resources/conf/k8s-control-node-add.yml   |  11 +
 .../src/main/resources/conf/k8s-control-node.yml   |  11 +
 .../src/main/resources/conf/k8s-node.yml           |  11 +
 .../CloudStackPrimaryDataStoreLifeCycleImpl.java   |  12 +-
 ...loudStackPrimaryDataStoreLifeCycleImplTest.java |  21 +-
 .../driver/ScaleIOPrimaryDataStoreDriver.java      |   2 +-
 .../storage/datastore/api/StorPoolSnapshotDef.java |  98 +++++
 .../driver/StorPoolPrimaryDataStoreDriver.java     |  87 +++-
 .../datastore/driver/StorPoolStatsCollector.java   | 111 ++++-
 .../storage/datastore/util/StorPoolUtil.java       |  41 +-
 .../storage/motion/StorPoolDataMotionStrategy.java |   9 +-
 .../snapshot/StorPoolConfigurationManager.java     |  12 +-
 .../storage/snapshot/StorPoolSnapshotStrategy.java |  26 +-
 .../api/command/LinkAccountToLdapCmd.java          |  17 +-
 .../apache/cloudstack/ldap/LdapManagerImpl.java    |   3 +-
 .../main/java/com/cloud/api/ApiResponseHelper.java |   2 +-
 .../java/com/cloud/api/query/QueryManagerImpl.java |  13 +-
 .../com/cloud/api/query/dao/HostJoinDaoImpl.java   |   2 +-
 .../cloud/api/query/dao/SnapshotJoinDaoImpl.java   |  15 +
 .../configuration/ConfigurationManagerImpl.java    |  99 +++--
 .../AgentBasedConsoleProxyManager.java             |   9 +-
 .../cloud/consoleproxy/ConsoleProxyManager.java    |  17 +-
 .../consoleproxy/ConsoleProxyManagerImpl.java      |  11 +
 .../com/cloud/ha/HighAvailabilityManagerImpl.java  |  50 ++-
 .../com/cloud/hypervisor/HypervisorGuruBase.java   |  15 +-
 .../java/com/cloud/network/NetworkModelImpl.java   |   2 +
 .../java/com/cloud/network/NetworkServiceImpl.java | 212 +++++-----
 .../com/cloud/network/guru/GuestNetworkGuru.java   |   1 +
 .../router/VirtualNetworkApplianceManagerImpl.java |  25 +-
 .../VpcVirtualNetworkApplianceManagerImpl.java     |   2 +-
 .../com/cloud/resource/ResourceManagerImpl.java    |   9 +
 .../main/java/com/cloud/server/StatsCollector.java |  12 +-
 .../java/com/cloud/storage/StorageManagerImpl.java |  88 +++-
 .../cloud/storage/StoragePoolAutomationImpl.java   |  18 +-
 .../secondary/SecondaryStorageVmManager.java       |  12 +
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |  10 +-
 .../cloudstack/cluster/ClusterDrsServiceImpl.java  |   2 +-
 .../cloudstack/vm/UnmanagedVMsManagerImpl.java     | 290 +++++++++----
 .../ConfigurationManagerImplTest.java              | 105 ++++-
 .../cloud/ha/HighAvailabilityManagerImplTest.java  |   6 +
 .../com/cloud/network/MockNetworkModelImpl.java    |   4 +
 .../java/com/cloud/network/NetworkModelTest.java   |  12 +-
 .../cloud/resource/MockResourceManagerImpl.java    |   6 +
 .../com/cloud/storage/StorageManagerImplTest.java  | 177 +++++++-
 .../java/com/cloud/vpc/MockNetworkModelImpl.java   |   5 +
 .../vpc/MockVpcVirtualNetworkApplianceManager.java |  26 +-
 .../cluster/ClusterDrsServiceImplTest.java         |  60 ++-
 .../cloudstack/vm/UnmanagedVMsManagerImplTest.java |  43 +-
 .../consoleproxy/ConsoleProxyNoVncClient.java      |  11 +-
 .../SecondaryStorageManagerImpl.java               |  11 +
 .../maint/test_primary_storage_nfsmountopts_kvm.py | 184 +++++++++
 test/integration/component/test_ldap.py            |   7 +-
 test/integration/plugins/ldap/test_ldap.py         |  20 +-
 ui/public/locales/en.json                          |  18 +-
 ui/src/components/view/InfoCard.vue                |   5 +-
 ui/src/components/widgets/CopyLabel.vue            |  10 +-
 ui/src/config/section/infra/primaryStorages.js     |   5 +-
 ui/src/views/AutogenView.vue                       |   6 +-
 ui/src/views/compute/DeployVM.vue                  |   6 +-
 .../views/compute/wizard/TemplateIsoRadioGroup.vue |   3 +
 ui/src/views/infra/AddPrimaryStorage.vue           |  14 +
 ui/src/views/infra/HostInfo.vue                    |   8 +
 ui/src/views/infra/UpdatePrimaryStorage.vue        | 195 +++++++++
 ui/src/views/infra/network/IpRangesTabPublic.vue   |  63 +--
 ui/src/views/infra/zone/ZoneWizard.vue             |   2 +-
 ui/src/views/infra/zone/ZoneWizardAddResources.vue |   9 +
 ui/src/views/infra/zone/ZoneWizardLaunchZone.vue   |   7 +-
 .../views/infra/zone/ZoneWizardZoneDetailsStep.vue |   4 +-
 ui/src/views/tools/ImportUnmanagedInstance.vue     |  31 +-
 .../main/java/com/cloud/utils/net/NetUtils.java    |   5 +
 .../com/cloud/hypervisor/vmware/mo/BaseMO.java     |   7 +-
 .../hypervisor/vmware/mo/VirtualMachineMO.java     | 116 +++++-
 .../hypervisor/vmware/util/VmwareContext.java      |   7 +-
 .../cloud/hypervisor/vmware/util/VmwareHelper.java |  11 +-
 135 files changed, 4253 insertions(+), 809 deletions(-)
 copy plugins/network-elements/bigswitch/src/main/java/com/cloud/agent/api/BcfAnswer.java => core/src/main/java/com/cloud/agent/api/CheckConvertInstanceAnswer.java (60%)
 copy core/src/main/java/com/cloud/agent/api/{ChangeAgentCommand.java => CheckConvertInstanceCommand.java} (68%)
 create mode 100644 plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapper.java
 create mode 100644 plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCheckConvertInstanceCommandWrapperTest.java
 create mode 100644 plugins/hypervisors/kvm/src/test/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptorTest.java
 create mode 100644 plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/api/StorPoolSnapshotDef.java
 create mode 100644 test/integration/component/maint/test_primary_storage_nfsmountopts_kvm.py
 create mode 100644 ui/src/views/infra/UpdatePrimaryStorage.vue


(cloudstack) 01/01: Merge branch '4.19'

Posted by vi...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 90fe1d5fdcc2909130b5de6803b57feeff194f54
Merge: 3a0c3125916 bcbf152a052
Author: Vishesh <vi...@gmail.com>
AuthorDate: Sat Jun 29 03:35:24 2024 +0530

    Merge branch '4.19'

 agent/src/main/java/com/cloud/agent/Agent.java     |   6 +
 .../cloud/agent/properties/AgentProperties.java    |   2 +-
 .../com/cloud/agent/api/to/RemoteInstanceTO.java   |  29 +-
 api/src/main/java/com/cloud/host/Host.java         |   1 +
 .../java/com/cloud/hypervisor/HypervisorGuru.java  |  33 +-
 .../main/java/com/cloud/network/NetworkModel.java  |   2 +
 .../network/VirtualNetworkApplianceService.java    |   9 +
 .../java/com/cloud/network/guru/NetworkGuru.java   |   3 +
 api/src/main/java/com/cloud/vm/NicProfile.java     |   8 +
 api/src/main/java/com/cloud/vm/UserVmService.java  |   5 +
 .../java/com/cloud/vm/VirtualMachineProfile.java   |   4 +
 .../org/apache/cloudstack/api/ApiConstants.java    |   4 +
 .../api/command/admin/vm/ImportVmCmd.java          |  25 +-
 .../loadbalancer/AssignToLoadBalancerRuleCmd.java  |   2 +-
 .../cloudstack/api/response/HostResponse.java      |  20 +-
 .../api/response/StoragePoolResponse.java          |  12 +
 .../apache/cloudstack/vm/UnmanagedInstanceTO.java  |  21 +
 .../apache/cloudstack/vm/UnmanagedVMsManager.java  |  31 ++
 .../cloudstack/api/response/HostResponseTest.java  |   8 +-
 .../agent/api/CheckConvertInstanceAnswer.java      |  52 +--
 .../agent/api/CheckConvertInstanceCommand.java     |  46 +--
 .../cloud/agent/api/ConvertInstanceCommand.java    |  30 +-
 .../cloud/agent/api/ModifyStoragePoolCommand.java  |   4 +
 .../java/com/cloud/resource/ResourceManager.java   |   2 +
 .../java/com/cloud/storage/StorageManager.java     |  13 +-
 .../com/cloud/vm/VirtualMachineProfileImpl.java    |   2 +
 .../java/com/cloud/agent/manager/AgentAttache.java |   7 +-
 .../com/cloud/vm/VirtualMachineManagerImpl.java    |  74 ++--
 .../engine/orchestration/NetworkOrchestrator.java  | 124 ++++--
 .../orchestration/NetworkOrchestratorTest.java     | 119 ++++++
 .../src/main/java/com/cloud/host/dao/HostDao.java  |   2 +
 .../main/java/com/cloud/host/dao/HostDaoImpl.java  |  31 ++
 .../com/cloud/storage/dao/SnapshotDetailsDao.java  |   3 +
 .../cloud/storage/dao/SnapshotDetailsDaoImpl.java  |  33 ++
 .../main/java/com/cloud/storage/dao/VolumeDao.java |   2 +
 .../java/com/cloud/storage/dao/VolumeDaoImpl.java  |   7 +
 .../java/com/cloud/storage/dao/VolumeStatsDao.java |   6 +-
 .../com/cloud/storage/dao/VolumeStatsDaoImpl.java  |  22 +-
 .../storage/datastore/db/PrimaryDataStoreDao.java  |   6 +
 .../datastore/db/PrimaryDataStoreDaoImpl.java      |  34 +-
 .../storage/image/TemplateDataFactoryImpl.java     |   3 +
 .../storage/image/store/TemplateObject.java        |   8 +
 .../datastore/provider/DefaultHostListener.java    |  10 +-
 .../storage/volume/VolumeServiceImpl.java          |   4 +-
 .../cloudstack/backup/veeam/VeeamClient.java       |  43 +-
 .../cloudstack/backup/veeam/VeeamClientTest.java   |  40 +-
 .../kvm/resource/LibvirtComputingResource.java     |  54 +++
 .../kvm/resource/LibvirtStoragePoolDef.java        | 131 ++++--
 .../kvm/resource/LibvirtStoragePoolXMLParser.java  |  21 +-
 .../LibvirtCheckConvertInstanceCommandWrapper.java |  57 +++
 .../LibvirtConvertInstanceCommandWrapper.java      | 161 +++++---
 .../kvm/storage/LibvirtStorageAdaptor.java         |  55 ++-
 .../kvm/resource/LibvirtStoragePoolDefTest.java    |  43 +-
 .../resource/LibvirtStoragePoolXMLParserTest.java  |  10 +-
 ...virtCheckConvertInstanceCommandWrapperTest.java |  67 +++
 .../LibvirtConvertInstanceCommandWrapperTest.java  |  43 +-
 .../kvm/storage/LibvirtStorageAdaptorTest.java     |  91 ++++
 .../java/com/cloud/hypervisor/guru/VMwareGuru.java | 331 +++++++++++++--
 .../com/cloud/hypervisor/guru/VMwareGuruTest.java  | 459 +++++++++++++++++++++
 .../xenserver/resource/CitrixResourceBase.java     |  26 +-
 .../wrapper/xenbase/CitrixStopCommandWrapper.java  |   4 +-
 .../main/resources/conf/k8s-control-node-add.yml   |  11 +
 .../src/main/resources/conf/k8s-control-node.yml   |  11 +
 .../src/main/resources/conf/k8s-node.yml           |  11 +
 .../CloudStackPrimaryDataStoreLifeCycleImpl.java   |  12 +-
 ...loudStackPrimaryDataStoreLifeCycleImplTest.java |  21 +-
 .../driver/ScaleIOPrimaryDataStoreDriver.java      |   2 +-
 .../storage/datastore/api/StorPoolSnapshotDef.java |  98 +++++
 .../driver/StorPoolPrimaryDataStoreDriver.java     |  87 +++-
 .../datastore/driver/StorPoolStatsCollector.java   | 111 ++++-
 .../storage/datastore/util/StorPoolUtil.java       |  41 +-
 .../storage/motion/StorPoolDataMotionStrategy.java |   9 +-
 .../snapshot/StorPoolConfigurationManager.java     |  12 +-
 .../storage/snapshot/StorPoolSnapshotStrategy.java |  26 +-
 .../api/command/LinkAccountToLdapCmd.java          |  17 +-
 .../apache/cloudstack/ldap/LdapManagerImpl.java    |   3 +-
 .../main/java/com/cloud/api/ApiResponseHelper.java |   2 +-
 .../java/com/cloud/api/query/QueryManagerImpl.java |  13 +-
 .../com/cloud/api/query/dao/HostJoinDaoImpl.java   |   2 +-
 .../cloud/api/query/dao/SnapshotJoinDaoImpl.java   |  15 +
 .../configuration/ConfigurationManagerImpl.java    |  99 +++--
 .../AgentBasedConsoleProxyManager.java             |   9 +-
 .../cloud/consoleproxy/ConsoleProxyManager.java    |  17 +-
 .../consoleproxy/ConsoleProxyManagerImpl.java      |  11 +
 .../com/cloud/ha/HighAvailabilityManagerImpl.java  |  50 ++-
 .../com/cloud/hypervisor/HypervisorGuruBase.java   |  15 +-
 .../java/com/cloud/network/NetworkModelImpl.java   |   2 +
 .../java/com/cloud/network/NetworkServiceImpl.java | 212 +++++-----
 .../com/cloud/network/guru/GuestNetworkGuru.java   |   1 +
 .../router/VirtualNetworkApplianceManagerImpl.java |  25 +-
 .../VpcVirtualNetworkApplianceManagerImpl.java     |   2 +-
 .../com/cloud/resource/ResourceManagerImpl.java    |   9 +
 .../main/java/com/cloud/server/StatsCollector.java |  12 +-
 .../java/com/cloud/storage/StorageManagerImpl.java |  88 +++-
 .../cloud/storage/StoragePoolAutomationImpl.java   |  18 +-
 .../secondary/SecondaryStorageVmManager.java       |  12 +
 .../main/java/com/cloud/vm/UserVmManagerImpl.java  |  10 +-
 .../cloudstack/cluster/ClusterDrsServiceImpl.java  |   2 +-
 .../cloudstack/vm/UnmanagedVMsManagerImpl.java     | 290 +++++++++----
 .../ConfigurationManagerImplTest.java              | 105 ++++-
 .../cloud/ha/HighAvailabilityManagerImplTest.java  |   6 +
 .../com/cloud/network/MockNetworkModelImpl.java    |   4 +
 .../java/com/cloud/network/NetworkModelTest.java   |  12 +-
 .../cloud/resource/MockResourceManagerImpl.java    |   6 +
 .../com/cloud/storage/StorageManagerImplTest.java  | 177 +++++++-
 .../java/com/cloud/vpc/MockNetworkModelImpl.java   |   5 +
 .../vpc/MockVpcVirtualNetworkApplianceManager.java |  26 +-
 .../cluster/ClusterDrsServiceImplTest.java         |  60 ++-
 .../cloudstack/vm/UnmanagedVMsManagerImplTest.java |  43 +-
 .../consoleproxy/ConsoleProxyNoVncClient.java      |  11 +-
 .../SecondaryStorageManagerImpl.java               |  11 +
 .../maint/test_primary_storage_nfsmountopts_kvm.py | 184 +++++++++
 test/integration/component/test_ldap.py            |   7 +-
 test/integration/plugins/ldap/test_ldap.py         |  20 +-
 ui/public/locales/en.json                          |  18 +-
 ui/src/components/view/InfoCard.vue                |   5 +-
 ui/src/components/widgets/CopyLabel.vue            |  10 +-
 ui/src/config/section/infra/primaryStorages.js     |   5 +-
 ui/src/views/AutogenView.vue                       |   6 +-
 ui/src/views/compute/DeployVM.vue                  |   6 +-
 .../views/compute/wizard/TemplateIsoRadioGroup.vue |   3 +
 ui/src/views/infra/AddPrimaryStorage.vue           |  14 +
 ui/src/views/infra/HostInfo.vue                    |   8 +
 ui/src/views/infra/UpdatePrimaryStorage.vue        | 195 +++++++++
 ui/src/views/infra/network/IpRangesTabPublic.vue   |  63 +--
 ui/src/views/infra/zone/ZoneWizard.vue             |   2 +-
 ui/src/views/infra/zone/ZoneWizardAddResources.vue |   9 +
 ui/src/views/infra/zone/ZoneWizardLaunchZone.vue   |   7 +-
 .../views/infra/zone/ZoneWizardZoneDetailsStep.vue |   4 +-
 ui/src/views/tools/ImportUnmanagedInstance.vue     |  31 +-
 .../main/java/com/cloud/utils/net/NetUtils.java    |   5 +
 .../com/cloud/hypervisor/vmware/mo/BaseMO.java     |   7 +-
 .../hypervisor/vmware/mo/VirtualMachineMO.java     | 116 +++++-
 .../hypervisor/vmware/util/VmwareContext.java      |   7 +-
 .../cloud/hypervisor/vmware/util/VmwareHelper.java |  11 +-
 135 files changed, 4268 insertions(+), 834 deletions(-)

diff --cc agent/src/main/java/com/cloud/agent/Agent.java
index 56732dad993,e73a26beea1..d2d4f165979
--- a/agent/src/main/java/com/cloud/agent/Agent.java
+++ b/agent/src/main/java/com/cloud/agent/Agent.java
@@@ -1122,11 -1136,17 +1122,17 @@@ public class Agent implements HandlerFa
                          _executor.submit(new AgentRequestHandler(getType(), getLink(), request));
                      }
                  } catch (final ClassNotFoundException e) {
 -                    s_logger.error("Unable to find this request ");
 +                    logger.error("Unable to find this request ");
                  } catch (final Exception e) {
 -                    s_logger.error("Error parsing task", e);
 +                    logger.error("Error parsing task", e);
                  }
              } else if (task.getType() == Task.Type.DISCONNECT) {
+                 try {
+                     // an issue has been found if reconnect immediately after disconnecting. please refer to https://github.com/apache/cloudstack/issues/8517
+                     // wait 5 seconds before reconnecting
+                     Thread.sleep(5000);
+                 } catch (InterruptedException e) {
+                 }
                  reconnect(task.getLink());
                  return;
              } else if (task.getType() == Task.Type.OTHER) {
diff --cc api/src/main/java/com/cloud/vm/UserVmService.java
index a30c4491f9d,2fdd2592483..5a9301eef7f
--- a/api/src/main/java/com/cloud/vm/UserVmService.java
+++ b/api/src/main/java/com/cloud/vm/UserVmService.java
@@@ -108,10 -112,14 +109,14 @@@ public interface UserVmService 
      UserVm startVirtualMachine(StartVMCmd cmd) throws StorageUnavailableException, ExecutionException, ConcurrentOperationException, ResourceUnavailableException,
          InsufficientCapacityException, ResourceAllocationException;
  
 +    UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException;
 +
      void startVirtualMachine(UserVm vm) throws OperationTimedoutException, ResourceUnavailableException, InsufficientCapacityException;
  
+     void startVirtualMachineForHA(VirtualMachine vm, Map<VirtualMachineProfile.Param, Object> params,
+           DeploymentPlanner planner) throws InsufficientCapacityException, ResourceUnavailableException,
+             ConcurrentOperationException, OperationTimedoutException;
+ 
 -    UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException;
 -
      UserVm updateVirtualMachine(UpdateVMCmd cmd) throws ResourceUnavailableException, InsufficientCapacityException;
  
      /**
diff --cc api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index bad462e5a00,050464a13a6..a2bcd103b76
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@@ -1132,9 -1101,8 +1134,11 @@@ public class ApiConstants 
  
      public static final String PARAMETER_DESCRIPTION_IS_TAG_A_RULE = "Whether the informed tag is a JS interpretable rule or not.";
  
 +    public static final String WEBHOOK_ID = "webhookid";
 +    public static final String WEBHOOK_NAME = "webhookname";
 +
+     public static final String NFS_MOUNT_OPTIONS = "nfsmountopts";
+ 
      /**
       * This enum specifies IO Drivers, each option controls specific policies on I/O.
       * Qemu guests support "threads" and "native" options Since 0.8.8 ; "io_uring" is supported Since 6.3.0 (QEMU 5.0).
diff --cc api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java
index 1a34b7ea6cc,945f861cd3e..6f148ff0ee4
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ImportVmCmd.java
@@@ -37,8 -37,10 +37,9 @@@ import org.apache.cloudstack.api.respon
  import org.apache.cloudstack.api.response.VmwareDatacenterResponse;
  import org.apache.cloudstack.api.response.ZoneResponse;
  import org.apache.cloudstack.vm.VmImportService;
+ import org.apache.commons.lang3.BooleanUtils;
  import org.apache.commons.lang3.ObjectUtils;
  import org.apache.commons.lang3.StringUtils;
 -import org.apache.log4j.Logger;
  
  import javax.inject.Inject;
  
diff --cc engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
index 6653bb2bad3,5e97cc9edfe..779c08e1505
--- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
+++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
@@@ -344,10 -332,12 +337,14 @@@ public interface StorageManager extend
  
      boolean isStoragePoolCompliantWithStoragePolicy(List<Pair<Volume, DiskProfile>> volumes, StoragePool pool) throws StorageUnavailableException;
  
 +    boolean isStoragePoolCompliantWithStoragePolicy(long diskOfferingId, StoragePool pool) throws StorageUnavailableException;
 +
      boolean registerHostListener(String providerUuid, HypervisorHostListener listener);
  
+     Pair<Map<String, String>, Boolean> getStoragePoolNFSMountOpts(StoragePool pool, Map<String, String> details);
+ 
+     String getStoragePoolMountFailureReason(String error);
+ 
      boolean connectHostToSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException;
  
      void disconnectHostFromSharedPool(long hostId, long poolId) throws StorageUnavailableException, StorageConflictException;
diff --cc engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
index e2e11965d3e,055c1a2d444..d21e8b0fc7b
--- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java
@@@ -1455,55 -1450,27 +1443,76 @@@ public class VirtualMachineManagerImpl 
          }
      }
  
 +    public void setVmNetworkDetails(VMInstanceVO vm, VirtualMachineTO vmTO) {
 +        Map<Long, String> networkToNetworkNameMap = new HashMap<>();
 +        if (VirtualMachine.Type.User.equals(vm.getType())) {
 +            List<UserVmJoinVO> userVmJoinVOs = userVmJoinDao.searchByIds(vm.getId());
 +            if (userVmJoinVOs != null && !userVmJoinVOs.isEmpty()) {
 +                for (UserVmJoinVO userVmJoinVO : userVmJoinVOs) {
 +                    addToNetworkNameMap(userVmJoinVO.getNetworkId(), vm.getDataCenterId(), networkToNetworkNameMap);
 +                }
 +                vmTO.setNetworkIdToNetworkNameMap(networkToNetworkNameMap);
 +            }
 +        } else if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) {
 +            List<DomainRouterJoinVO> routerJoinVO = domainRouterJoinDao.getRouterByIdAndTrafficType(vm.getId(), Networks.TrafficType.Guest);
 +            for (DomainRouterJoinVO router : routerJoinVO) {
 +                NetworkVO guestNetwork = _networkDao.findById(router.getNetworkId());
 +                if (guestNetwork.getVpcId() == null && guestNetwork.getBroadcastDomainType() == Networks.BroadcastDomainType.NSX) {
 +                    addToNetworkNameMap(router.getNetworkId(), vm.getDataCenterId(), networkToNetworkNameMap);
 +                }
 +            }
 +            vmTO.setNetworkIdToNetworkNameMap(networkToNetworkNameMap);
 +        }
 +    }
 +
 +    private void addToNetworkNameMap(long networkId, long dataCenterId, Map<Long, String> networkToNetworkNameMap) {
 +        NetworkVO networkVO = _networkDao.findById(networkId);
 +        Account acc = accountDao.findById(networkVO.getAccountId());
 +        Domain domain = domainDao.findById(networkVO.getDomainId());
 +        DataCenter zone = _dcDao.findById(dataCenterId);
 +        if (Objects.isNull(zone)) {
 +            throw new CloudRuntimeException(String.format("Failed to find zone with ID: %s", dataCenterId));
 +        }
 +        if (Objects.isNull(acc)) {
 +            throw new CloudRuntimeException(String.format("Failed to find account with ID: %s", networkVO.getAccountId()));
 +        }
 +        if (Objects.isNull(domain)) {
 +            throw new CloudRuntimeException(String.format("Failed to find domain with ID: %s", networkVO.getDomainId()));
 +        }
 +        String networkName = String.format("D%s-A%s-Z%s", domain.getId(), acc.getId(), zone.getId());
 +        if (Objects.isNull(networkVO.getVpcId())) {
 +            networkName += "-S" + networkVO.getId();
 +        } else {
 +            VpcVO vpc = vpcDao.findById(networkVO.getVpcId());
 +            if (Objects.isNull(vpc)) {
 +                throw new CloudRuntimeException(String.format("Failed to find VPC with ID: %s", networkVO.getVpcId()));
 +            }
 +            networkName = String.format("%s-V%s-S%s", networkName, vpc.getId(), networkVO.getId());
 +        }
 +        networkToNetworkNameMap.put(networkVO.getId(), networkName);
 +    }
 +
+     private void updateOverCommitRatioForVmProfile(VirtualMachineProfile vmProfile, long clusterId) {
+         final ClusterDetailsVO clusterDetailCpu = _clusterDetailsDao.findDetail(clusterId, VmDetailConstants.CPU_OVER_COMMIT_RATIO);
+         final ClusterDetailsVO clusterDetailRam = _clusterDetailsDao.findDetail(clusterId, VmDetailConstants.MEMORY_OVER_COMMIT_RATIO);
+         final float parsedClusterCpuDetailCpu = Float.parseFloat(clusterDetailCpu.getValue());
+         final float parsedClusterDetailRam = Float.parseFloat(clusterDetailRam.getValue());
+         UserVmDetailVO vmDetailCpu = userVmDetailsDao.findDetail(vmProfile.getId(), VmDetailConstants.CPU_OVER_COMMIT_RATIO);
+         UserVmDetailVO vmDetailRam = userVmDetailsDao.findDetail(vmProfile.getId(), VmDetailConstants.MEMORY_OVER_COMMIT_RATIO);
+ 
+         if ((vmDetailCpu == null && parsedClusterCpuDetailCpu > 1f) ||
+                 (vmDetailCpu != null && Float.parseFloat(vmDetailCpu.getValue()) != parsedClusterCpuDetailCpu)) {
+             userVmDetailsDao.addDetail(vmProfile.getId(), VmDetailConstants.CPU_OVER_COMMIT_RATIO, clusterDetailCpu.getValue(), true);
+         }
+         if ((vmDetailRam == null && parsedClusterDetailRam > 1f) ||
+                 (vmDetailRam != null && Float.parseFloat(vmDetailRam.getValue()) != parsedClusterDetailRam)) {
+             userVmDetailsDao.addDetail(vmProfile.getId(), VmDetailConstants.MEMORY_OVER_COMMIT_RATIO, clusterDetailRam.getValue(), true);
+         }
+ 
+         vmProfile.setCpuOvercommitRatio(Float.parseFloat(clusterDetailCpu.getValue()));
+         vmProfile.setMemoryOvercommitRatio(Float.parseFloat(clusterDetailRam.getValue()));
+     }
+ 
      /**
       * Setting pod id to null can result in migration of Volumes across pods. This is not desirable for VMs which
       * have a volume in Ready state (happens when a VM is shutdown and started again).
@@@ -4824,8 -4875,8 +4850,8 @@@
                      + " -> Running) from out-of-context transition. VM network environment may need to be reset");
  
              ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM, vm.getDomainId(),
-                     EventTypes.EVENT_VM_START, "Out of band VM power on", vm.getId(), ApiCommandResourceType.VirtualMachine.toString());
 -                    EventTypes.EVENT_VM_START, "Out of band VM power on", vm.getId(), getApiCommandResourceTypeForVm(vm).toString());
 -            s_logger.info("VM " + vm.getInstanceName() + " is sync-ed to at Running state according to power-on report from hypervisor");
++                EventTypes.EVENT_VM_START, "Out of band VM power on", vm.getId(), getApiCommandResourceTypeForVm(vm).toString());
 +            logger.info("VM {} is sync-ed to at Running state according to power-on report from hypervisor.", vm.getInstanceName());
              break;
  
          case Destroyed:
@@@ -4857,10 -4910,13 +4883,10 @@@
          case Running:
          case Stopped:
              ActionEventUtils.onActionEvent(User.UID_SYSTEM, Account.ACCOUNT_ID_SYSTEM,vm.getDomainId(),
-                     EventTypes.EVENT_VM_STOP, "Out of band VM power off", vm.getId(), ApiCommandResourceType.VirtualMachine.toString());
+                     EventTypes.EVENT_VM_STOP, "Out of band VM power off", vm.getId(), getApiCommandResourceTypeForVm(vm).toString());
          case Migrating:
 -            if (s_logger.isInfoEnabled()) {
 -                s_logger.info(
 -                        String.format("VM %s is at %s and we received a %s report while there is no pending jobs on it"
 -                                , vm.getInstanceName(), vm.getState(), vm.getPowerState()));
 -            }
 +            logger.info("VM {} is at {} and we received a {} report while there is no pending jobs on it"
 +                            , vm.getInstanceName(), vm.getState(), vm.getPowerState());
              if((HighAvailabilityManager.ForceHA.value() || vm.isHaEnabled()) && vm.getState() == State.Running
                      && HaVmRestartHostUp.value()
                      && vm.getHypervisorType() != HypervisorType.VMware
diff --cc engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index e589f688cdb,0232e3aeb9c..ea34f62ecd5
--- a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@@ -1033,48 -1020,84 +1041,84 @@@ public class NetworkOrchestrator extend
          }
      }
  
-     @DB
-     @Override
-     public Pair<NicProfile, Integer> allocateNic(final NicProfile requested, final Network network, final Boolean isDefaultNic, int deviceId, final VirtualMachineProfile vm)
-             throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException {
+     private NicVO persistNicAfterRaceCheck(final NicVO nic, final Long networkId, final NicProfile profile, int deviceId) {
+         return Transaction.execute(new TransactionCallback<NicVO>() {
+             @Override
+             public NicVO doInTransaction(TransactionStatus status) {
+                 NicVO vo = _nicDao.findByIp4AddressAndNetworkId(profile.getIPv4Address(), networkId);
+                 if (vo == null) {
+                     applyProfileToNic(nic, profile, deviceId);
+                     vo = _nicDao.persist(nic);
+                     return vo;
+                 } else {
+                     return null;
+                 }
+             }
+         });
+     }
  
+     private NicVO checkForRaceAndAllocateNic(final NicProfile requested, final Network network, final Boolean isDefaultNic, int deviceId, final VirtualMachineProfile vm)
+             throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException {
          final NetworkVO ntwkVO = _networksDao.findById(network.getId());
 -        s_logger.debug("Allocating nic for vm " + vm.getVirtualMachine() + " in network " + network + " with requested profile " + requested);
 +        logger.debug("Allocating nic for vm {} in network {} with requested profile {}", vm.getVirtualMachine(), network, requested);
          final NetworkGuru guru = AdapterBase.getAdapterByName(networkGurus, ntwkVO.getGuruName());
  
-         if (requested != null && requested.getMode() == null) {
-             requested.setMode(network.getMode());
-         }
-         final NicProfile profile = guru.allocate(network, requested, vm);
-         if (profile == null) {
-             return null;
-         }
+         NicVO vo = null;
+         boolean retryIpAllocation;
+         do {
+             retryIpAllocation = false;
+             final NicProfile profile = guru.allocate(network, requested, vm);
+             if (profile == null) {
+                 return null;
+             }
  
-         if (isNicAllocatedForNsxPublicNetworkOnVR(network, profile, vm)) {
-             String guruName = "NsxPublicNetworkGuru";
-             NetworkGuru nsxGuru = AdapterBase.getAdapterByName(networkGurus, guruName);
-             nsxGuru.allocate(network, profile, vm);
-         }
+             if (isDefaultNic != null) {
+                 profile.setDefaultNic(isDefaultNic);
+             }
  
-         if (isDefaultNic != null) {
-             profile.setDefaultNic(isDefaultNic);
-         }
+             if (requested != null && requested.getMode() == null) {
+                 profile.setMode(requested.getMode());
+             } else {
+                 profile.setMode(network.getMode());
+             }
  
-         if (requested != null && requested.getMode() == null) {
-             profile.setMode(requested.getMode());
-         } else {
-             profile.setMode(network.getMode());
-         }
+             vo = new NicVO(guru.getName(), vm.getId(), network.getId(), vm.getType());
  
-         NicVO vo = new NicVO(guru.getName(), vm.getId(), network.getId(), vm.getType());
+             DataCenterVO dcVo = _dcDao.findById(network.getDataCenterId());
+             if (dcVo.getNetworkType() == NetworkType.Basic) {
+                 configureNicProfileBasedOnRequestedIp(requested, profile, network);
+             }
  
-         DataCenterVO dcVo = _dcDao.findById(network.getDataCenterId());
-         if (dcVo.getNetworkType() == NetworkType.Basic) {
-             configureNicProfileBasedOnRequestedIp(requested, profile, network);
+             if (profile.getIpv4AllocationRaceCheck()) {
+                 vo = persistNicAfterRaceCheck(vo, network.getId(), profile, deviceId);
+             } else {
+                 applyProfileToNic(vo, profile, deviceId);
+                 vo = _nicDao.persist(vo);
+             }
+ 
+             if (vo == null) {
+                 if (requested.getRequestedIPv4() != null) {
+                     throw new InsufficientVirtualNetworkCapacityException("Unable to acquire requested Guest IP address " + requested.getRequestedIPv4() + " for network " + network, DataCenter.class, dcVo.getId());
+                 } else {
+                     requested.setIPv4Address(null);
+                 }
+                 retryIpAllocation = true;
+             }
+         } while (retryIpAllocation);
+ 
+         return vo;
+     }
+ 
+     @DB
+     @Override
+     public Pair<NicProfile, Integer> allocateNic(final NicProfile requested, final Network network, final Boolean isDefaultNic, int deviceId, final VirtualMachineProfile vm)
+             throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException {
+ 
+         if (requested != null && requested.getMode() == null) {
+             requested.setMode(network.getMode());
          }
  
-         deviceId = applyProfileToNic(vo, profile, deviceId);
-         vo = _nicDao.persist(vo);
+         NicVO vo = checkForRaceAndAllocateNic(requested, network, isDefaultNic, deviceId, vm);
  
          final Integer networkRate = _networkModel.getNetworkRate(network.getId(), vm.getId());
          final NicProfile vmNic = new NicProfile(vo, network, vo.getBroadcastUri(), vo.getIsolationUri(), networkRate, _networkModel.isSecurityGroupSupportedInNetwork(network),
diff --cc engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java
index a4700f6cdc0,2d016d03f2e..58746a9a6cf
--- a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java
+++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java
@@@ -31,14 -32,18 +32,16 @@@ import java.util.List
  import java.util.Map;
  
  import com.cloud.dc.DataCenter;
+ import com.cloud.exception.InsufficientVirtualNetworkCapacityException;
  import com.cloud.network.IpAddressManager;
  import com.cloud.utils.Pair;
 -
 -import org.apache.log4j.Logger;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  import org.junit.runners.JUnit4;
 -import org.mockito.Matchers;
 +import org.mockito.ArgumentMatchers;
+ import org.mockito.MockedStatic;
  import org.mockito.Mockito;
  
  import com.cloud.api.query.dao.DomainRouterJoinDao;
diff --cc engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java
index db1563f3ff1,8e3bbf68dea..e6ffca06f9e
--- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDao.java
@@@ -157,7 -157,5 +157,9 @@@ public interface VolumeDao extends Gene
  
      List<VolumeVO> listByIds(List<Long> ids);
  
 +    List<VolumeVO> listAllocatedVolumesForAccountDiskOfferingIdsAndNotForVms(long accountId, List<Long> diskOfferingIds, List<Long> vmIds);
 +
 +    List<VolumeVO> searchRemovedByVms(List<Long> vmIds, Long batchSize);
++
+     VolumeVO findOneByIScsiName(String iScsiName);
  }
diff --cc engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java
index 551ff7d2d78,3a3c02d83b4..16004ac13b9
--- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java
@@@ -863,51 -842,9 +864,57 @@@ public class VolumeDaoImpl extends Gene
          return listBy(sc, null);
      }
  
 +    @Override
 +    public List<VolumeVO> listAllocatedVolumesForAccountDiskOfferingIdsAndNotForVms(long accountId, List<Long> diskOfferingIds, List<Long> vmIds) {
 +        SearchBuilder<VolumeVO> sb = createSearchBuilder();
 +        sb.and("account", sb.entity().getAccountId(), SearchCriteria.Op.EQ);
 +        sb.and("state", sb.entity().getState(), SearchCriteria.Op.NIN);
 +        sb.and("diskOfferingIds", sb.entity().getDiskOfferingId(), SearchCriteria.Op.IN);
 +        sb.and("displayVolume", sb.entity().isDisplayVolume(), Op.EQ);
 +        if (CollectionUtils.isNotEmpty(vmIds)) {
 +            sb.and().op("instanceId", sb.entity().getInstanceId(), Op.NULL);
 +            sb.or("notVmIds", sb.entity().getInstanceId(), Op.NIN);
 +            sb.cp();
 +        }
 +        sb.done();
 +        SearchCriteria<VolumeVO> sc = sb.create();
 +        sc.setParameters("account", accountId);
 +        sc.setParameters("state", Volume.State.Destroy, Volume.State.Expunged);
 +        sc.setParameters("diskOfferingIds", diskOfferingIds.toArray());
 +        sc.setParameters("displayVolume", 1);
 +        if (CollectionUtils.isNotEmpty(vmIds)) {
 +            sc.setParameters("notVmIds", vmIds.toArray());
 +        }
 +        return listBy(sc);
 +    }
 +
 +    @Override
 +    public VolumeVO persist(VolumeVO entity) {
 +        return Transaction.execute((TransactionCallback<VolumeVO>) status -> {
 +            VolumeVO volume = super.persist(entity);
 +            reservationDao.setResourceId(Resource.ResourceType.volume, volume.getId());
 +            reservationDao.setResourceId(Resource.ResourceType.primary_storage, volume.getId());
 +            return volume;
 +        });
 +    }
 +
 +    @Override
 +    public List<VolumeVO> searchRemovedByVms(List<Long> vmIds, Long batchSize) {
 +        if (CollectionUtils.isEmpty(vmIds)) {
 +            return new ArrayList<>();
 +        }
 +        SearchBuilder<VolumeVO> sb = createSearchBuilder();
 +        sb.and("vmIds", sb.entity().getInstanceId(), SearchCriteria.Op.IN);
 +        sb.and("removed", sb.entity().getRemoved(), SearchCriteria.Op.NNULL);
 +        SearchCriteria<VolumeVO> sc = sb.create();
 +        sc.setParameters("vmIds", vmIds.toArray());
 +        Filter filter = new Filter(VolumeVO.class, "id", true, 0L, batchSize);
 +        return searchIncludingRemoved(sc, filter, null, false);
 +    }
++
+     public VolumeVO findOneByIScsiName(String iScsiName) {
+         SearchCriteria<VolumeVO> sc = AllFieldsSearch.create();
+         sc.setParameters("iScsiName", iScsiName);
+         return findOneIncludingRemovedBy(sc);
+     }
  }
diff --cc engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDaoImpl.java
index 5d0d3c8921c,002310125af..d1149e47408
--- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDaoImpl.java
+++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeStatsDaoImpl.java
@@@ -21,6 -21,7 +21,8 @@@ import java.util.List
  
  import javax.annotation.PostConstruct;
  
 -import org.apache.log4j.Logger;
++import org.apache.logging.log4j.LogManager;
++import org.apache.logging.log4j.Logger;
  import org.springframework.stereotype.Component;
  
  import com.cloud.utils.db.Filter;
@@@ -33,6 -34,8 +35,8 @@@ import com.cloud.storage.VolumeStatsVO
  @Component
  public class VolumeStatsDaoImpl extends GenericDaoBase<VolumeStatsVO, Long> implements VolumeStatsDao {
  
 -    protected Logger logger = Logger.getLogger(getClass());
++    protected Logger logger = LogManager.getLogger(getClass());
+ 
      protected SearchBuilder<VolumeStatsVO> volumeIdSearch;
      protected SearchBuilder<VolumeStatsVO> volumeIdTimestampGreaterThanEqualSearch;
      protected SearchBuilder<VolumeStatsVO> volumeIdTimestampLessThanEqualSearch;
diff --cc engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java
index d42e863cbed,2fce0628554..f0c235e842c
--- a/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java
+++ b/engine/schema/src/main/java/org/apache/cloudstack/storage/datastore/db/PrimaryDataStoreDao.java
@@@ -139,8 -143,10 +143,10 @@@ public interface PrimaryDataStoreDao ex
  
      Integer countAll();
  
 -    List<StoragePoolVO> findPoolsByStorageType(String storageType);
 +    List<StoragePoolVO> findPoolsByStorageType(Storage.StoragePoolType storageType);
  
+     StoragePoolVO findPoolByZoneAndPath(long zoneId, String datastorePath);
+ 
      List<StoragePoolVO> listStoragePoolsWithActiveVolumesByOfferingId(long offeringid);
  
      Pair<List<Long>, Integer> searchForIdsAndCount(Long storagePoolId, String storagePoolName, Long zoneId,
diff --cc engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java
index fdb4fe6753a,899f1d83e0b..37ddc3573c1
--- a/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java
+++ b/engine/storage/image/src/main/java/org/apache/cloudstack/storage/image/store/TemplateObject.java
@@@ -82,6 -81,10 +82,10 @@@ public class TemplateObject implements 
      }
  
      protected void configure(VMTemplateVO template, DataStore dataStore) {
+         if (template == null) {
+             String msg = String.format("Template Object is not properly initialised %s", this.toString());
 -            s_logger.warn(msg);
++            logger.warn(msg);
+         }
          imageVO = template;
          this.dataStore = dataStore;
      }
@@@ -98,6 -101,10 +102,10 @@@
      }
  
      public VMTemplateVO getImage() {
+         if (imageVO == null) {
+             String msg = String.format("Template Object is not properly initialised %s", this.toString());
 -            s_logger.error(msg);
++            logger.error(msg);
+         } // somehow the nullpointer is needed : refacter needed!?!
          return imageVO;
      }
  
diff --cc engine/storage/volume/src/main/java/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java
index b8f90e46538,ea1e524f5d6..c6d9fab5f17
--- a/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java
+++ b/engine/storage/volume/src/main/java/org/apache/cloudstack/storage/datastore/provider/DefaultHostListener.java
@@@ -50,14 -52,15 +52,16 @@@ import org.apache.cloudstack.storage.da
  import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
  import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
  import org.apache.commons.lang3.StringUtils;
 -import org.apache.log4j.Logger;
 +import org.apache.logging.log4j.Logger;
 +import org.apache.logging.log4j.LogManager;
  
  import javax.inject.Inject;
+ 
  import java.util.List;
+ import java.util.Map;
  
  public class DefaultHostListener implements HypervisorHostListener {
 -    private static final Logger s_logger = Logger.getLogger(DefaultHostListener.class);
 +    protected Logger logger = LogManager.getLogger(getClass());
  
      /**
       * Wait time for modify storage pool command to complete. We should wait for 5 minutes for the command to complete.
@@@ -126,9 -129,11 +130,11 @@@
      @Override
      public boolean hostConnect(long hostId, long poolId) throws StorageConflictException {
          StoragePool pool = (StoragePool) this.dataStoreMgr.getDataStore(poolId, DataStoreRole.Primary);
-         ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, pool);
+         Pair<Map<String, String>, Boolean> nfsMountOpts = storageManager.getStoragePoolNFSMountOpts(pool, null);
+ 
+         ModifyStoragePoolCommand cmd = new ModifyStoragePoolCommand(true, pool, nfsMountOpts.first());
          cmd.setWait(modifyStoragePoolCommandWait);
 -        s_logger.debug(String.format("Sending modify storage pool command to agent: %d for storage pool: %d with timeout %d seconds",
 +        logger.debug(String.format("Sending modify storage pool command to agent: %d for storage pool: %d with timeout %d seconds",
                  hostId, poolId, cmd.getWait()));
          final Answer answer = agentMgr.easySend(hostId, cmd);
  
diff --cc plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index 5cffa77c297,342bb87cf41..dec7f70e62f
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@@ -3646,7 -3645,7 +3657,8 @@@ public class LibvirtComputingResource e
          cmd.setGatewayIpAddress(localGateway);
          cmd.setIqn(getIqn());
          cmd.getHostDetails().put(HOST_VOLUME_ENCRYPTION, String.valueOf(hostSupportsVolumeEncryption()));
 +        cmd.setHostTags(getHostTags());
+         cmd.getHostDetails().put(HOST_INSTANCE_CONVERSION, String.valueOf(hostSupportsInstanceConversion()));
          HealthCheckResult healthCheckResult = getHostHealthCheckResult();
          if (healthCheckResult != HealthCheckResult.IGNORE) {
              cmd.setHostHealthCheckResult(healthCheckResult == HealthCheckResult.SUCCESS);
@@@ -5163,9 -5149,51 +5175,51 @@@
          return false;
      }
  
+     public boolean hostSupportsInstanceConversion() {
+         int exitValue = Script.runSimpleBashScriptForExitValue(INSTANCE_CONVERSION_SUPPORTED_CHECK_CMD);
+         if (isUbuntuHost() && exitValue == 0) {
+             exitValue = Script.runSimpleBashScriptForExitValue(UBUNTU_NBDKIT_PKG_CHECK_CMD);
+         }
+         return exitValue == 0;
+     }
+ 
+     public boolean hostSupportsWindowsGuestConversion() {
+         if (isUbuntuHost()) {
+             int exitValue = Script.runSimpleBashScriptForExitValue(UBUNTU_WINDOWS_GUEST_CONVERSION_SUPPORTED_CHECK_CMD);
+             return exitValue == 0;
+         }
+         int exitValue = Script.runSimpleBashScriptForExitValue(WINDOWS_GUEST_CONVERSION_SUPPORTED_CHECK_CMD);
+         return exitValue == 0;
+     }
+ 
+     public boolean hostSupportsOvfExport() {
+         int exitValue = Script.runSimpleBashScriptForExitValue(OVF_EXPORT_SUPPORTED_CHECK_CMD);
+         return exitValue == 0;
+     }
+ 
+     public boolean ovfExportToolSupportsParallelThreads() {
+         String ovfExportToolVersion = Script.runSimpleBashScript(OVF_EXPORT_TOOl_GET_VERSION_CMD);
+         if (StringUtils.isBlank(ovfExportToolVersion)) {
+             return false;
+         }
+         String[] ovfExportToolVersions = ovfExportToolVersion.trim().split("\\.");
+         if (ovfExportToolVersions.length > 1) {
+             try {
+                 int majorVersion = Integer.parseInt(ovfExportToolVersions[0]);
+                 int minorVersion = Integer.parseInt(ovfExportToolVersions[1]);
+                 //ovftool version >= 4.4 supports parallel threads
+                 if (majorVersion > 4 || (majorVersion == 4 && minorVersion >= 4)) {
+                     return true;
+                 }
+             } catch (NumberFormatException ignored) {
+             }
+         }
+         return false;
+     }
+ 
 -    private void setCpuTopology(CpuModeDef cmd, int vCpusInDef, Map<String, String> details) {
 +    protected void setCpuTopology(CpuModeDef cmd, int vCpusInDef, Map<String, String> details) {
          if (!enableManuallySettingCpuTopologyOnKvmVm) {
 -            s_logger.debug(String.format("Skipping manually setting CPU topology on VM's XML due to it is disabled in agent.properties {\"property\": \"%s\", \"value\": %s}.",
 +            LOGGER.debug(String.format("Skipping manually setting CPU topology on VM's XML due to it is disabled in agent.properties {\"property\": \"%s\", \"value\": %s}.",
                AgentProperties.ENABLE_MANUALLY_SETTING_CPU_TOPOLOGY_ON_KVM_VM.getName(), enableManuallySettingCpuTopologyOnKvmVm));
              return;
          }
diff --cc plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtStoragePoolDef.java
index ff44c8df2fa,4f8b2843ec2..09ee45d5908
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtStoragePoolDef.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtStoragePoolDef.java
@@@ -16,9 -16,15 +16,15 @@@
  // under the License.
  package com.cloud.hypervisor.kvm.resource;
  
+ import java.util.HashSet;
+ import java.util.List;
+ import java.util.Set;
+ 
+ import org.apache.commons.collections.CollectionUtils;
+ 
  public class LibvirtStoragePoolDef {
      public enum PoolType {
 -        ISCSI("iscsi"), NETFS("netfs"), LOGICAL("logical"), DIR("dir"), RBD("rbd"), GLUSTERFS("glusterfs"), POWERFLEX("powerflex");
 +        ISCSI("iscsi"), NETFS("netfs"), loggerICAL("logical"), DIR("dir"), RBD("rbd"), GLUSTERFS("glusterfs"), POWERFLEX("powerflex");
          String _poolType;
  
          PoolType(String poolType) {
diff --cc plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtStoragePoolXMLParser.java
index 30616e04798,a39ff07708e..430e4ef851f
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtStoragePoolXMLParser.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/LibvirtStoragePoolXMLParser.java
@@@ -36,8 -35,21 +36,21 @@@ import org.xml.sax.InputSource
  import org.xml.sax.SAXException;
  
  public class LibvirtStoragePoolXMLParser {
 -    private static final Logger s_logger = Logger.getLogger(LibvirtStoragePoolXMLParser.class);
 +    protected Logger logger = LogManager.getLogger(getClass());
  
+     private List<String> getNFSMountOptsFromRootElement(Element rootElement) {
+         List<String> nfsMountOpts = new ArrayList<>();
+         Element mountOpts = (Element) rootElement.getElementsByTagName("fs:mount_opts").item(0);
+         if (mountOpts != null) {
+             NodeList options = mountOpts.getElementsByTagName("fs:option");
+             for (int i = 0; i < options.getLength(); i++) {
+                 Element option = (Element) options.item(i);
+                 nfsMountOpts.add(option.getAttribute("name"));
+             }
+         }
+         return nfsMountOpts;
+     }
+ 
      public LibvirtStoragePoolDef parseStoragePoolXML(String poolXML) {
          DocumentBuilder builder;
          try {
@@@ -98,15 -110,19 +111,19 @@@
                  Element target = (Element)rootElement.getElementsByTagName("target").item(0);
                  String targetPath = getTagValue("path", target);
  
-                 return new LibvirtStoragePoolDef(LibvirtStoragePoolDef.PoolType.valueOf(type.toUpperCase()), poolName, uuid, host, path, targetPath);
+                 if (type.equalsIgnoreCase("netfs")) {
+                     List<String> nfsMountOpts = getNFSMountOptsFromRootElement(rootElement);
+                     return new LibvirtStoragePoolDef(LibvirtStoragePoolDef.PoolType.valueOf(type.toUpperCase()), poolName, uuid, host, path, targetPath, nfsMountOpts);
+                 } else {
+                     return new LibvirtStoragePoolDef(LibvirtStoragePoolDef.PoolType.valueOf(type.toUpperCase()), poolName, uuid, host, path, targetPath);
+                 }
              }
          } catch (ParserConfigurationException e) {
 -            s_logger.debug(e.toString());
 +            logger.debug(e.toString());
          } catch (SAXException e) {
 -            s_logger.debug(e.toString());
 +            logger.debug(e.toString());
          } catch (IOException e) {
 -            s_logger.debug(e.toString());
 +            logger.debug(e.toString());
          }
          return null;
      }
diff --cc plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java
index bd6634c83a4,2e5d1a78816..4c80a99cb84
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtConvertInstanceCommandWrapper.java
@@@ -74,10 -75,10 +72,10 @@@ public class LibvirtConvertInstanceComm
          DataStoreTO conversionTemporaryLocation = cmd.getConversionTemporaryLocation();
          long timeout = (long) cmd.getWait() * 1000;
  
-         if (!isInstanceConversionSupportedOnHost()) {
+         if (cmd.getCheckConversionSupport() && !serverResource.hostSupportsInstanceConversion()) {
              String msg = String.format("Cannot convert the instance %s from VMware as the virt-v2v binary is not found. " +
-                     "Please install virt-v2v on the host before attempting the instance conversion", sourceInstanceName);
+                     "Please install virt-v2v%s on the host before attempting the instance conversion.", sourceInstanceName, serverResource.isUbuntuHost()? ", nbdkit" : "");
 -            s_logger.info(msg);
 +            logger.info(msg);
              return new ConvertInstanceAnswer(cmd, false, msg);
          }
  
@@@ -91,22 -92,49 +89,52 @@@
  
          final KVMStoragePoolManager storagePoolMgr = serverResource.getStoragePoolMgr();
          KVMStoragePool temporaryStoragePool = getTemporaryStoragePool(conversionTemporaryLocation, storagePoolMgr);
 +
 +        logger.info(String.format("Attempting to convert the instance %s from %s to KVM",
 +                sourceInstanceName, sourceHypervisorType));
-         final String convertInstanceUrl = getConvertInstanceUrl(sourceInstance);
-         final String temporaryConvertUuid = UUID.randomUUID().toString();
-         final String temporaryPasswordFilePath = createTemporaryPasswordFileAndRetrievePath(sourceInstance);
          final String temporaryConvertPath = temporaryStoragePool.getLocalPath();
+ 
+         String ovfTemplateDirOnConversionLocation;
+         String sourceOVFDirPath;
+         boolean ovfExported = false;
+         if (cmd.getExportOvfToConversionLocation()) {
+             String exportInstanceOVAUrl = getExportInstanceOVAUrl(sourceInstance);
+             if (StringUtils.isBlank(exportInstanceOVAUrl)) {
+                 String err = String.format("Couldn't export OVA for the VM %s, due to empty url", sourceInstanceName);
 -                s_logger.error(err);
++                logger.error(err);
+                 return new ConvertInstanceAnswer(cmd, false, err);
+             }
+ 
+             int noOfThreads = cmd.getThreadsCountToExportOvf();
+             if (noOfThreads > 1 && !serverResource.ovfExportToolSupportsParallelThreads()) {
+                 noOfThreads = 0;
+             }
+             ovfTemplateDirOnConversionLocation = UUID.randomUUID().toString();
+             temporaryStoragePool.createFolder(ovfTemplateDirOnConversionLocation);
+             sourceOVFDirPath = String.format("%s/%s/", temporaryConvertPath, ovfTemplateDirOnConversionLocation);
+             ovfExported = exportOVAFromVMOnVcenter(exportInstanceOVAUrl, sourceOVFDirPath, noOfThreads, timeout);
+             if (!ovfExported) {
+                 String err = String.format("Export OVA for the VM %s failed", sourceInstanceName);
 -                s_logger.error(err);
++                logger.error(err);
+                 return new ConvertInstanceAnswer(cmd, false, err);
+             }
+             sourceOVFDirPath = String.format("%s%s/", sourceOVFDirPath, sourceInstanceName);
+         } else {
+             ovfTemplateDirOnConversionLocation = cmd.getTemplateDirOnConversionLocation();
+             sourceOVFDirPath = String.format("%s/%s/", temporaryConvertPath, ovfTemplateDirOnConversionLocation);
+         }
+ 
 -        s_logger.info(String.format("Attempting to convert the OVF %s of the instance %s from %s to KVM", ovfTemplateDirOnConversionLocation, sourceInstanceName, sourceHypervisorType));
++        logger.info(String.format("Attempting to convert the OVF %s of the instance %s from %s to KVM", ovfTemplateDirOnConversionLocation, sourceInstanceName, sourceHypervisorType));
+         final String temporaryConvertUuid = UUID.randomUUID().toString();
          boolean verboseModeEnabled = serverResource.isConvertInstanceVerboseModeEnabled();
  
          try {
-             boolean result = performInstanceConversion(convertInstanceUrl, sourceInstanceName, temporaryPasswordFilePath,
-                     temporaryConvertPath, temporaryConvertUuid, timeout, verboseModeEnabled);
+             boolean result = performInstanceConversion(sourceOVFDirPath, temporaryConvertPath, temporaryConvertUuid,
+                     timeout, verboseModeEnabled);
              if (!result) {
-                 String err = String.format("The virt-v2v conversion of the instance %s failed. " +
-                                 "Please check the agent logs for the virt-v2v output", sourceInstanceName);
+                 String err = String.format("The virt-v2v conversion for the OVF %s failed. " +
+                                 "Please check the agent logs for the virt-v2v output", ovfTemplateDirOnConversionLocation);
 -                s_logger.error(err);
 +                logger.error(err);
                  return new ConvertInstanceAnswer(cmd, false, err);
              }
              String convertedBasePath = String.format("%s/%s", temporaryConvertPath, temporaryConvertUuid);
@@@ -127,13 -155,16 +155,16 @@@
          } catch (Exception e) {
              String error = String.format("Error converting instance %s from %s, due to: %s",
                      sourceInstanceName, sourceHypervisorType, e.getMessage());
 -            s_logger.error(error, e);
 +            logger.error(error, e);
              return new ConvertInstanceAnswer(cmd, false, error);
          } finally {
-             logger.debug("Cleaning up instance conversion temporary password file");
-             Script.runSimpleBashScript(String.format("rm -rf %s", temporaryPasswordFilePath));
+             if (ovfExported && StringUtils.isNotBlank(ovfTemplateDirOnConversionLocation)) {
+                 String sourceOVFDir = String.format("%s/%s", temporaryConvertPath, ovfTemplateDirOnConversionLocation);
 -                s_logger.debug("Cleaning up exported OVA at dir " + sourceOVFDir);
++                logger.debug("Cleaning up exported OVA at dir " + sourceOVFDir);
+                 Script.runSimpleBashScript("rm -rf " + sourceOVFDir);
+             }
              if (conversionTemporaryLocation instanceof NfsTO) {
 -                s_logger.debug("Cleaning up secondary storage temporary location");
 +                logger.debug("Cleaning up secondary storage temporary location");
                  storagePoolMgr.deleteStoragePool(temporaryStoragePool.getType(), temporaryStoragePool.getUuid());
              }
          }
@@@ -231,14 -278,19 +278,19 @@@
              KVMStoragePool destinationPool = storagePoolMgr.getStoragePool(Storage.StoragePoolType.NetworkFilesystem, poolPath);
              if (destinationPool == null) {
                  String err = String.format("Could not find a storage pool by URI: %s", poolPath);
 -                s_logger.error(err);
 +                logger.error(err);
                  continue;
              }
+             if (destinationPool.getType() != Storage.StoragePoolType.NetworkFilesystem) {
+                 String err = String.format("Storage pool by URI: %s is not an NFS storage", poolPath);
 -                s_logger.error(err);
++                logger.error(err);
+                 continue;
+             }
              KVMPhysicalDisk sourceDisk = temporaryDisks.get(i);
 -            if (s_logger.isDebugEnabled()) {
 +            if (logger.isDebugEnabled()) {
                  String msg = String.format("Trying to copy converted instance disk number %s from the temporary location %s" +
                          " to destination storage pool %s", i, sourceDisk.getPool().getLocalPath(), destinationPool.getUuid());
 -                s_logger.debug(msg);
 +                logger.debug(msg);
              }
  
              String destinationName = UUID.randomUUID().toString();
@@@ -305,6 -357,7 +357,7 @@@
          String sourceHostIp = null;
          String sourcePath = null;
          String storagePoolMountPoint = Script.runSimpleBashScript(String.format("mount | grep %s", storagePool.getLocalPath()));
 -        s_logger.debug(String.format("NFS Storage pool: %s - local path: %s, mount point: %s", storagePool.getUuid(), storagePool.getLocalPath(), storagePoolMountPoint));
++        logger.debug(String.format("NFS Storage pool: %s - local path: %s, mount point: %s", storagePool.getUuid(), storagePool.getLocalPath(), storagePoolMountPoint));
          if (StringUtils.isNotEmpty(storagePoolMountPoint)) {
              String[] res = storagePoolMountPoint.strip().split(" ");
              res = res[0].split(":");
@@@ -314,16 -369,33 +369,33 @@@
          return new Pair<>(sourceHostIp, sourcePath);
      }
  
-     protected boolean performInstanceConversion(String convertInstanceUrl, String sourceInstanceName,
-                                               String temporaryPasswordFilePath,
-                                               String temporaryConvertFolder,
-                                               String temporaryConvertUuid,
-                                               long timeout, boolean verboseModeEnabled) {
+     private boolean exportOVAFromVMOnVcenter(String vmExportUrl,
+                                              String targetOvfDir,
+                                              int noOfThreads,
+                                              long timeout) {
 -        Script script = new Script("ovftool", timeout, s_logger);
++        Script script = new Script("ovftool", timeout, logger);
+         script.add("--noSSLVerify");
+         if (noOfThreads > 1) {
+             script.add(String.format("--parallelThreads=%s", noOfThreads));
+         }
+         script.add(vmExportUrl);
+         script.add(targetOvfDir);
+ 
+         String logPrefix = "export ovf";
 -        OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(s_logger, logPrefix);
++        OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(logger, logPrefix);
+         script.execute(outputLogger);
+         int exitValue = script.getExitValue();
+         return exitValue == 0;
+     }
+ 
+     protected boolean performInstanceConversion(String sourceOVFDirPath,
+                                                 String temporaryConvertFolder,
+                                                 String temporaryConvertUuid,
+                                                 long timeout, boolean verboseModeEnabled) {
 -        Script script = new Script("virt-v2v", timeout, s_logger);
 +        Script script = new Script("virt-v2v", timeout, logger);
          script.add("--root", "first");
-         script.add("-ic", convertInstanceUrl);
-         script.add(sourceInstanceName);
-         script.add("--password-file", temporaryPasswordFilePath);
+         script.add("-i", "ova");
+         script.add(sourceOVFDirPath);
          script.add("-o", "local");
          script.add("-os", temporaryConvertFolder);
          script.add("-of", "qcow2");
@@@ -332,8 -404,8 +404,8 @@@
              script.add("-v");
          }
  
-         String logPrefix = String.format("virt-v2v source: %s %s progress", convertInstanceUrl, sourceInstanceName);
+         String logPrefix = String.format("virt-v2v ovf source: %s progress", sourceOVFDirPath);
 -        OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(s_logger, logPrefix);
 +        OutputInterpreter.LineByLineOutputLogger outputLogger = new OutputInterpreter.LineByLineOutputLogger(logger, logPrefix);
          script.execute(outputLogger);
          int exitValue = script.getExitValue();
          return exitValue == 0;
diff --cc plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
index 97a4c4dc044,e26b2c51790..6b9e52c3e26
--- a/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
+++ b/plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java
@@@ -32,8 -33,8 +33,9 @@@ import org.apache.cloudstack.utils.qemu
  import org.apache.cloudstack.utils.qemu.QemuImgFile;
  import org.apache.cloudstack.utils.qemu.QemuObject;
  import org.apache.commons.codec.binary.Base64;
 +import org.apache.logging.log4j.Logger;
 +import org.apache.logging.log4j.LogManager;
+ import org.apache.commons.collections.CollectionUtils;
 -import org.apache.log4j.Logger;
  import org.libvirt.Connect;
  import org.libvirt.LibvirtException;
  import org.libvirt.Secret;
@@@ -374,6 -375,42 +376,42 @@@ public class LibvirtStorageAdaptor impl
  
      }
  
+     private List<String> getNFSMountOptsFromDetails(StoragePoolType type, Map<String, String> details) {
+         List<String> nfsMountOpts = null;
+         if (!type.equals(StoragePoolType.NetworkFilesystem) || details == null) {
+             return nfsMountOpts;
+         }
+         if (details.containsKey(ApiConstants.NFS_MOUNT_OPTIONS)) {
+             nfsMountOpts = Arrays.asList(details.get(ApiConstants.NFS_MOUNT_OPTIONS).replaceAll("\\s", "").split(","));
+         }
+         return nfsMountOpts;
+     }
+ 
+     private boolean destroyStoragePoolOnNFSMountOptionsChange(StoragePool sp, Connect conn, List<String> nfsMountOpts) {
+         try {
+             LibvirtStoragePoolDef poolDef = getStoragePoolDef(conn, sp);
+             Set poolNfsMountOpts = poolDef.getNfsMountOpts();
+             boolean mountOptsDiffer = false;
+             if (poolNfsMountOpts.size() != nfsMountOpts.size()) {
+                 mountOptsDiffer = true;
+             } else {
+                 for (String nfsMountOpt : nfsMountOpts) {
+                     if (!poolNfsMountOpts.contains(nfsMountOpt)) {
+                         mountOptsDiffer = true;
+                         break;
+                     }
+                 }
+             }
+             if (mountOptsDiffer) {
+                 sp.destroy();
+                 return true;
+             }
+         } catch (LibvirtException e) {
 -            s_logger.error("Failure in destroying the pre-existing storage pool for changing the NFS mount options" + e);
++            logger.error("Failure in destroying the pre-existing storage pool for changing the NFS mount options" + e);
+         }
+         return false;
+     }
+ 
      private StoragePool createRBDStoragePool(Connect conn, String uuid, String host, int port, String userInfo, String path) {
  
          LibvirtStoragePoolDef spd;
@@@ -669,25 -700,34 +707,34 @@@
                      }
                  }
              } catch (LibvirtException e) {
 -                s_logger.error("Failure in attempting to see if an existing storage pool might be using the path of the pool to be created:" + e);
 +                logger.error("Failure in attempting to see if an existing storage pool might be using the path of the pool to be created:" + e);
              }
+         }
+ 
+         List<String> nfsMountOpts = getNFSMountOptsFromDetails(type, details);
+         if (sp != null && CollectionUtils.isNotEmpty(nfsMountOpts) &&
+             destroyStoragePoolOnNFSMountOptionsChange(sp, conn, nfsMountOpts)) {
+             sp = null;
+         }
+ 
+         if (sp == null) {
  
 -            s_logger.debug("Attempting to create storage pool " + name);
 +            logger.debug("Attempting to create storage pool " + name);
  
              if (type == StoragePoolType.NetworkFilesystem) {
                  try {
-                     sp = createNetfsStoragePool(PoolType.NETFS, conn, name, host, path);
+                     sp = createNetfsStoragePool(PoolType.NETFS, conn, name, host, path, nfsMountOpts);
                  } catch (LibvirtException e) {
 -                    s_logger.error("Failed to create netfs mount: " + host + ":" + path , e);
 -                    s_logger.error(e.getStackTrace());
 +                    logger.error("Failed to create netfs mount: " + host + ":" + path , e);
 +                    logger.error(e.getStackTrace());
                      throw new CloudRuntimeException(e.toString());
                  }
              } else if (type == StoragePoolType.Gluster) {
                  try {
-                     sp = createNetfsStoragePool(PoolType.GLUSTERFS, conn, name, host, path);
+                     sp = createNetfsStoragePool(PoolType.GLUSTERFS, conn, name, host, path, null);
                  } catch (LibvirtException e) {
 -                    s_logger.error("Failed to create glusterfs mount: " + host + ":" + path , e);
 -                    s_logger.error(e.getStackTrace());
 +                    logger.error("Failed to create glusterfs mount: " + host + ":" + path , e);
 +                    logger.error(e.getStackTrace());
                      throw new CloudRuntimeException(e.toString());
                  }
              } else if (type == StoragePoolType.SharedMountPoint || type == StoragePoolType.Filesystem) {
diff --cc plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/guru/VMwareGuru.java
index 972fa4828df,822fc870c67..012ebc1b8b5
--- 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
@@@ -158,8 -170,10 +169,9 @@@ import com.vmware.vim25.VirtualEthernet
  import com.vmware.vim25.VirtualEthernetCardNetworkBackingInfo;
  import com.vmware.vim25.VirtualMachineConfigSummary;
  import com.vmware.vim25.VirtualMachineRuntimeInfo;
+ import com.vmware.vim25.VmwareDistributedVirtualSwitchVlanIdSpec;
  
  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();
  
  
@@@ -901,8 -938,13 +936,13 @@@
          Map<String, NetworkVO> mapping = new HashMap<>();
          for (String networkName : vmNetworkNames) {
              NetworkVO networkVO = getGuestNetworkFromNetworkMorName(networkName, accountId, zoneId, domainId);
-             logger.debug(String.format("Mapping network name [%s] to networkVO [id: %s].", networkName, networkVO.getUuid()));
-             mapping.put(networkName, networkVO);
+             URI broadcastUri = networkVO.getBroadcastUri();
+             if (broadcastUri == null) {
+                 continue;
+             }
+             String vlan = broadcastUri.getHost();
 -            s_logger.debug(String.format("Mapping network vlan [%s] to networkVO [id: %s].", vlan, networkVO.getUuid()));
++            logger.debug(String.format("Mapping network vlan [%s] to networkVO [id: %s].", vlan, networkVO.getUuid()));
+             mapping.put(vlan, networkVO);
          }
          return mapping;
      }
@@@ -932,13 -1024,21 +1022,21 @@@
          VmwareContext context = dcMo.getContext();
          List<NicVO> allNics = nicDao.listByVmId(vm.getId());
          for (VirtualDevice nicDevice : nicDevices) {
-             Pair<String, String> pair = getNicMacAddressAndNetworkName(nicDevice, context);
+             Pair<String, String> pair = getNicMacAddressAndVlan(nicDevice, context);
              String macAddress = pair.first();
-             String networkName = pair.second();
-             NetworkVO networkVO = networksMapping.get(networkName);
+             String vlanId = pair.second();
+             if (vlanId == null) {
+                 s_logger.warn(String.format("vlanId for MAC address [%s] is null", macAddress));
+                 continue;
+             }
+             NetworkVO networkVO = networksMapping.get(vlanId);
+             if (networkVO == null) {
+                 s_logger.warn(String.format("Cannot find network for MAC address [%s] and vlanId [%s]", macAddress, vlanId));
+                 continue;
+             }
              NicVO nicVO = nicDao.findByNetworkIdAndMacAddressIncludingRemoved(networkVO.getId(), macAddress);
              if (nicVO != null) {
 -                s_logger.warn(String.format("Find NIC in DB with networkId [%s] and MAC Address [%s], so this NIC will be removed from list of unmapped NICs of VM [id: %s, name: %s].",
 +                logger.warn(String.format("Find NIC in DB with networkId [%s] and MAC Address [%s], so this NIC will be removed from list of unmapped NICs of VM [id: %s, name: %s].",
                          networkVO.getId(), macAddress, vm.getUuid(), vm.getInstanceName()));
                  allNics.remove(nicVO);
  
@@@ -1254,17 -1360,26 +1358,26 @@@
          VirtualMachineMO clonedVM = dataCenterMO.findVm(cloneName);
          if (!result || clonedVM == null) {
              String err = String.format("Could not clone VM %s before migration from VMware", vmName);
 -            s_logger.error(err);
 +            logger.error(err);
              throw new CloudRuntimeException(err);
          }
+ 
          relocateClonedVMToSourceHost(clonedVM, sourceHost);
          return clonedVM;
      }
  
+     private String createOVFTemplateOfVM(VirtualMachineMO vmMO, DataStoreTO convertLocation, int threadsCountToExportOvf) throws Exception {
+         String dataStoreUrl = getDataStoreUrlForTemplate(convertLocation);
+         String vmOvfName = UUID.randomUUID().toString();
+         String vmOvfCreationPath = createDirOnStorage(vmOvfName, dataStoreUrl, null);
 -        s_logger.debug(String.format("Creating OVF %s for the VM %s at %s", vmOvfName, vmMO.getName(), vmOvfCreationPath));
++        logger.debug(String.format("Creating OVF %s for the VM %s at %s", vmOvfName, vmMO.getName(), vmOvfCreationPath));
+         vmMO.exportVm(vmOvfCreationPath, vmOvfName, false, false, threadsCountToExportOvf);
 -        s_logger.debug(String.format("Created OVF %s for the VM %s at %s", vmOvfName, vmMO.getName(), vmOvfCreationPath));
++        logger.debug(String.format("Created OVF %s for the VM %s at %s", vmOvfName, vmMO.getName(), vmOvfCreationPath));
+         return vmOvfName;
+     }
+ 
      @Override
-     public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName,
-                                                                  Map<String, String> params) {
-         logger.debug(String.format("Cloning VM %s on external vCenter %s", vmName, hostIp));
+     public Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequired(String hostIp, String vmName, Map<String, String> params) {
          String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST);
          String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME);
          String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME);
@@@ -1275,26 -1390,46 +1388,47 @@@
              DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter);
              VirtualMachineMO vmMo = dataCenterMO.findVm(vmName);
              if (vmMo == null) {
-                 String err = String.format("Cannot find VM with name %s on %s/%s", vmName, vcenter, datacenter);
+                 String err = String.format("Cannot find VM with name %s on vCenter %s/%s", vmName, vcenter, datacenter);
 -                s_logger.error(err);
 +                logger.error(err);
                  throw new CloudRuntimeException(err);
              }
+ 
              VirtualMachinePowerState sourceVmPowerState = vmMo.getPowerState();
-             if (sourceVmPowerState == VirtualMachinePowerState.POWERED_ON && isWindowsVm(vmMo)) {
-                 logger.debug(String.format("VM %s is a Windows VM and its Running, cannot be imported." +
-                                 "Please gracefully shut it down before attempting the import",
-                         vmName));
++
+             if (sourceVmPowerState == VirtualMachinePowerState.POWERED_OFF) {
+                 // Don't clone for powered off VMs, can export OVF from it
+                 UnmanagedInstanceTO instanceTO = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), vmMo);
+                 return new Pair<>(instanceTO, false);
              }
  
+             if (sourceVmPowerState == VirtualMachinePowerState.POWERED_ON) {
+                 if (isWindowsVm(vmMo)) {
+                     String err = String.format("VM %s is a Windows VM and its Running, cannot be imported." +
+                             " Please gracefully shut it down before attempting the import", vmName);
 -                    s_logger.error(err);
++                    logger.error(err);
+                     throw new CloudRuntimeException(err);
+                 }
+ 
+                 if (isVMOnStandaloneHost(vmMo)) { // or datacenter.equalsIgnoreCase("ha-datacenter")? [Note: default datacenter name on standalone host: ha-datacenter]
+                     String err = String.format("VM %s might be on standalone host and is Running, cannot be imported." +
+                             " Please shut it down before attempting the import", vmName);
 -                    s_logger.error(err);
++                    logger.error(err);
+                     throw new CloudRuntimeException(err);
+                 }
+             }
+ 
 -            s_logger.debug(String.format("Cloning VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter));
++            logger.debug(String.format("Cloning VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter));
              VirtualMachineMO clonedVM = createCloneFromSourceVM(vmName, vmMo, dataCenterMO);
-             logger.debug(String.format("VM %s cloned successfully", vmName));
 -            s_logger.debug(String.format("VM %s cloned successfully, to VM %s", vmName, clonedVM.getName()));
++            logger.debug(String.format("VM %s cloned successfully, to VM %s", vmName, clonedVM.getName()));
              UnmanagedInstanceTO clonedInstance = VmwareHelper.getUnmanagedInstance(vmMo.getRunningHost(), clonedVM);
-             setNicsFromSourceVM(clonedInstance, vmMo);
+             setDisksFromSourceVM(clonedInstance, vmMo);
              clonedInstance.setCloneSourcePowerState(sourceVmPowerState == VirtualMachinePowerState.POWERED_ON ? UnmanagedInstanceTO.PowerState.PowerOn : UnmanagedInstanceTO.PowerState.PowerOff);
-             return clonedInstance;
+             return new Pair<>(clonedInstance, true);
+         } catch (CloudRuntimeException cre) {
+             throw cre;
          } catch (Exception e) {
-             String err = String.format("Error cloning VM: %s from external vCenter %s: %s", vmName, vcenter, e.getMessage());
+             String err = String.format("Error while finding or cloning VM: %s from vCenter %s: %s", vmName, vcenter, e.getMessage());
 -            s_logger.error(err, e);
 +            logger.error(err, e);
              throw new CloudRuntimeException(err, e);
          }
      }
@@@ -1315,13 -1455,41 +1454,41 @@@
          }
      }
  
+     @Override
+     public String createVMTemplateOutOfBand(String hostIp, String vmName, Map<String, String> params, DataStoreTO templateLocation, int threadsCountToExportOvf) {
+         String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST);
+         String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME);
+         String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME);
+         String password = params.get(VmDetailConstants.VMWARE_VCENTER_PASSWORD);
 -        s_logger.debug(String.format("Creating template of the VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter));
++        logger.debug(String.format("Creating template of the VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter));
+ 
+         try {
+             VmwareContext context = connectToVcenter(vcenter, username, password);
+             DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter);
+             VirtualMachineMO vmMo = dataCenterMO.findVm(vmName);
+             if (vmMo == null) {
+                 String err = String.format("Cannot find VM with name %s on vCenter %s/%s, to create template file", vmName, vcenter, datacenter);
 -                s_logger.error(err);
++                logger.error(err);
+                 throw new CloudRuntimeException(err);
+             }
+             String ovaTemplate = createOVFTemplateOfVM(vmMo, templateLocation, threadsCountToExportOvf);
 -            s_logger.debug(String.format("OVF %s created successfully on the datastore", ovaTemplate));
++            logger.debug(String.format("OVF %s created successfully on the datastore", ovaTemplate));
+             return ovaTemplate;
+         } catch (Exception e) {
+             String err = String.format("Error create template file of the VM: %s from vCenter %s: %s", vmName, vcenter, e.getMessage());
 -            s_logger.error(err, e);
++            logger.error(err, e);
+             throw new CloudRuntimeException(err, e);
+         }
+     }
+ 
      @Override
      public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params) {
-         logger.debug(String.format("Removing VM %s on external vCenter %s", vmName, hostIp));
          String vcenter = params.get(VmDetailConstants.VMWARE_VCENTER_HOST);
          String datacenter = params.get(VmDetailConstants.VMWARE_DATACENTER_NAME);
          String username = params.get(VmDetailConstants.VMWARE_VCENTER_USERNAME);
          String password = params.get(VmDetailConstants.VMWARE_VCENTER_PASSWORD);
 -        s_logger.debug(String.format("Removing cloned VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter));
++        logger.debug(String.format("Removing cloned VM %s at VMware host %s on vCenter %s", vmName, hostIp, vcenter));
+ 
          try {
              VmwareContext context = connectToVcenter(vcenter, username, password);
              DatacenterMO dataCenterMO = new DatacenterMO(context, datacenter);
@@@ -1329,14 -1497,100 +1496,100 @@@
              if (vmMo == null) {
                  String err = String.format("Cannot find VM %s on datacenter %s, not possible to remove VM out of band",
                          vmName, datacenter);
 -                s_logger.error(err);
 +                logger.error(err);
                  return false;
              }
+ 
              return vmMo.destroy();
          } catch (Exception e) {
-             String err = String.format("Error destroying external VM %s: %s", vmName, e.getMessage());
+             String err = String.format("Error destroying cloned VM %s: %s", vmName, e.getMessage());
 -            s_logger.error(err, e);
++            logger.error(err, e);
+             return false;
+         }
+     }
+ 
+     @Override
+     public boolean removeVMTemplateOutOfBand(DataStoreTO templateLocation, String templateDir) {
 -        s_logger.debug(String.format("Removing template %s", templateDir));
++        logger.debug(String.format("Removing template %s", templateDir));
+ 
+         try {
+             String dataStoreUrl = getDataStoreUrlForTemplate(templateLocation);
+             return deleteDirOnStorage(templateDir, dataStoreUrl, null);
+         } catch (Exception e) {
+             String err = String.format("Error removing template file %s: %s", templateDir, e.getMessage());
 -            s_logger.error(err, e);
 +            logger.error(err, e);
              return false;
          }
      }
+ 
+     private String getDataStoreUrlForTemplate(DataStoreTO templateLocation) {
+         String dataStoreUrl = null;
+         if (templateLocation instanceof NfsTO) {
+             NfsTO nfsStore = (NfsTO) templateLocation;
+             dataStoreUrl = nfsStore.getUrl();
+         } else if (templateLocation instanceof PrimaryDataStoreTO) {
+             PrimaryDataStoreTO primaryDataStoreTO = (PrimaryDataStoreTO) templateLocation;
+             if (primaryDataStoreTO.getPoolType().equals(Storage.StoragePoolType.NetworkFilesystem)) {
+                 String psHost = primaryDataStoreTO.getHost();
+                 String psPath = primaryDataStoreTO.getPath();
+                 dataStoreUrl = "nfs://" + psHost + File.separator + psPath;
+             }
+         }
+ 
+         if (dataStoreUrl == null) {
+             throw new CloudRuntimeException("Only NFS storage is supported for template creation");
+         }
+ 
+         return dataStoreUrl;
+     }
+ 
+     private String createDirOnStorage(String dirName, String nfsStorageUrl, String nfsVersion) throws Exception {
+         String mountPoint = mountManager.getMountPoint(nfsStorageUrl, nfsVersion);
 -        s_logger.debug("Create dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName);
++        logger.debug("Create dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName);
+         String dirMountPath = mountPoint + File.separator + dirName;
+         createDir(dirMountPath);
+         return dirMountPath;
+     }
+ 
+     private void createDir(String dirName) throws Exception {
+         synchronized (dirName.intern()) {
 -            Script command = new Script("mkdir", s_logger);
++            Script command = new Script("mkdir", logger);
+             command.add("-p");
+             command.add(dirName);
+             String cmdResult = command.execute();
+             if (cmdResult != null) {
+                 String msg = "Unable to create directory: " + dirName + ", error msg: " + cmdResult;
 -                s_logger.error(msg);
++                logger.error(msg);
+                 throw new Exception(msg);
+             }
+         }
+     }
+ 
+     private boolean deleteDirOnStorage(String dirName, String nfsStorageUrl, String nfsVersion) throws Exception {
+         try {
+             String mountPoint = mountManager.getMountPoint(nfsStorageUrl, nfsVersion);
 -            s_logger.debug("Delete dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName);
++            logger.debug("Delete dir storage location - url: " + nfsStorageUrl + ", mount point: " + mountPoint + ", dir: " + dirName);
+             String dirMountPath = mountPoint + File.separator + dirName;
+             deleteDir(dirMountPath);
+             return true;
+         } catch (Exception e) {
+             String err = String.format("Unable to delete dir %s: %s", dirName, e.getMessage());
 -            s_logger.error(err, e);
++            logger.error(err, e);
+             return false;
+         }
+     }
+ 
+     private void deleteDir(String dirName) throws Exception {
+         synchronized (dirName.intern()) {
 -            Script command = new Script("rm", s_logger);
++            Script command = new Script("rm", logger);
+             command.add("-rf");
+             command.add(dirName);
+             String cmdResult = command.execute();
+             if (cmdResult != null) {
+                 String msg = "Unable to delete directory: " + dirName + ", error msg: " + cmdResult;
 -                s_logger.error(msg);
++                logger.error(msg);
+                 throw new Exception(msg);
+             }
+         }
+     }
  }
diff --cc plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
index 8f03648deb6,f53a70de722..e06268cd6ab
--- a/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
+++ b/plugins/hypervisors/xenserver/src/main/java/com/cloud/hypervisor/xenserver/resource/CitrixResourceBase.java
@@@ -5662,10 -5680,10 +5678,10 @@@ public abstract class CitrixResourceBas
          // attach the config drive in destination host
  
          try {
 -            s_logger.debug("Attaching config drive iso device for the VM " + vmName + " In host " + ipAddr);
 +            logger.debug("Attaching config drive iso device for the VM " + vmName + " In host " + ipAddr);
              Set<VM> vms = VM.getByNameLabel(conn, vmName);
  
-             SR sr = getSRByNameLabel(conn, vmName + VM_NAME_ISO_SUFFIX);
+             SR sr = getSRByNameLabel(conn, vmName + VM_NAME_CONFIGDRIVE_ISO_SUFFIX);
              //Here you will find only two vdis with the <vmname>.iso.
              //one is from source host and second from dest host
              Set<VDI> vdis = VDI.getByNameLabel(conn, vmName + VM_FILE_ISO_SUFFIX);
diff --cc plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java
index 880213020ee,73d9ca4b4f9..3b0add21ca5
--- a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java
+++ b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java
@@@ -411,7 -410,11 +411,11 @@@ public class CloudStackPrimaryDataStore
                  primaryDataStoreDao.expunge(primarystore.getId());
                  throw new CloudRuntimeException("Storage has already been added as local storage");
              } catch (Exception e) {
 -                s_logger.warn("Unable to establish a connection between " + h + " and " + primarystore, e);
 +                logger.warn("Unable to establish a connection between " + h + " and " + primarystore, e);
+                 String reason = storageMgr.getStoragePoolMountFailureReason(e.getMessage());
+                 if (reason != null) {
+                     throw new CloudRuntimeException(reason);
+                 }
              }
          }
  
@@@ -427,8 -430,8 +431,8 @@@
  
      @Override
      public boolean attachZone(DataStore dataStore, ZoneScope scope, HypervisorType hypervisorType) {
-         List<HostVO> hosts = _resourceMgr.listAllUpAndEnabledHostsInOneZoneByHypervisor(hypervisorType, scope.getScopeId());
+         List<HostVO> hosts = _resourceMgr.listAllUpHostsInOneZoneByHypervisor(hypervisorType, scope.getScopeId());
 -        s_logger.debug("In createPool. Attaching the pool to each of the hosts.");
 +        logger.debug("In createPool. Attaching the pool to each of the hosts.");
          List<HostVO> poolHosts = new ArrayList<HostVO>();
          for (HostVO host : hosts) {
              try {
@@@ -438,7 -441,11 +442,11 @@@
                      primaryDataStoreDao.expunge(dataStore.getId());
                      throw new CloudRuntimeException("Storage has already been added as local storage to host: " + host.getName());
              } catch (Exception e) {
 -                s_logger.warn("Unable to establish a connection between " + host + " and " + dataStore, e);
 +                logger.warn("Unable to establish a connection between " + host + " and " + dataStore, e);
+                 String reason = storageMgr.getStoragePoolMountFailureReason(e.getMessage());
+                 if (reason != null) {
+                     throw new CloudRuntimeException(reason);
+                 }
              }
          }
          if (poolHosts.isEmpty()) {
diff --cc plugins/storage/volume/default/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImplTest.java
index 8518d79417b,1939dc26db2..924c98b7912
--- a/plugins/storage/volume/default/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImplTest.java
+++ b/plugins/storage/volume/default/src/test/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImplTest.java
@@@ -55,9 -56,8 +56,8 @@@ import org.mockito.InjectMocks
  import org.mockito.Mock;
  import org.mockito.Mockito;
  import org.mockito.MockitoAnnotations;
 -import org.mockito.runners.MockitoJUnitRunner;
 +import org.mockito.junit.MockitoJUnitRunner;
  import org.springframework.test.util.ReflectionTestUtils;
- 
  import java.util.ArrayList;
  import java.util.List;
  import java.util.UUID;
diff --cc plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolStatsCollector.java
index a41ff66229c,44acd1eab75..56b150a04e6
--- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolStatsCollector.java
+++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/datastore/driver/StorPoolStatsCollector.java
@@@ -35,7 -38,14 +38,13 @@@ import org.apache.cloudstack.storage.da
  import org.apache.cloudstack.storage.datastore.util.StorPoolUtil;
  import org.apache.cloudstack.storage.snapshot.StorPoolConfigurationManager;
  import org.apache.commons.collections.CollectionUtils;
 -import org.apache.log4j.Logger;
+ import org.apache.commons.lang3.StringUtils;
  
+ import com.cloud.storage.DataStoreRole;
+ import com.cloud.storage.SnapshotVO;
+ import com.cloud.storage.dao.SnapshotDao;
+ import com.cloud.storage.dao.SnapshotDetailsDao;
+ import com.cloud.storage.dao.SnapshotDetailsVO;
  import com.cloud.utils.NumbersUtil;
  import com.cloud.utils.Pair;
  import com.cloud.utils.component.ManagerBase;
@@@ -182,4 -208,90 +205,90 @@@ public class StorPoolStatsCollector ext
              }
          }
      }
+ 
+     class StorPoolSnapshotsWithDelayDelete implements Runnable {
+ 
+         @Override
+         public void run() {
+             List<StoragePoolVO> spPools = storagePoolDao.findPoolsByProvider(StorPoolUtil.SP_PROVIDER_NAME);
+             if (CollectionUtils.isNotEmpty(spPools)) {
+                 Map<Long, StoragePoolVO> onePoolForZone = new HashMap<>();
+                 for (StoragePoolVO storagePoolVO : spPools) {
+                     onePoolForZone.put(storagePoolVO.getDataCenterId(), storagePoolVO);
+                 }
+                 for (StoragePoolVO storagePool : onePoolForZone.values()) {
+                     List<SnapshotDetailsVO> snapshotsDetails = snapshotDetailsDao.findDetailsByZoneAndKey(storagePool.getDataCenterId(), StorPoolUtil.SP_DELAY_DELETE);
+                     if (CollectionUtils.isEmpty(snapshotsDetails)) {
+                         return;
+                     }
+                     Map<String, String> snapshotsWithDelayDelete = new HashMap<>();
+ 
+                     try {
 -                        log.debug(String.format("Collecting snapshots marked to be deleted for zone [%s]", storagePool.getDataCenterId()));
++                        logger.debug(String.format("Collecting snapshots marked to be deleted for zone [%s]", storagePool.getDataCenterId()));
+                         JsonArray arr = StorPoolUtil.snapshotsListAllClusters(StorPoolUtil.getSpConnection(storagePool.getUuid(),
+                                 storagePool.getId(), storagePoolDetailsDao, storagePoolDao));
+                          snapshotsWithDelayDelete.putAll(getSnapshotsMarkedForDeletion(arr));
 -                         log.debug(String.format("Found snapshot details [%s] and snapshots on StorPool with delay delete flag [%s]", snapshotsDetails, snapshotsWithDelayDelete));
++                         logger.debug(String.format("Found snapshot details [%s] and snapshots on StorPool with delay delete flag [%s]", snapshotsDetails, snapshotsWithDelayDelete));
+                          syncSnapshots(snapshotsDetails, snapshotsWithDelayDelete);
+                     } catch (Exception e) {
 -                        log.debug("Could not fetch the snapshots with delay delete flag " + e.getMessage());
++                        logger.debug("Could not fetch the snapshots with delay delete flag " + e.getMessage());
+                     }
+                 }
+             }
+         }
+ 
+         private void syncSnapshots(List<SnapshotDetailsVO> snapshotsDetails,
+                 Map<String, String> snapshotsWithDelayDelete) {
+             for (SnapshotDetailsVO snapshotDetailsVO : snapshotsDetails) {
+                  if (!snapshotsWithDelayDelete.containsKey(snapshotDetailsVO.getValue())) {
+                      StorPoolUtil.spLog("The snapshot [%s] with delayDelete flag is no longer on StorPool. Removing it from CloudStack", snapshotDetailsVO.getValue());
+                      SnapshotDataStoreVO ss = snapshotDataStoreDao
+                              .findBySourceSnapshot(snapshotDetailsVO.getResourceId(), DataStoreRole.Primary);
+                      if (ss != null) {
+                          ss.setState(State.Destroyed);
+                          snapshotDataStoreDao.update(ss.getId(), ss);
+                      }
+                      SnapshotVO snap = snapshotDao.findById(snapshotDetailsVO.getResourceId());
+                      if (snap != null) {
+                          snap.setState(com.cloud.storage.Snapshot.State.Destroyed);
+                          snapshotDao.update(snap.getId(), snap);
+                      }
+                      snapshotDetailsDao.remove(snapshotDetailsVO.getId());
+                  }
+              }
+         }
+ 
+         private  Map<String, String> getSnapshotsMarkedForDeletion(JsonArray arr) {
+             for (JsonElement jsonElement : arr) {
+                 JsonObject error = jsonElement.getAsJsonObject().getAsJsonObject("error");
+                 if (error != null) {
+                     throw new CloudRuntimeException(String.format("Could not collect the snapshots marked for deletion from all storage nodes due to: [%s]", error));
+                 }
+             }
+             Map<String, String> snapshotsWithDelayDelete = new HashMap<>();
+             for (JsonElement jsonElement : arr) {
+                 JsonObject response = jsonElement.getAsJsonObject().getAsJsonObject("response");
+                 if (response == null) {
+                     return snapshotsWithDelayDelete;
+                 }
+                 collectSnapshots(snapshotsWithDelayDelete, response);
+             }
 -            log.debug("Found snapshots on StorPool" + snapshotsWithDelayDelete);
++            logger.debug("Found snapshots on StorPool" + snapshotsWithDelayDelete);
+             return snapshotsWithDelayDelete;
+         }
+ 
+         private void collectSnapshots(Map<String, String> snapshotsWithDelayDelete, JsonObject response) {
+             JsonArray snapshots = response.getAsJsonObject().getAsJsonArray("data");
+             for (JsonElement snapshot : snapshots) {
+                 String name = snapshot.getAsJsonObject().get("name").getAsString();
+                 JsonObject tags = snapshot.getAsJsonObject().get("tags").getAsJsonObject();
+                 if (!StringUtils.startsWith(name, "*") && StringUtils.containsNone(name, "@") && tags != null && !tags.entrySet().isEmpty()) {
+                     String tag = tags.getAsJsonPrimitive("cs").getAsString();
+                     if (tag != null && tag.equals(StorPoolUtil.DELAY_DELETE)) {
+                         snapshotsWithDelayDelete.put(name, tag);
+                     }
+                 }
+             }
+         }
+     }
  }
diff --cc plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java
index 0b58247c661,23361bd1e5e..5cdb7b8cda1
--- a/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java
+++ b/plugins/storage/volume/storpool/src/main/java/org/apache/cloudstack/storage/snapshot/StorPoolSnapshotStrategy.java
@@@ -130,9 -131,19 +132,19 @@@ public class StorPoolSnapshotStrategy i
          return res;
      }
  
+     private void markSnapshotAsDestroyedIfAlreadyRemoved(Long snapshotId, SpApiResponse resp) {
+         if (resp.getError().getName().equals("objectDoesNotExist")) {
+             SnapshotDataStoreVO snapshotOnPrimary = _snapshotStoreDao.findBySourceSnapshot(snapshotId, DataStoreRole.Primary);
+             if (snapshotOnPrimary != null) {
+                 snapshotOnPrimary.setState(State.Destroyed);
+                 _snapshotStoreDao.update(snapshotOnPrimary.getId(), snapshotOnPrimary);
+             }
+         }
+     }
+ 
      @Override
      public StrategyPriority canHandle(Snapshot snapshot, Long zoneId, SnapshotOperation op) {
 -        log.debug(String.format("StorpoolSnapshotStrategy.canHandle: snapshot=%s, uuid=%s, op=%s", snapshot.getName(), snapshot.getUuid(), op));
 +        logger.debug(String.format("StorpoolSnapshotStrategy.canHandle: snapshot=%s, uuid=%s, op=%s", snapshot.getName(), snapshot.getUuid(), op));
  
          if (op != SnapshotOperation.DELETE) {
              return StrategyPriority.CANT_HANDLE;
diff --cc server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java
index 49505821fd8,14736042148..197e68300f8
--- a/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/HostJoinDaoImpl.java
@@@ -247,9 -247,9 +247,9 @@@ public class HostJoinDaoImpl extends Ge
                      host.getHypervisorType() == Hypervisor.HypervisorType.Custom)) {
                  //only kvm has the requirement to return host details
                  try {
-                     hostResponse.setDetails(hostDetails);
+                     hostResponse.setDetails(hostDetails, host.getHypervisorType());
                  } catch (Exception e) {
 -                    s_logger.debug("failed to get host details", e);
 +                    logger.debug("failed to get host details", e);
                  }
              }
  
diff --cc server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java
index 0810540a377,b08fb4529f4..76b3ffae566
--- a/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/SnapshotJoinDaoImpl.java
@@@ -33,11 -33,16 +33,15 @@@ import org.apache.cloudstack.engine.sub
  import org.apache.cloudstack.engine.subsystem.api.storage.SnapshotInfo;
  import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
  import org.apache.cloudstack.query.QueryService;
 -import org.apache.log4j.Logger;
  
+ import com.cloud.api.ApiDBUtils;
  import com.cloud.api.ApiResponseHelper;
  import com.cloud.api.query.vo.SnapshotJoinVO;
+ import com.cloud.storage.GuestOS;
  import com.cloud.storage.Snapshot;
  import com.cloud.storage.VMTemplateStorageResourceAssoc;
+ import com.cloud.storage.Volume.Type;
+ import com.cloud.storage.VolumeVO;
  import com.cloud.user.Account;
  import com.cloud.user.AccountService;
  import com.cloud.utils.Pair;
diff --cc server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index d9277377cbf,29579759c7f..fce4b3496fc
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@@ -1180,11 -1175,10 +1182,10 @@@ public class ConfigurationManagerImpl e
          return new Pair<Configuration, String>(_configDao.findByName(name), newValue);
      }
  
-     private String validateConfigurationValue(final String name, String value, final String scope) {
- 
+     protected String validateConfigurationValue(final String name, String value, final String scope) {
          final ConfigurationVO cfg = _configDao.findByName(name);
          if (cfg == null) {
 -            s_logger.error("Missing configuration variable " + name + " in configuration table");
 +            logger.error("Missing configuration variable " + name + " in configuration table");
              return "Invalid configuration variable.";
          }
  
@@@ -1263,45 -1257,48 +1264,47 @@@
              return null;
          }
  
-         if (type.equals(Integer.class) && NetworkModel.MACIdentifier.key().equalsIgnoreCase(name)) {
+         if (type.equals(Integer.class)) {
              try {
                  final int val = Integer.parseInt(value);
-                 //The value need to be between 0 to 255 because the mac generation needs a value of 8 bit
-                 //0 value is considered as disable.
-                 if(val < 0 || val > 255){
-                     throw new InvalidParameterValueException(name+" value should be between 0 and 255. 0 value will disable this feature");
 -
+                 if (NetworkModel.MACIdentifier.key().equalsIgnoreCase(name)) {
+                     //The value need to be between 0 to 255 because the mac generation needs a value of 8 bit
+                     //0 value is considered as disable.
+                     if(val < 0 || val > 255){
+                         throw new InvalidParameterValueException(name + " value should be between 0 and 255. 0 value will disable this feature");
+                     }
                  }
-             } catch (final NumberFormatException e) {
-                 logger.error("There was an error trying to parse the integer value for:" + name);
-                 throw new InvalidParameterValueException("There was an error trying to parse the integer value for:" + name);
-             }
-         }
  
-         if (type.equals(Integer.class) && configValuesForValidation.contains(name)) {
-             try {
-                 final int val = Integer.parseInt(value);
-                 if (val <= 0) {
-                     throw new InvalidParameterValueException("Please enter a positive value for the configuration parameter:" + name);
-                 }
-                 if ("vm.password.length".equalsIgnoreCase(name) && val < 6) {
-                     throw new InvalidParameterValueException("Please enter a value greater than 5 for the configuration parameter:" + name);
+                 if (UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.key().equalsIgnoreCase(name) || UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles.key().equalsIgnoreCase(name)) {
+                     if (val > 10) {
+                         throw new InvalidParameterValueException("Please enter a value between 0 and 10 for the configuration parameter: " + name + ", -1 will disable it");
+                     }
                  }
-                 if ("remote.access.vpn.psk.length".equalsIgnoreCase(name)) {
-                     if (val < 8) {
-                         throw new InvalidParameterValueException("Please enter a value greater than 7 for the configuration parameter:" + name);
+ 
+                 if (configValuesForValidation.contains(name)) {
+                     if (val <= 0) {
+                         throw new InvalidParameterValueException("Please enter a positive value for the configuration parameter:" + name);
                      }
-                     if (val > 256) {
-                         throw new InvalidParameterValueException("Please enter a value less than 257 for the configuration parameter:" + name);
+                     if ("vm.password.length".equalsIgnoreCase(name) && val < 6) {
+                         throw new InvalidParameterValueException("Please enter a value greater than 5 for the configuration parameter:" + name);
                      }
-                 }
-                 if (UserDataManager.VM_USERDATA_MAX_LENGTH_STRING.equalsIgnoreCase(name)) {
-                     if (val > 1048576) {
-                         throw new InvalidParameterValueException("Please enter a value less than 1048576 for the configuration parameter:" + name);
+                     if ("remote.access.vpn.psk.length".equalsIgnoreCase(name)) {
+                         if (val < 8) {
+                             throw new InvalidParameterValueException("Please enter a value greater than 7 for the configuration parameter:" + name);
+                         }
+                         if (val > 256) {
+                             throw new InvalidParameterValueException("Please enter a value less than 257 for the configuration parameter:" + name);
+                         }
+                     }
+                     if (UserDataManager.VM_USERDATA_MAX_LENGTH_STRING.equalsIgnoreCase(name)) {
+                         if (val > 1048576) {
+                             throw new InvalidParameterValueException("Please enter a value less than 1048576 for the configuration parameter:" + name);
+                         }
                      }
                  }
              } catch (final NumberFormatException e) {
-                 logger.error("There was an error trying to parse the integer value for:" + name);
-                 throw new InvalidParameterValueException("There was an error trying to parse the integer value for:" + name);
 -                s_logger.error("There was an error trying to parse the integer value for configuration parameter: " + name);
++                logger.error("There was an error trying to parse the integer value for configuration parameter: " + name);
+                 throw new InvalidParameterValueException("There was an error trying to parse the integer value for configuration parameter: " + name);
              }
          }
  
@@@ -1312,8 -1309,8 +1315,8 @@@
                      throw new InvalidParameterValueException("Please enter a value between 0 and 1 for the configuration parameter: " + name);
                  }
              } catch (final NumberFormatException e) {
-                 logger.error("There was an error trying to parse the float value for:" + name);
-                 throw new InvalidParameterValueException("There was an error trying to parse the float value for:" + name);
 -                s_logger.error("There was an error trying to parse the float value for configuration parameter: " + name);
++                logger.error("There was an error trying to parse the float value for configuration parameter: " + name);
+                 throw new InvalidParameterValueException("There was an error trying to parse the float value for configuration parameter: " + name);
              }
          }
  
diff --cc server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
index 0602026f027,e4344ca8524..53f76f8ad42
--- a/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
+++ b/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyManagerImpl.java
@@@ -491,10 -496,18 +494,18 @@@ public class ConsoleProxyManagerImpl ex
          return null;
      }
  
+     @Override
+     @ActionEvent(eventType = EventTypes.EVENT_PROXY_START, eventDescription = "restarting console proxy VM for HA", async = true)
+     public void startProxyForHA(VirtualMachine vm, Map<VirtualMachineProfile.Param, Object> params,
+             DeploymentPlanner planner) throws InsufficientCapacityException, ResourceUnavailableException,
+             ConcurrentOperationException, OperationTimedoutException {
+         virtualMachineManager.advanceStart(vm.getUuid(), params, planner);
+     }
+ 
      public ConsoleProxyVO assignProxyFromRunningPool(long dataCenterId) {
  
 -        if (s_logger.isDebugEnabled()) {
 -            s_logger.debug("Assign console proxy from running pool for request from data center : " + dataCenterId);
 +        if (logger.isDebugEnabled()) {
 +            logger.debug("Assign console proxy from running pool for request from data center : " + dataCenterId);
          }
  
          ConsoleProxyAllocator allocator = getCurrentAllocator();
diff --cc server/src/main/java/com/cloud/ha/HighAvailabilityManagerImpl.java
index 00e25284f42,81ae44a9763..d8fc99a0934
--- a/server/src/main/java/com/cloud/ha/HighAvailabilityManagerImpl.java
+++ b/server/src/main/java/com/cloud/ha/HighAvailabilityManagerImpl.java
@@@ -435,8 -445,38 +443,38 @@@ public class HighAvailabilityManagerImp
  
      }
  
+     private void startVm(VirtualMachine vm, Map<VirtualMachineProfile.Param, Object> params,
+            DeploymentPlanner planner) throws InsufficientCapacityException, ResourceUnavailableException,
+             ConcurrentOperationException, OperationTimedoutException {
+         CallContext ctx = CallContext.register(CallContext.current(), ApiCommandResourceType.VirtualMachine);
+         ctx.setEventResourceId(vm.getId());
+         try {
+             switch (vm.getType()) {
+                 case DomainRouter:
+                     ctx.setEventResourceType(ApiCommandResourceType.DomainRouter);
+                     routerService.startRouterForHA(vm, params, planner);
+                     break;
+                 case ConsoleProxy:
+                     ctx.setEventResourceType(ApiCommandResourceType.ConsoleProxy);
+                     consoleProxyManager.startProxyForHA(vm, params, planner);
+                     break;
+                 case SecondaryStorageVm:
+                     ctx.setEventResourceType(ApiCommandResourceType.SystemVm);
+                     secondaryStorageVmManager.startSecStorageVmForHA(vm, params, planner);
+                     break;
+                 case User:
+                     userVmManager.startVirtualMachineForHA(vm, params, planner);
+                     break;
+                 default:
+                     _itMgr.advanceStart(vm.getUuid(), params, planner);
+             }
+         } finally {
+             CallContext.unregister();
+         }
+     }
+ 
      protected Long restart(final HaWorkVO work) {
 -        s_logger.debug("RESTART with HAWORK");
 +        logger.debug("RESTART with HAWORK");
          List<HaWorkVO> items = _haDao.listFutureHaWorkForVm(work.getInstanceId(), work.getId());
          if (items.size() > 0) {
              StringBuilder str = new StringBuilder("Cancelling this work item because newer ones have been scheduled.  Work Ids = [");
@@@ -626,10 -666,10 +664,10 @@@
                      }
                  }
                  // First try starting the vm with its original planner, if it doesn't succeed send HAPlanner as its an emergency.
-                 _itMgr.advanceStart(vm.getUuid(), params, null);
-             }catch (InsufficientCapacityException e){
+                 startVm(vm, params, null);
+             } catch (InsufficientCapacityException e){
 -                s_logger.warn("Failed to deploy vm " + vmId + " with original planner, sending HAPlanner");
 +                logger.warn("Failed to deploy vm " + vmId + " with original planner, sending HAPlanner");
-                 _itMgr.advanceStart(vm.getUuid(), params, _haPlanners.get(0));
+                 startVm(vm, params, _haPlanners.get(0));
              }
  
              VMInstanceVO started = _instanceDao.findById(vm.getId());
@@@ -649,17 -689,17 +687,17 @@@
              _alertMgr.sendAlert(alertType, vm.getDataCenterId(), vm.getPodIdToDeployIn(), "Unable to restart " + vm.getHostName() + " which was running on host " +
                  hostDesc, "Insufficient capacity to restart VM, name: " + vm.getHostName() + ", id: " + vmId + " which was running on host " + hostDesc);
          } catch (final ResourceUnavailableException e) {
 -            s_logger.warn("Unable to restart " + vm.toString() + " due to " + e.getMessage());
 +            logger.warn("Unable to restart " + vm.toString() + " due to " + e.getMessage());
              _alertMgr.sendAlert(alertType, vm.getDataCenterId(), vm.getPodIdToDeployIn(), "Unable to restart " + vm.getHostName() + " which was running on host " +
-                 hostDesc, "The Storage is unavailable for trying to restart VM, name: " + vm.getHostName() + ", id: " + vmId + " which was running on host " + hostDesc);
+                 hostDesc, "The resource is unavailable for trying to restart VM, name: " + vm.getHostName() + ", id: " + vmId + " which was running on host " + hostDesc);
          } catch (ConcurrentOperationException e) {
 -            s_logger.warn("Unable to restart " + vm.toString() + " due to " + e.getMessage());
 +            logger.warn("Unable to restart " + vm.toString() + " due to " + e.getMessage());
              _alertMgr.sendAlert(alertType, vm.getDataCenterId(), vm.getPodIdToDeployIn(), "Unable to restart " + vm.getHostName() + " which was running on host " +
                  hostDesc, "The Storage is unavailable for trying to restart VM, name: " + vm.getHostName() + ", id: " + vmId + " which was running on host " + hostDesc);
          } catch (OperationTimedoutException e) {
 -            s_logger.warn("Unable to restart " + vm.toString() + " due to " + e.getMessage());
 +            logger.warn("Unable to restart " + vm.toString() + " due to " + e.getMessage());
              _alertMgr.sendAlert(alertType, vm.getDataCenterId(), vm.getPodIdToDeployIn(), "Unable to restart " + vm.getHostName() + " which was running on host " +
-                 hostDesc, "The Storage is unavailable for trying to restart VM, name: " + vm.getHostName() + ", id: " + vmId + " which was running on host " + hostDesc);
+                     hostDesc, "The operation timed out while trying to restart VM, name: " + vm.getHostName() + ", id: " + vmId + " which was running on host " + hostDesc);
          }
          vm = _itMgr.findById(vm.getId());
          work.setUpdateTime(vm.getUpdated());
diff --cc server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
index 6242289caea,44e3b0c6da2..a5b2a3b75a5
--- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
+++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java
@@@ -42,8 -32,10 +42,9 @@@ import org.apache.cloudstack.vm.Unmanag
  import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.collections.MapUtils;
  import org.apache.commons.lang3.StringUtils;
 -import org.apache.log4j.Logger;
  
  import com.cloud.agent.api.Command;
+ import com.cloud.agent.api.to.DataStoreTO;
  import com.cloud.agent.api.to.DiskTO;
  import com.cloud.agent.api.to.NicTO;
  import com.cloud.agent.api.to.VirtualMachineTO;
@@@ -420,14 -378,26 +421,26 @@@ public abstract class HypervisorGuruBas
      }
  
      @Override
-     public UnmanagedInstanceTO cloneHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params) {
+     public Pair<UnmanagedInstanceTO, Boolean> getHypervisorVMOutOfBandAndCloneIfRequired(String hostIp, String vmName, Map<String, String> params) {
 -        s_logger.error("Unsupported operation: cannot clone external VM");
 +        logger.error("Unsupported operation: cannot clone external VM");
          return null;
      }
  
      @Override
      public boolean removeClonedHypervisorVMOutOfBand(String hostIp, String vmName, Map<String, String> params) {
 -        s_logger.error("Unsupported operation: cannot remove external VM");
 +        logger.error("Unsupported operation: cannot remove external VM");
          return false;
      }
+ 
+     @Override
+     public String createVMTemplateOutOfBand(String hostIp, String vmName, Map<String, String> params, DataStoreTO templateLocation, int threadsCountToExportOvf) {
 -        s_logger.error("Unsupported operation: cannot create template file");
++        logger.error("Unsupported operation: cannot create template file");
+         return null;
+     }
+ 
+     @Override
+     public boolean removeVMTemplateOutOfBand(DataStoreTO templateLocation, String templateDir) {
 -        s_logger.error("Unsupported operation: cannot remove template file");
++        logger.error("Unsupported operation: cannot remove template file");
+         return false;
+     }
  }
diff --cc server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 0a55f75bbbd,83ef8b1b213..dd788aee5d2
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@@ -34,10 -34,8 +34,9 @@@ import java.util.HashMap
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
 +import java.util.Objects;
  import java.util.Set;
  import java.util.UUID;
- import java.util.stream.Collectors;
  
  import javax.inject.Inject;
  import javax.naming.ConfigurationException;
diff --cc server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
index b9f1350615b,702f614d776..52a488fad82
--- a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
@@@ -2437,9 -2440,9 +2438,9 @@@ Configurable, StateListener<VirtualMach
      }
  
      protected void finalizeNetworkRulesForNetwork(final Commands cmds, final DomainRouterVO router, final Provider provider, final Long guestNetworkId) {
 -        s_logger.debug("Resending ipAssoc, port forwarding, load balancing rules as a part of Virtual router start");
 +        logger.debug("Resending ipAssoc, port forwarding, load balancing rules as a part of Virtual router start");
  
-         final ArrayList<? extends PublicIpAddress> publicIps = getPublicIpsToApply(router, provider, guestNetworkId);
+         final ArrayList<? extends PublicIpAddress> publicIps = getPublicIpsToApply(provider, guestNetworkId);
          final List<FirewallRule> firewallRulesEgress = new ArrayList<FirewallRule>();
          final List<FirewallRule> ipv6firewallRules = new ArrayList<>();
  
@@@ -2646,10 -2649,10 +2647,10 @@@
      protected void finalizeIpAssocForNetwork(final Commands cmds, final VirtualRouter router, final Provider provider, final Long guestNetworkId,
              final Map<String, String> vlanMacAddress) {
  
-         final ArrayList<? extends PublicIpAddress> publicIps = getPublicIpsToApply(router, provider, guestNetworkId);
+         final ArrayList<? extends PublicIpAddress> publicIps = getPublicIpsToApply(provider, guestNetworkId);
  
          if (publicIps != null && !publicIps.isEmpty()) {
 -            s_logger.debug("Found " + publicIps.size() + " ip(s) to apply as a part of domR " + router + " start.");
 +            logger.debug("Found " + publicIps.size() + " ip(s) to apply as a part of domR " + router + " start.");
              // Re-apply public ip addresses - should come before PF/LB/VPN
              if (_networkModel.isProviderSupportServiceInNetwork(guestNetworkId, Service.Firewall, provider)) {
                  _commandSetupHelper.createAssociateIPCommands(router, publicIps, cmds, 0);
diff --cc server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java
index 4156c85f880,9ce26bc213c..d6c53504c68
--- a/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java
@@@ -757,10 -747,10 +757,10 @@@ public class VpcVirtualNetworkAppliance
          }
  
          if (domainRouterVO.getState() == State.Starting || domainRouterVO.getState() == State.Running) {
-             final ArrayList<? extends PublicIpAddress> publicIps = getPublicIpsToApply(domainRouterVO, provider, guestNetworkId, IpAddress.State.Releasing);
+             final ArrayList<? extends PublicIpAddress> publicIps = getPublicIpsToApply(provider, guestNetworkId, IpAddress.State.Releasing);
  
              if (publicIps != null && !publicIps.isEmpty()) {
 -                s_logger.debug("Found " + publicIps.size() + " ip(s) to apply as a part of domR " + domainRouterVO + " start.");
 +                logger.debug("Found " + publicIps.size() + " ip(s) to apply as a part of domR " + domainRouterVO + " start.");
                  // Re-apply public ip addresses - should come before PF/LB/VPN
                  _commandSetupHelper.createVpcAssociatePublicIPCommands(domainRouterVO, publicIps, cmds, vlanMacAddress);
              }
diff --cc server/src/main/java/com/cloud/server/StatsCollector.java
index 9d0d95c8b39,308ca11f469..ac9ee06f772
--- a/server/src/main/java/com/cloud/server/StatsCollector.java
+++ b/server/src/main/java/com/cloud/server/StatsCollector.java
@@@ -1962,10 -1960,10 +1958,10 @@@ public class StatsCollector extends Man
                      vmStatsMaxRetentionTime.scope(), vmStatsMaxRetentionTime.toString()));
              return;
          }
 -        LOGGER.trace("Removing older VM stats records.");
 +        logger.trace("Removing older VM stats records.");
          Date now = new Date();
          Date limit = DateUtils.addMinutes(now, -maxRetentionTime);
-         vmStatsDao.removeAllByTimestampLessThan(limit, vmStatsRemoveBatchSize.value());
+         vmStatsDao.removeAllByTimestampLessThan(limit, DELETE_QUERY_BATCH_SIZE.value());
      }
  
      /**
@@@ -1981,10 -1979,10 +1977,10 @@@
              }
              return;
          }
 -        LOGGER.trace("Removing older Volume stats records.");
 +        logger.trace("Removing older Volume stats records.");
          Date now = new Date();
          Date limit = DateUtils.addMinutes(now, -maxRetentionTime);
-         volumeStatsDao.removeAllByTimestampLessThan(limit);
+         volumeStatsDao.removeAllByTimestampLessThan(limit, DELETE_QUERY_BATCH_SIZE.value());
      }
  
      /**
diff --cc server/src/main/java/com/cloud/storage/StorageManagerImpl.java
index a25dadacf44,a5fd7842964..aa5c90e0d8a
--- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
@@@ -408,8 -410,10 +408,10 @@@ public class StorageManagerImpl extend
      int _downloadUrlExpirationInterval;
      private long _serverId;
  
 -    private final Map<String, HypervisorHostListener> hostListeners = new HashMap<String, HypervisorHostListener>();
 +    private final Map<String, HypervisorHostListener> hostListeners = new HashMap<>();
  
+     private static final String NFS_MOUNT_OPTIONS_INCORRECT = "An incorrect mount option was specified";
+ 
      public boolean share(VMInstanceVO vm, List<VolumeVO> vols, HostVO host, boolean cancelPreviousShare) throws StorageUnavailableException {
  
          // if pool is in maintenance and it is the ONLY pool available; reject
@@@ -1082,9 -1137,12 +1133,12 @@@
              throw new IllegalArgumentException("Unable to find storage pool with ID: " + id);
          }
  
+         Map<String, String> inputDetails = extractApiParamAsMap(cmd.getDetails());
+         checkNFSMountOptionsForUpdate(inputDetails, pool, cmd.getEntityOwnerId());
+ 
          String name = cmd.getName();
          if(StringUtils.isNotBlank(name)) {
 -            s_logger.debug("Updating Storage Pool name to: " + name);
 +            logger.debug("Updating Storage Pool name to: " + name);
              pool.setName(name);
              _storagePoolDao.update(pool.getId(), pool);
          }
diff --cc server/src/main/java/com/cloud/storage/StoragePoolAutomationImpl.java
index 6a8e3f0ff51,740e247723a..f1c7c38b8dc
--- a/server/src/main/java/com/cloud/storage/StoragePoolAutomationImpl.java
+++ b/server/src/main/java/com/cloud/storage/StoragePoolAutomationImpl.java
@@@ -28,9 -29,9 +29,10 @@@ import org.apache.cloudstack.engine.sub
  import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
  import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreProviderManager;
  import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
  import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 -import org.apache.log4j.Logger;
 +import org.apache.logging.log4j.Logger;
 +import org.apache.logging.log4j.LogManager;
  import org.springframework.stereotype.Component;
  
  import com.cloud.agent.AgentManager;
@@@ -319,20 -323,31 +324,31 @@@ public class StoragePoolAutomationImpl 
          if (hosts == null || hosts.size() == 0) {
              return true;
          }
+ 
+         Pair<Map<String, String>, Boolean> nfsMountOpts = storageManager.getStoragePoolNFSMountOpts(pool, null);
          // add heartbeat
          for (HostVO host : hosts) {
-             ModifyStoragePoolCommand msPoolCmd = new ModifyStoragePoolCommand(true, pool);
+             ModifyStoragePoolCommand msPoolCmd = new ModifyStoragePoolCommand(true, pool, nfsMountOpts.first());
              final Answer answer = agentMgr.easySend(host.getId(), msPoolCmd);
              if (answer == null || !answer.getResult()) {
 -                if (s_logger.isDebugEnabled()) {
 -                    s_logger.debug("ModifyStoragePool add failed due to " + ((answer == null) ? "answer null" : answer.getDetails()));
 +                if (logger.isDebugEnabled()) {
 +                    logger.debug("ModifyStoragePool add failed due to " + ((answer == null) ? "answer null" : answer.getDetails()));
                  }
+                 if (answer != null && nfsMountOpts.second()) {
 -                    s_logger.error(String.format("Unable to attach storage pool to the host %s due to %s",  host,  answer.getDetails()));
++                    logger.error(String.format("Unable to attach storage pool to the host %s due to %s",  host,  answer.getDetails()));
+                     StringBuilder exceptionSB = new StringBuilder("Unable to attach storage pool to the host ").append(host.getName());
+                     String reason = storageManager.getStoragePoolMountFailureReason(answer.getDetails());
+                     if (reason!= null) {
+                         exceptionSB.append(". ").append(reason).append(".");
+                     }
+                     throw new CloudRuntimeException(exceptionSB.toString());
+                 }
              } else {
 -                if (s_logger.isDebugEnabled()) {
 -                    s_logger.debug("ModifyStoragePool add succeeded");
 +                if (logger.isDebugEnabled()) {
 +                    logger.debug("ModifyStoragePool add succeeded");
                  }
                  if (pool.getPoolType() == Storage.StoragePoolType.DatastoreCluster) {
 -                    s_logger.debug(String.format("Started synchronising datastore cluster storage pool %s with vCenter", pool.getUuid()));
 +                    logger.debug(String.format("Started synchronising datastore cluster storage pool %s with vCenter", pool.getUuid()));
                      storageManager.syncDatastoreClusterStoragePool(pool.getId(), ((ModifyStoragePoolAnswer) answer).getDatastoreClusterChildren(), host.getId());
                  }
              }
diff --cc server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
index 685cf41b146,1176fb31a3e..958b81e8351
--- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
@@@ -3266,9 -3230,17 +3266,17 @@@ public class UserVmManagerImpl extends 
          _itMgr.advanceStart(vm.getUuid(), null, null);
      }
  
+     @Override
+     @ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "restarting VM for HA", async = true)
+     public void startVirtualMachineForHA(VirtualMachine vm, Map<VirtualMachineProfile.Param, Object> params,
+            DeploymentPlanner planner) throws InsufficientCapacityException, ResourceUnavailableException,
+             ConcurrentOperationException, OperationTimedoutException {
+         _itMgr.advanceStart(vm.getUuid(), params, planner);
+     }
+ 
      @Override
      @ActionEvent(eventType = EventTypes.EVENT_VM_REBOOT, eventDescription = "rebooting Vm", async = true)
 -    public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException {
 +    public UserVm rebootVirtualMachine(RebootVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ResourceAllocationException {
          Account caller = CallContext.current().getCallingAccount();
          Long vmId = cmd.getId();
  
@@@ -8672,10 -8460,7 +8680,10 @@@
              UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_STOP, vm.getAccountId(), vm.getDataCenterId(),
                      vm.getId(), vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(),
                      vm.getHypervisorType().toString(), VirtualMachine.class.getName(), vm.getUuid(), vm.isDisplayVm());
++
 +            resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(), offering, template);
 +            resourceNotDecremented = false;
          }
- 
          // VM destroy usage event
          UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VM_DESTROY, vm.getAccountId(), vm.getDataCenterId(),
                  vm.getId(), vm.getHostName(), vm.getServiceOfferingId(), vm.getTemplateId(),
diff --cc server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
index c593b23d7cc,aa216fbfec2..267faf66dc2
--- a/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImpl.java
@@@ -1601,8 -1582,8 +1615,8 @@@ public class UnmanagedVMsManagerImpl im
          if (existingVcenterId != null) {
              VmwareDatacenterVO existingDC = vmwareDatacenterDao.findById(existingVcenterId);
              if (existingDC == null) {
-                 String err = String.format("Cannot find any existing Vmware DC with ID %s", existingVcenterId);
+                 String err = String.format("Cannot find any existing VMware DC with ID %s", existingVcenterId);
 -                LOGGER.error(err);
 +                logger.error(err);
                  throw new CloudRuntimeException(err);
              }
              vcenter = existingDC.getVcenterHost();
@@@ -1611,24 -1592,55 +1625,55 @@@
              password = existingDC.getPassword();
          }
  
-         UnmanagedInstanceTO clonedInstance = null;
+         boolean isClonedInstance = false;
+         UnmanagedInstanceTO sourceVMwareInstance = null;
+         DataStoreTO temporaryConvertLocation = null;
+         String ovfTemplateOnConvertLocation = null;
          try {
+             HostVO convertHost = selectInstanceConversionKVMHostInCluster(destinationCluster, convertInstanceHostId);
+             CheckConvertInstanceAnswer conversionSupportAnswer = checkConversionSupportOnHost(convertHost, sourceVMName, false);
 -            LOGGER.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" +
++            logger.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" +
+                     " from VMware to KVM ", convertHost.getId(), convertHost.getName(), sourceVMName));
+ 
+             temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId);
+             List<StoragePoolVO> convertStoragePools = findInstanceConversionStoragePoolsInCluster(destinationCluster);
+             long importStartTime = System.currentTimeMillis();
+             Pair<UnmanagedInstanceTO, Boolean> sourceInstanceDetails = getSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password, clusterName, sourceHostName, sourceVMName);
+             sourceVMwareInstance = sourceInstanceDetails.first();
+             isClonedInstance = sourceInstanceDetails.second();
+             boolean isWindowsVm = sourceVMwareInstance.getOperatingSystem().toLowerCase().contains("windows");
+             if (isWindowsVm) {
+                 checkConversionSupportOnHost(convertHost, sourceVMName, true);
+             }
+ 
              String instanceName = getGeneratedInstanceName(owner);
-             clonedInstance = cloneSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password,
-                     clusterName, sourceHostName, sourceVM);
-             checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, clonedInstance, nicNetworkMap, nicIpAddressMap, forced);
-             UnmanagedInstanceTO convertedInstance = convertVmwareInstanceToKVM(vcenter, datacenterName, clusterName, username, password,
-                     sourceHostName, clonedInstance, destinationCluster, convertInstanceHostId, convertStoragePoolId);
-             sanitizeConvertedInstance(convertedInstance, clonedInstance);
+             checkNetworkingBeforeConvertingVmwareInstance(zone, owner, instanceName, hostName, sourceVMwareInstance, nicNetworkMap, nicIpAddressMap, forced);
+             UnmanagedInstanceTO convertedInstance;
+             if (cmd.getForceMsToImportVmFiles() || !conversionSupportAnswer.isOvfExportSupported()) {
+                 // Uses MS for OVF export to temporary conversion location
+                 int noOfThreads = UnmanagedVMsManager.ThreadsOnMSToImportVMwareVMFiles.value();
+                 ovfTemplateOnConvertLocation = createOvfTemplateOfSourceVmwareUnmanagedInstance(vcenter, datacenterName, username, password,
+                         clusterName, sourceHostName, sourceVMwareInstance.getName(), temporaryConvertLocation, noOfThreads);
+                 convertedInstance = convertVmwareInstanceToKVMWithOVFOnConvertLocation(sourceVMName, sourceVMwareInstance, convertHost, convertStoragePools,
+                         temporaryConvertLocation, ovfTemplateOnConvertLocation);
+             } else {
+                 // Uses KVM Host for OVF export to temporary conversion location, through ovftool
+                 convertedInstance = convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(sourceVMName, sourceVMwareInstance, convertHost, convertStoragePools,
+                         temporaryConvertLocation, vcenter, username, password, datacenterName);
+             }
+ 
+             sanitizeConvertedInstance(convertedInstance, sourceVMwareInstance);
              UserVm userVm = importVirtualMachineInternal(convertedInstance, instanceName, zone, destinationCluster, null,
                      template, displayName, hostName, caller, owner, userId,
                      serviceOffering, dataDiskOfferingMap,
                      nicNetworkMap, nicIpAddressMap,
                      details, false, forced, false);
-             logger.debug(String.format("VM %s imported successfully", sourceVM));
+             long timeElapsedInSecs = (System.currentTimeMillis() - importStartTime) / 1000;
 -            LOGGER.debug(String.format("VMware VM %s imported successfully to CloudStack instance %s (%s), Time taken: %d secs, OVF files imported from %s, Source VMware VM details - OS: %s, PowerState: %s, Disks: %s, NICs: %s",
++            logger.debug(String.format("VMware VM %s imported successfully to CloudStack instance %s (%s), Time taken: %d secs, OVF files imported from %s, Source VMware VM details - OS: %s, PowerState: %s, Disks: %s, NICs: %s",
+                     sourceVMName, instanceName, displayName, timeElapsedInSecs, (ovfTemplateOnConvertLocation != null)? "MS" : "KVM Host", sourceVMwareInstance.getOperatingSystem(), sourceVMwareInstance.getPowerState(), sourceVMwareInstance.getDisks(), sourceVMwareInstance.getNics()));
              return userVm;
          } catch (CloudRuntimeException e) {
 -            LOGGER.error(String.format("Error importing VM: %s", e.getMessage()), e);
 +            logger.error(String.format("Error importing VM: %s", e.getMessage()), e);
              ActionEventUtils.onCompletedActionEvent(userId, owner.getId(), EventVO.LEVEL_ERROR, EventTypes.EVENT_VM_IMPORT,
                      cmd.getEventDescription(), null, null, 0);
              throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage());
@@@ -1646,8 -1663,8 +1696,8 @@@
          List<Long> networkIds = new ArrayList<>(nicNetworkMap.values());
          if (nics.size() != networkIds.size()) {
              String msg = String.format("Different number of nics found on instance %s: %s vs %s nics provided",
-                     clonedInstance.getName(), nics.size(), networkIds.size());
+                     sourceVMwareInstance.getName(), nics.size(), networkIds.size());
 -            LOGGER.error(msg);
 +            logger.error(msg);
              throw new CloudRuntimeException(msg);
          }
  
@@@ -1674,9 -1691,9 +1724,9 @@@
      private void checkUnmanagedNicAndNetworkMacAddressForImport(NetworkVO network, UnmanagedInstanceTO.Nic nic, boolean forced) {
          NicVO existingNic = nicDao.findByNetworkIdAndMacAddress(network.getId(), nic.getMacAddress());
          if (existingNic != null && !forced) {
-             String err = String.format("NIC with MAC address = %s exists on network with ID = %s and forced flag is disabled",
-                     nic.getMacAddress(), network.getId());
+             String err = String.format("NIC with MAC address %s already exists on network with ID %s and forced flag is disabled. " +
+                     "Retry with forced flag enabled if a new MAC address to be generated.", nic.getMacAddress(), network.getUuid());
 -            LOGGER.error(err);
 +            logger.error(err);
              throw new CloudRuntimeException(err);
          }
      }
@@@ -1728,13 -1751,26 +1784,26 @@@
          if (!result) {
              String msg = String.format("Could not properly remove the cloned instance %s from VMware datacenter %s:%s",
                      clonedInstanceName, vcenter, datacenterName);
 -            LOGGER.warn(msg);
 +            logger.warn(msg);
              return;
          }
-         logger.debug(String.format("Removed the cloned instance %s from VMWare datacenter %s:%s",
 -        LOGGER.debug(String.format("Removed the cloned instance %s from VMWare datacenter %s/%s",
++        logger.debug(String.format("Removed the cloned instance %s from VMWare datacenter %s/%s",
                  clonedInstanceName, vcenter, datacenterName));
      }
  
+     private void removeTemplate(DataStoreTO convertLocation, String ovfTemplateOnConvertLocation) {
+         HypervisorGuru vmwareGuru = hypervisorGuruManager.getGuru(Hypervisor.HypervisorType.VMware);
+         boolean result = vmwareGuru.removeVMTemplateOutOfBand(convertLocation, ovfTemplateOnConvertLocation);
+         if (!result) {
+             String msg = String.format("Could not remove the template file %s on datastore %s",
+                     ovfTemplateOnConvertLocation, convertLocation.getUrl());
 -            LOGGER.warn(msg);
++            logger.warn(msg);
+             return;
+         }
 -        LOGGER.debug(String.format("Removed the template file %s on datastore %s",
++        logger.debug(String.format("Removed the template file %s on datastore %s",
+                 ovfTemplateOnConvertLocation, convertLocation.getUrl()));
+     }
+ 
      private Map<String, String> createParamsForRemoveClonedInstance(String vcenter, String datacenterName, String username,
                                                                      String password, String sourceVM) {
          Map<String, String> params = new HashMap<>();
@@@ -1762,41 -1798,62 +1831,61 @@@
              }
              return selectedHost;
          }
-         List<HostVO> hosts = hostDao.listByClusterAndHypervisorType(destinationCluster.getId(), destinationCluster.getHypervisorType());
-         if (CollectionUtils.isEmpty(hosts)) {
-             String err = String.format("Could not find any running %s host in cluster %s",
-                     destinationCluster.getHypervisorType(), destinationCluster.getName());
 -
+         // Auto select host with conversion capability
+         List<HostVO> hosts = hostDao.listByClusterHypervisorTypeAndHostCapability(destinationCluster.getId(), destinationCluster.getHypervisorType(), Host.HOST_INSTANCE_CONVERSION);
+         if (CollectionUtils.isNotEmpty(hosts)) {
+             return hosts.get(new Random().nextInt(hosts.size()));
+         }
+ 
+         // Try without host capability check
+         hosts = hostDao.listByClusterAndHypervisorType(destinationCluster.getId(), destinationCluster.getHypervisorType());
+         if (CollectionUtils.isNotEmpty(hosts)) {
+             return hosts.get(new Random().nextInt(hosts.size()));
+         }
+ 
+         String err = String.format("Could not find any suitable %s host in cluster %s to perform the instance conversion",
+                 destinationCluster.getHypervisorType(), destinationCluster.getName());
 -        LOGGER.error(err);
++        logger.error(err);
+         throw new CloudRuntimeException(err);
+     }
+ 
+     private CheckConvertInstanceAnswer checkConversionSupportOnHost(HostVO convertHost, String sourceVM, boolean checkWindowsGuestConversionSupport) {
 -        LOGGER.debug(String.format("Checking the %s conversion support on the host %s (%s)", checkWindowsGuestConversionSupport? "windows guest" : "", convertHost.getId(), convertHost.getName()));
++        logger.debug(String.format("Checking the %s conversion support on the host %s (%s)", checkWindowsGuestConversionSupport? "windows guest" : "", convertHost.getId(), convertHost.getName()));
+         CheckConvertInstanceCommand cmd = new CheckConvertInstanceCommand(checkWindowsGuestConversionSupport);
+         int timeoutSeconds = 60;
+         cmd.setWait(timeoutSeconds);
+ 
+         CheckConvertInstanceAnswer checkConvertInstanceAnswer;
+         try {
+             checkConvertInstanceAnswer = (CheckConvertInstanceAnswer) agentManager.send(convertHost.getId(), cmd);
+         } catch (AgentUnavailableException | OperationTimedoutException e) {
+             String err = String.format("Failed to check %s conversion support on the host %s for converting instance %s from VMware to KVM due to: %s",
+                     checkWindowsGuestConversionSupport? "windows guest" : "", convertHost.getName(), sourceVM, e.getMessage());
 -            LOGGER.error(err);
 +            logger.error(err);
              throw new CloudRuntimeException(err);
          }
-         List<HostVO> filteredHosts = hosts.stream()
-                 .filter(x -> x.getResourceState() == ResourceState.Enabled)
-                 .collect(Collectors.toList());
-         if (CollectionUtils.isEmpty(filteredHosts)) {
-             String err = String.format("Could not find a %s host in cluster %s to perform the instance conversion",
-                     destinationCluster.getHypervisorType(), destinationCluster.getName());
+ 
+         if (!checkConvertInstanceAnswer.getResult()) {
+             String err = String.format("The host %s doesn't support conversion of instance %s from VMware to KVM due to: %s",
+                     convertHost.getName(), sourceVM, checkConvertInstanceAnswer.getDetails());
 -            LOGGER.error(err);
 +            logger.error(err);
              throw new CloudRuntimeException(err);
          }
-         return filteredHosts.get(new Random().nextInt(filteredHosts.size()));
+ 
+         return checkConvertInstanceAnswer;
      }
  
-     private UnmanagedInstanceTO convertVmwareInstanceToKVM(String vcenter, String datacenterName, String clusterName,
-                                                            String username, String password, String hostName,
-                                                            UnmanagedInstanceTO clonedInstance, Cluster destinationCluster,
-                                                            Long convertInstanceHostId, Long convertStoragePoolId) {
-         HostVO convertHost = selectInstanceConvertionKVMHostInCluster(destinationCluster, convertInstanceHostId);
-         String vmName = clonedInstance.getName();
-         logger.debug(String.format("The host %s (%s) is selected to execute the conversion of the instance %s" +
-                 " from VMware to KVM ", convertHost.getId(), convertHost.getName(), vmName));
+     private UnmanagedInstanceTO convertVmwareInstanceToKVMWithOVFOnConvertLocation(String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost,
+                                                                                    List<StoragePoolVO> convertStoragePools, DataStoreTO temporaryConvertLocation,
+                                                                                    String ovfTemplateDirConvertLocation) {
 -        LOGGER.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) using OVF %s on conversion datastore",
++        logger.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) using OVF %s on conversion datastore",
+                 sourceVM, convertHost.getId(), convertHost.getName(), ovfTemplateDirConvertLocation));
  
-         RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(hostName, vmName,
-                 vcenter, datacenterName, clusterName, username, password);
-         DataStoreTO temporaryConvertLocation = selectInstanceConversionTemporaryLocation(destinationCluster, convertStoragePoolId, convertHost);
-         List<String> destinationStoragePools = selectInstanceConvertionStoragePools(destinationCluster, clonedInstance.getDisks());
+         RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVM);
+         List<String> destinationStoragePools = selectInstanceConversionStoragePools(convertStoragePools, sourceVMwareInstance.getDisks());
          ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO,
-                 Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation);
-         int timeoutSeconds = StorageManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60;
+                 Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation, ovfTemplateDirConvertLocation, false, false);
+         int timeoutSeconds = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60;
          cmd.setWait(timeoutSeconds);
  
          Answer convertAnswer;
@@@ -1810,17 -1867,68 +1899,68 @@@
          }
  
          if (!convertAnswer.getResult()) {
-             String err = String.format("The convert process failed for instance %s from Vmware to KVM on host %s: %s",
-                     vmName, convertHost.getName(), convertAnswer.getDetails());
+             String err = String.format("The convert process failed for instance %s from VMware to KVM on host %s: %s",
+                     sourceVM, convertHost.getName(), convertAnswer.getDetails());
 -            LOGGER.error(err);
++            logger.error(err);
+             throw new CloudRuntimeException(err);
+         }
+         return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance();
+     }
+ 
+     private UnmanagedInstanceTO convertVmwareInstanceToKVMAfterExportingOVFToConvertLocation(String sourceVM, UnmanagedInstanceTO sourceVMwareInstance, HostVO convertHost,
+                                                                                              List<StoragePoolVO> convertStoragePools, DataStoreTO temporaryConvertLocation,
+                                                                                              String vcenterHost, String vcenterUsername, String vcenterPassword, String datacenterName) {
 -        LOGGER.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) after OVF export through ovftool",
++        logger.debug(String.format("Delegating the conversion of instance %s from VMware to KVM to the host %s (%s) after OVF export through ovftool",
+                 sourceVM, convertHost.getId(), convertHost.getName()));
+ 
+         RemoteInstanceTO remoteInstanceTO = new RemoteInstanceTO(sourceVMwareInstance.getName(), vcenterHost, vcenterUsername, vcenterPassword, datacenterName);
+         List<String> destinationStoragePools = selectInstanceConversionStoragePools(convertStoragePools, sourceVMwareInstance.getDisks());
+         ConvertInstanceCommand cmd = new ConvertInstanceCommand(remoteInstanceTO,
+                 Hypervisor.HypervisorType.KVM, destinationStoragePools, temporaryConvertLocation, null, false, true);
+         int timeoutSeconds = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.value() * 60 * 60;
+         cmd.setWait(timeoutSeconds);
+         int noOfThreads = UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles.value();
+         if (noOfThreads == 0) {
+             // Use no. of threads as the disks count
+             noOfThreads = sourceVMwareInstance.getDisks().size();
+         }
+         cmd.setThreadsCountToExportOvf(noOfThreads);
+ 
+         Answer convertAnswer;
+         try {
+             convertAnswer = agentManager.send(convertHost.getId(), cmd);
+         } catch (AgentUnavailableException | OperationTimedoutException e) {
+             String err = String.format("Could not send the convert instance command to host %s (%s) due to: %s",
+                     convertHost.getId(), convertHost.getName(), e.getMessage());
 -            LOGGER.error(err, e);
++            logger.error(err, e);
+             throw new CloudRuntimeException(err);
+         }
+ 
+         if (!convertAnswer.getResult()) {
+             String err = String.format("The convert process failed for instance %s from VMware to KVM on host %s: %s",
+                     sourceVM, convertHost.getName(), convertAnswer.getDetails());
 -            LOGGER.error(err);
 +            logger.error(err);
              throw new CloudRuntimeException(err);
          }
          return ((ConvertInstanceAnswer) convertAnswer).getConvertedInstance();
      }
  
-     private List<String> selectInstanceConvertionStoragePools(Cluster destinationCluster, List<UnmanagedInstanceTO.Disk> disks) {
+     private List<StoragePoolVO> findInstanceConversionStoragePoolsInCluster(Cluster destinationCluster) {
+         List<StoragePoolVO> pools = new ArrayList<>();
+         List<StoragePoolVO> clusterPools = primaryDataStoreDao.findClusterWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem);
+         pools.addAll(clusterPools);
+         List<StoragePoolVO> zonePools = primaryDataStoreDao.findZoneWideStoragePoolsByHypervisorAndPoolType(destinationCluster.getDataCenterId(), Hypervisor.HypervisorType.KVM, Storage.StoragePoolType.NetworkFilesystem);
+         pools.addAll(zonePools);
+         if (pools.isEmpty()) {
+             String msg = String.format("Cannot find suitable storage pools in cluster %s for the conversion", destinationCluster.getName());
 -            LOGGER.error(msg);
++            logger.error(msg);
+             throw new CloudRuntimeException(msg);
+         }
+         return pools;
+     }
+ 
+     private List<String> selectInstanceConversionStoragePools(List<StoragePoolVO> pools, List<UnmanagedInstanceTO.Disk> disks) {
          List<String> storagePools = new ArrayList<>(disks.size());
-         List<StoragePoolVO> pools = primaryDataStoreDao.listPoolsByCluster(destinationCluster.getId());
          //TODO: Choose pools by capacity
          for (UnmanagedInstanceTO.Disk disk : disks) {
              Long capacity = disk.getCapacity();
diff --cc server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java
index f7606a9a962,8f4dbbe675d..a4ffa04a411
--- a/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java
+++ b/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java
@@@ -16,82 -16,43 +16,84 @@@
  // under the License.
  package com.cloud.configuration;
  
 -import static org.mockito.Mockito.mock;
 -import static org.mockito.Mockito.when;
 -
 -import com.cloud.exception.InvalidParameterValueException;
 -import com.cloud.storage.StorageManager;
 -import com.cloud.utils.net.NetUtils;
 -import org.apache.cloudstack.framework.config.ConfigDepot;
 -import org.apache.cloudstack.framework.config.ConfigKey;
 +import com.cloud.capacity.dao.CapacityDao;
 +import com.cloud.dc.DataCenterVO;
 +import com.cloud.dc.VlanVO;
  import com.cloud.dc.dao.DataCenterDao;
 +import com.cloud.dc.dao.DataCenterIpAddressDao;
 +import com.cloud.dc.dao.DedicatedResourceDao;
 +import com.cloud.dc.dao.HostPodDao;
 +import com.cloud.dc.dao.VlanDao;
+ import com.cloud.domain.Domain;
+ import com.cloud.domain.dao.DomainDao;
 +import com.cloud.exception.InvalidParameterValueException;
 +import com.cloud.host.dao.HostDao;
 +import com.cloud.network.Network;
 +import com.cloud.network.NetworkModel;
 +import com.cloud.network.NetworkService;
 +import com.cloud.network.Networks;
 +import com.cloud.network.dao.IPAddressDao;
 +import com.cloud.network.dao.NsxProviderDao;
 +import com.cloud.network.dao.PhysicalNetworkDao;
 +import com.cloud.network.element.NsxProviderVO;
+ import com.cloud.offering.DiskOffering;
 +import com.cloud.offering.NetworkOffering;
 +import com.cloud.offerings.NetworkOfferingVO;
 +import com.cloud.offerings.dao.NetworkOfferingDao;
+ import com.cloud.storage.DiskOfferingVO;
 +import com.cloud.storage.StorageManager;
 +import com.cloud.storage.dao.VMTemplateZoneDao;
 +import com.cloud.storage.dao.VolumeDao;
+ import com.cloud.user.Account;
+ import com.cloud.user.User;
+ import com.cloud.utils.db.EntityManager;
+ import com.cloud.utils.db.SearchCriteria;
 +import com.cloud.utils.net.NetUtils;
 +import com.cloud.vm.dao.VMInstanceDao;
 +import org.apache.cloudstack.annotation.dao.AnnotationDao;
 +import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd;
+ import org.apache.cloudstack.api.command.admin.offering.UpdateDiskOfferingCmd;
 +import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd;
 +import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope;
 +import org.apache.cloudstack.framework.config.ConfigDepot;
 +import org.apache.cloudstack.framework.config.ConfigKey;
  import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
- import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
- import com.cloud.domain.Domain;
- import com.cloud.domain.dao.DomainDao;
- import com.cloud.offering.DiskOffering;
- import com.cloud.storage.DiskOfferingVO;
- import com.cloud.user.Account;
- import com.cloud.user.User;
- import com.cloud.utils.db.EntityManager;
- import com.cloud.utils.db.SearchCriteria;
- import org.apache.cloudstack.api.command.admin.offering.UpdateDiskOfferingCmd;
+ import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
  import org.apache.cloudstack.resourcedetail.DiskOfferingDetailVO;
  import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
++import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
+ import org.apache.cloudstack.vm.UnmanagedVMsManager;
  import org.junit.Assert;
  import org.junit.Before;
  import org.junit.Test;
  import org.junit.runner.RunWith;
++import org.mockito.InjectMocks;
  import org.mockito.Mock;
  import org.mockito.MockedStatic;
  import org.mockito.Mockito;
 -import org.mockito.junit.MockitoJUnitRunner;
 -import org.mockito.InjectMocks;
+ import org.mockito.Spy;
 +import org.mockito.junit.MockitoJUnitRunner;
 +import org.springframework.test.util.ReflectionTestUtils;
  
- import java.util.Collections;
- import org.mockito.InjectMocks;
- import org.mockito.Spy;
- 
  import java.util.ArrayList;
++import java.util.Collections;
  import java.util.List;
  
 +import static org.mockito.ArgumentMatchers.any;
 +import static org.mockito.ArgumentMatchers.anyLong;
- import static org.mockito.Mockito.when;
- import static org.mockito.Mockito.lenient;
- import static org.mockito.Mockito.anyString;
 +import static org.mockito.Mockito.anyBoolean;
++import static org.mockito.Mockito.anyInt;
++import static org.mockito.Mockito.anyList;
++import static org.mockito.Mockito.anyMap;
++import static org.mockito.Mockito.anyString;
 +import static org.mockito.Mockito.doNothing;
++import static org.mockito.Mockito.lenient;
++import static org.mockito.Mockito.mock;
 +import static org.mockito.Mockito.nullable;
- import static org.mockito.Mockito.anyMap;
- import static org.mockito.Mockito.anyList;
- import static org.mockito.Mockito.anyInt;
- import static org.mockito.Mockito.verify;
 +import static org.mockito.Mockito.times;
++import static org.mockito.Mockito.verify;
++import static org.mockito.Mockito.when;
 +
  @RunWith(MockitoJUnitRunner.class)
  public class ConfigurationManagerImplTest {
      @Mock
@@@ -109,6 -70,10 +111,8 @@@
      @Mock
      Domain domainMock;
      @Mock
 -    ConfigurationDao configDaoMock;
 -    @Mock
+     DataCenterDao zoneDaoMock;
+     @Mock
      DomainDao domainDaoMock;
      @Mock
      EntityManager entityManagerMock;
@@@ -407,56 -308,65 +411,115 @@@
      }
  
      @Test
 +    public void testDeleteZoneInvokesDeleteNsxProviderWhenNSXIsEnabled() {
 +        NsxProviderVO nsxProviderVO = Mockito.mock(NsxProviderVO.class);
 +        DataCenterVO dataCenterVO = Mockito.mock(DataCenterVO.class);
 +
 +        when(nsxProviderDao.findByZoneId(anyLong())).thenReturn(nsxProviderVO);
 +        when(zoneDao.findById(anyLong())).thenReturn(dataCenterVO);
 +        lenient().when(hostDao.findByDataCenterId(anyLong())).thenReturn(Collections.emptyList());
 +        when(podDao.listByDataCenterId(anyLong())).thenReturn(Collections.emptyList());
 +        when(ipAddressDao.countIPs(anyLong(), anyBoolean())).thenReturn(0);
 +        when(publicIpAddressDao.countIPs(anyLong(), anyBoolean())).thenReturn(0);
 +        when(vmInstanceDao.listByZoneId(anyLong())).thenReturn(Collections.emptyList());
 +        when(volumeDao.findByDc(anyLong())).thenReturn(Collections.emptyList());
 +        when(physicalNetworkDao.listByZone(anyLong())).thenReturn(Collections.emptyList());
 +        when(imageStoreDao.findByZone(any(ZoneScope.class), nullable(Boolean.class))).thenReturn(Collections.emptyList());
 +        when(vlanDao.listByZone(anyLong())).thenReturn(List.of(Mockito.mock(VlanVO.class)));
 +        when(nsxProviderVO.getId()).thenReturn(1L);
 +        when(zoneDao.remove(anyLong())).thenReturn(true);
 +        when(capacityDao.removeBy(nullable(Short.class), anyLong(), nullable(Long.class), nullable(Long.class), nullable(Long.class))).thenReturn(true);
 +        when(dedicatedResourceDao.findByZoneId(anyLong())).thenReturn(null);
 +        lenient().when(annotationDao.removeByEntityType(anyString(), anyString())).thenReturn(true);
 +
 +        configurationManagerImplSpy.deleteZone(deleteZoneCmd);
 +
 +        verify(nsxProviderDao, times(1)).remove(anyLong());
 +    }
 +
 +    @Test
 +    public void testCreateNetworkOfferingForNsx() {
 +        NetworkOfferingVO offeringVO = Mockito.mock(NetworkOfferingVO.class);
 +
 +        when(createNetworkOfferingCmd.isForNsx()).thenReturn(true);
 +        when(createNetworkOfferingCmd.getNsxMode()).thenReturn(NetworkOffering.NsxMode.NATTED.name());
 +        when(createNetworkOfferingCmd.getTraffictype()).thenReturn(Networks.TrafficType.Guest.name());
 +        when(createNetworkOfferingCmd.getGuestIpType()).thenReturn(Network.GuestType.Isolated.name());
 +        when(createNetworkOfferingCmd.getAvailability()).thenReturn(NetworkOffering.Availability.Optional.name());
 +        lenient().when(configurationManagerImplSpy.createNetworkOffering(anyString(), anyString(), any(Networks.TrafficType.class), anyString(),
 +                        anyBoolean(), any(NetworkOffering.Availability.class), anyInt(), anyMap(), anyBoolean(), any(Network.GuestType.class),
 +                        anyBoolean(), anyLong(), anyBoolean(), anyMap(), anyBoolean(), anyBoolean(), anyMap(), anyBoolean(), anyInt(),
 +                        anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyString(), anyList(), anyList(), anyBoolean(), any(NetUtils.InternetProtocol.class)))
 +                .thenReturn(offeringVO);
 +        when(configDao.getValue(anyString())).thenReturn("1000");
 +        lenient().when(networkOfferingDao.persist(any(NetworkOfferingVO.class), anyMap())).thenReturn(offeringVO);
 +        doNothing().when(networkService).validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(anyLong());
 +        doNothing().when(networkModel).canProviderSupportServices(anyMap());
 +
 +        NetworkOffering offering = configurationManagerImplSpy.createNetworkOffering(createNetworkOfferingCmd);
 +
 +        Assert.assertNotNull(offering);
 +    }
 +
+     public void testValidateInvalidConfiguration() {
 -        Mockito.doReturn(null).when(configDaoMock).findByName(Mockito.anyString());
++        Mockito.doReturn(null).when(configDao).findByName(Mockito.anyString());
+         String msg = configurationManagerImplSpy.validateConfigurationValue("test.config.name", "testvalue", ConfigKey.Scope.Global.toString());
+         Assert.assertEquals("Invalid configuration variable.", msg);
+     }
+ 
+     @Test
+     public void testValidateInvalidScopeForConfiguration() {
+         ConfigurationVO cfg = mock(ConfigurationVO.class);
+         when(cfg.getScope()).thenReturn(ConfigKey.Scope.Account.toString());
 -        Mockito.doReturn(cfg).when(configDaoMock).findByName(Mockito.anyString());
++        Mockito.doReturn(cfg).when(configDao).findByName(Mockito.anyString());
+         String msg = configurationManagerImplSpy.validateConfigurationValue("test.config.name", "testvalue", ConfigKey.Scope.Domain.toString());
+         Assert.assertEquals("Invalid scope id provided for the parameter test.config.name", msg);
+     }
+ 
+     @Test(expected = InvalidParameterValueException.class)
+     public void testValidateConfig_ThreadsOnKVMHostToTransferVMwareVMFiles_Failure() {
+         ConfigurationVO cfg = mock(ConfigurationVO.class);
+         when(cfg.getScope()).thenReturn(ConfigKey.Scope.Global.toString());
+         ConfigKey<Integer> configKey = UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles;
 -        Mockito.doReturn(cfg).when(configDaoMock).findByName(Mockito.anyString());
++        Mockito.doReturn(cfg).when(configDao).findByName(Mockito.anyString());
+         Mockito.doReturn(configKey).when(configurationManagerImplSpy._configDepot).get(configKey.key());
+         configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "11", configKey.scope().toString());
+     }
+ 
+     @Test
+     public void testValidateConfig_ThreadsOnKVMHostToTransferVMwareVMFiles_Success() {
+         ConfigurationVO cfg = mock(ConfigurationVO.class);
+         when(cfg.getScope()).thenReturn(ConfigKey.Scope.Global.toString());
+         ConfigKey<Integer> configKey = UnmanagedVMsManager.ThreadsOnKVMHostToImportVMwareVMFiles;
 -        Mockito.doReturn(cfg).when(configDaoMock).findByName(Mockito.anyString());
++        Mockito.doReturn(cfg).when(configDao).findByName(Mockito.anyString());
+         Mockito.doReturn(configKey).when(configurationManagerImplSpy._configDepot).get(configKey.key());
+         String msg = configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "10", configKey.scope().toString());
+         Assert.assertNull(msg);
+     }
+ 
+     @Test(expected = InvalidParameterValueException.class)
+     public void testValidateConfig_ConvertVmwareInstanceToKvmTimeout_Failure() {
+         ConfigurationVO cfg = mock(ConfigurationVO.class);
+         when(cfg.getScope()).thenReturn(ConfigKey.Scope.Global.toString());
+         ConfigKey<Integer> configKey = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout;
 -        Mockito.doReturn(cfg).when(configDaoMock).findByName(Mockito.anyString());
++        Mockito.doReturn(cfg).when(configDao).findByName(Mockito.anyString());
+         Mockito.doReturn(configKey).when(configurationManagerImplSpy._configDepot).get(configKey.key());
+         configurationManagerImplSpy.populateConfigValuesForValidationSet();
+         configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "0", configKey.scope().toString());
+     }
+ 
+     @Test
+     public void testValidateConfig_ConvertVmwareInstanceToKvmTimeout_Success() {
+         ConfigurationVO cfg = mock(ConfigurationVO.class);
+         when(cfg.getScope()).thenReturn(ConfigKey.Scope.Global.toString());
+         ConfigKey<Integer> configKey = UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout;
 -        Mockito.doReturn(cfg).when(configDaoMock).findByName(Mockito.anyString());
++        Mockito.doReturn(cfg).when(configDao).findByName(Mockito.anyString());
+         Mockito.doReturn(configKey).when(configurationManagerImplSpy._configDepot).get(configKey.key());
+         configurationManagerImplSpy.populateConfigValuesForValidationSet();
+         String msg = configurationManagerImplSpy.validateConfigurationValue(configKey.key(), "9", configKey.scope().toString());
+         Assert.assertNull(msg);
+     }
+ 
      @Test
      public void validateDomainTestInvalidIdThrowException() {
          Mockito.doReturn(null).when(domainDaoMock).findById(invalidId);
diff --cc server/src/test/java/com/cloud/storage/StorageManagerImplTest.java
index 2910476c18e,2596f9facba..309282282f6
--- a/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java
+++ b/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java
@@@ -16,18 -16,26 +16,33 @@@
  // under the License.
  package com.cloud.storage;
  
 +import java.util.ArrayList;
 +import java.util.Arrays;
 +import java.util.HashMap;
 +import java.util.List;
 +import java.util.Map;
+ import com.cloud.agent.api.StoragePoolInfo;
+ import com.cloud.dc.DataCenterVO;
+ import com.cloud.dc.dao.DataCenterDao;
+ import com.cloud.exception.ConnectionException;
+ import com.cloud.exception.InvalidParameterValueException;
+ import com.cloud.exception.PermissionDeniedException;
+ import com.cloud.host.Host;
+ import com.cloud.hypervisor.Hypervisor;
+ import com.cloud.storage.dao.VolumeDao;
+ import com.cloud.user.AccountManager;
+ import com.cloud.utils.Pair;
+ import com.cloud.vm.VMInstanceVO;
+ import com.cloud.vm.dao.VMInstanceDao;
  
  import org.apache.cloudstack.api.ApiConstants;
  import org.apache.cloudstack.framework.config.ConfigDepot;
  import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
 +import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand;
  import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
+ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailVO;
+ import org.apache.cloudstack.storage.datastore.db.StoragePoolDetailsDao;
  import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
  import org.apache.commons.collections.MapUtils;
  import org.junit.Assert;
@@@ -38,28 -46,11 +53,18 @@@ import org.mockito.Mock
  import org.mockito.Mockito;
  import org.mockito.Spy;
  import org.mockito.junit.MockitoJUnitRunner;
 +import org.mockito.stubbing.Answer;
  
 -import java.util.ArrayList;
 -import java.util.HashMap;
 -import java.util.List;
 -import java.util.Map;
 +import com.cloud.agent.AgentManager;
 +import com.cloud.agent.api.Command;
- import com.cloud.agent.api.StoragePoolInfo;
 +import com.cloud.capacity.CapacityManager;
- import com.cloud.dc.DataCenterVO;
 +import com.cloud.dc.VsphereStoragePolicyVO;
- import com.cloud.dc.dao.DataCenterDao;
 +import com.cloud.dc.dao.VsphereStoragePolicyDao;
 +import com.cloud.exception.AgentUnavailableException;
- import com.cloud.exception.ConnectionException;
- import com.cloud.exception.InvalidParameterValueException;
 +import com.cloud.exception.OperationTimedoutException;
 +import com.cloud.exception.StorageUnavailableException;
- import com.cloud.host.Host;
 +import com.cloud.hypervisor.HypervisorGuruManager;
- import com.cloud.storage.dao.VolumeDao;
- import com.cloud.utils.Pair;
 +import com.cloud.vm.DiskProfile;
- import com.cloud.vm.VMInstanceVO;
- import com.cloud.vm.dao.VMInstanceDao;
  
  @RunWith(MockitoJUnitRunner.class)
  public class StorageManagerImplTest {
diff --cc server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
index 81358d99ae7,9e96d74ea4b..f3ba600817f
--- a/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
+++ b/server/src/test/java/org/apache/cloudstack/vm/UnmanagedVMsManagerImplTest.java
@@@ -17,59 -17,10 +17,62 @@@
  
  package org.apache.cloudstack.vm;
  
 +import static org.mockito.ArgumentMatchers.any;
 +import static org.mockito.ArgumentMatchers.anyBoolean;
++import static org.mockito.ArgumentMatchers.anyInt;
 +import static org.mockito.ArgumentMatchers.anyLong;
 +import static org.mockito.ArgumentMatchers.anyMap;
 +import static org.mockito.ArgumentMatchers.anyString;
 +import static org.mockito.ArgumentMatchers.nullable;
 +import static org.mockito.Mockito.doNothing;
 +import static org.mockito.Mockito.mock;
 +import static org.mockito.Mockito.verify;
 +import static org.mockito.Mockito.when;
 +
 +import java.net.URI;
 +import java.util.ArrayList;
 +import java.util.HashMap;
 +import java.util.LinkedHashMap;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.UUID;
 +
 +import org.apache.cloudstack.api.ResponseGenerator;
 +import org.apache.cloudstack.api.ResponseObject;
 +import org.apache.cloudstack.api.ServerApiException;
 +import org.apache.cloudstack.api.command.admin.vm.ImportUnmanagedInstanceCmd;
 +import org.apache.cloudstack.api.command.admin.vm.ImportVmCmd;
 +import org.apache.cloudstack.api.command.admin.vm.ListUnmanagedInstancesCmd;
 +import org.apache.cloudstack.api.command.admin.vm.ListVmsForImportCmd;
 +import org.apache.cloudstack.api.response.ListResponse;
 +import org.apache.cloudstack.api.response.UserVmResponse;
 +import org.apache.cloudstack.context.CallContext;
 +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
 +import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
 +import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
 +import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
 +import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
 +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao;
 +import org.apache.cloudstack.storage.datastore.db.ImageStoreVO;
 +import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
 +import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
 +import org.junit.After;
 +import org.junit.Assert;
 +import org.junit.Before;
 +import org.junit.Test;
 +import org.junit.runner.RunWith;
 +import org.mockito.BDDMockito;
 +import org.mockito.InjectMocks;
 +import org.mockito.Mock;
 +import org.mockito.MockedStatic;
 +import org.mockito.Mockito;
 +import org.mockito.MockitoAnnotations;
 +import org.mockito.junit.MockitoJUnitRunner;
 +
  import com.cloud.agent.AgentManager;
  import com.cloud.agent.api.Answer;
+ import com.cloud.agent.api.CheckConvertInstanceAnswer;
+ import com.cloud.agent.api.CheckConvertInstanceCommand;
  import com.cloud.agent.api.CheckVolumeAnswer;
  import com.cloud.agent.api.CheckVolumeCommand;
  import com.cloud.agent.api.ConvertInstanceAnswer;
@@@ -841,42 -853,6 +854,42 @@@ public class UnmanagedVMsManagerImplTes
      public void testSelectInstanceConversionTemporaryLocationNoPoolAvailable() {
          ClusterVO cluster = getClusterForTests();
          Mockito.when(imageStoreDao.findOneByZoneAndProtocol(anyLong(), anyString())).thenReturn(null);
-         unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null, null);
+         unmanagedVMsManager.selectInstanceConversionTemporaryLocation(cluster, null);
      }
 +
 +    @Test
 +    public void testCheckUnmanagedDiskLimits() {
 +        Account owner = Mockito.mock(Account.class);
 +        UnmanagedInstanceTO.Disk disk = Mockito.mock(UnmanagedInstanceTO.Disk.class);
 +        Mockito.when(disk.getDiskId()).thenReturn("disk1");
 +        Mockito.when(disk.getCapacity()).thenReturn(100L);
 +        ServiceOffering serviceOffering = Mockito.mock(ServiceOffering.class);
 +        Mockito.when(serviceOffering.getDiskOfferingId()).thenReturn(1L);
 +        UnmanagedInstanceTO.Disk dataDisk = Mockito.mock(UnmanagedInstanceTO.Disk.class);
 +        Mockito.when(dataDisk.getDiskId()).thenReturn("disk2");
 +        Mockito.when(dataDisk.getCapacity()).thenReturn(1000L);
 +        Map<String, Long> dataDiskMap = new HashMap<>();
 +        dataDiskMap.put("disk2", 2L);
 +        DiskOfferingVO offering1 = Mockito.mock(DiskOfferingVO.class);
 +        Mockito.when(diskOfferingDao.findById(1L)).thenReturn(offering1);
 +        String tag1 = "tag1";
 +        Mockito.when(resourceLimitService.getResourceLimitStorageTags(offering1)).thenReturn(List.of(tag1));
 +        DiskOfferingVO offering2 = Mockito.mock(DiskOfferingVO.class);
 +        Mockito.when(diskOfferingDao.findById(2L)).thenReturn(offering2);
 +        String tag2 = "tag2";
 +        Mockito.when(resourceLimitService.getResourceLimitStorageTags(offering2)).thenReturn(List.of(tag2));
 +        try {
 +            Mockito.doNothing().when(resourceLimitService).checkResourceLimit(any(), any(), any());
 +            Mockito.doNothing().when(resourceLimitService).checkResourceLimitWithTag(any(), any(), any(), any());
 +            unmanagedVMsManager.checkUnmanagedDiskLimits(owner, disk, serviceOffering, List.of(dataDisk), dataDiskMap);
 +            Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimit(owner, Resource.ResourceType.volume, 2);
 +            Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimit(owner, Resource.ResourceType.primary_storage, 1100L);
 +            Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.volume, tag1,1);
 +            Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.volume, tag2,1);
 +            Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.primary_storage, tag1,100L);
 +            Mockito.verify(resourceLimitService, Mockito.times(1)).checkResourceLimitWithTag(owner, Resource.ResourceType.primary_storage, tag2,1000L);
 +        } catch (ResourceAllocationException e) {
 +            Assert.fail("Exception encountered: " + e.getMessage());
 +        }
 +    }
  }
diff --cc services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVncClient.java
index e89984b1749,05063e0f861..cf9e3cfddcc
--- a/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVncClient.java
+++ b/services/console-proxy/server/src/main/java/com/cloud/consoleproxy/ConsoleProxyNoVncClient.java
@@@ -16,6 -16,13 +16,12 @@@
  // under the License.
  package com.cloud.consoleproxy;
  
+ import com.cloud.utils.net.NetUtils;
+ import org.apache.commons.lang3.StringUtils;
 -import org.apache.log4j.Logger;
+ import org.eclipse.jetty.websocket.api.Session;
+ import org.eclipse.jetty.websocket.api.WebSocketException;
+ import org.eclipse.jetty.websocket.api.extensions.Frame;
+ 
  import java.awt.Image;
  import java.io.IOException;
  import java.net.URI;
@@@ -23,13 -30,6 +29,9 @@@ import java.nio.ByteBuffer
  import java.nio.charset.StandardCharsets;
  import java.util.List;
  
- import org.apache.commons.lang3.StringUtils;
 +import org.apache.logging.log4j.LogManager;
 +import org.apache.logging.log4j.Logger;
- import org.eclipse.jetty.websocket.api.Session;
- import org.eclipse.jetty.websocket.api.WebSocketException;
- import org.eclipse.jetty.websocket.api.extensions.Frame;
 +
  import com.cloud.consoleproxy.vnc.NoVncClient;
  
  public class ConsoleProxyNoVncClient implements ConsoleProxyClient {
@@@ -284,7 -284,8 +286,8 @@@
      private void connectClientToVNCServer(String tunnelUrl, String tunnelSession, String websocketUrl) {
          try {
              if (StringUtils.isNotBlank(websocketUrl)) {
 -                s_logger.info(String.format("Connect to VNC over websocket URL: %s", websocketUrl));
 +                logger.info(String.format("Connect to VNC over websocket URL: %s", websocketUrl));
+                 ConsoleProxy.ensureRoute(NetUtils.extractHost(websocketUrl));
                  client.connectToWebSocket(websocketUrl, session);
              } else if (tunnelUrl != null && !tunnelUrl.isEmpty() && tunnelSession != null
                      && !tunnelSession.isEmpty()) {
diff --cc ui/public/locales/en.json
index c946770d26c,66d45056c96..6d1b5296a83
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@@ -584,8 -581,9 +585,10 @@@
  "label.create.tier.networkofferingid.description": "The Network offering for the Network Tier.",
  "label.create.tungsten.routing.policy": "Create Tungsten-Fabric routing policy",
  "label.create.user": "Create User",
+ "label.create.vm": "Create Instance",
+ "label.create.vm.and.stay": "Create Instance & stay on this page",
  "label.create.vpn.connection": "Create VPN connection",
 +"label.create.webhook": "Create Webhook",
  "label.created": "Created",
  "label.creating": "Creating",
  "label.creating.iprange": "Creating IP ranges",
diff --cc ui/src/views/compute/DeployVM.vue
index 6792314e74b,c99ffe4d321..746841e65a1
--- a/ui/src/views/compute/DeployVM.vue
+++ b/ui/src/views/compute/DeployVM.vue
@@@ -2343,11 -2275,7 +2343,10 @@@ export default 
          args.pageSize = args.pageSize || 10
        }
        args.zoneid = _.get(this.zone, 'id')
 +      args.account = store.getters.project?.id ? null : this.owner.account
 +      args.domainid = store.getters.project?.id ? null : this.owner.domainid
 +      args.projectid = store.getters.project?.id || this.owner.projectid
        args.templatefilter = templateFilter
-       args.projectid = -1
        args.details = 'all'
        args.showicon = 'true'
        args.id = this.templateId
diff --cc vmware-base/src/main/java/com/cloud/hypervisor/vmware/mo/VirtualMachineMO.java
index 1e53f7a0a8b,d21d20b791c..e0033e55fc6
--- 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
@@@ -1829,14 -1838,27 +1836,27 @@@ public class VirtualMachineMO extends B
                  try {
                      HttpNfcLeaseInfo leaseInfo = leaseMo.getLeaseInfo();
                      final long totalBytes = leaseInfo.getTotalDiskCapacityInKB() * 1024;
-                     long totalBytesDownloaded = 0;
+                     AtomicLong totalBytesDownloaded = new AtomicLong(0L);
  
                      List<HttpNfcLeaseDeviceUrl> deviceUrls = leaseInfo.getDeviceUrl();
 -                    s_logger.info("volss: copy vmdk and ovf file starts " + System.currentTimeMillis());
 +                    logger.info("volss: copy vmdk and ovf file starts " + System.currentTimeMillis());
                      if (deviceUrls != null) {
-                         OvfFile[] ovfFiles = new OvfFile[deviceUrls.size()];
-                         for (int i = 0; i < deviceUrls.size(); i++) {
+                         int deviceUrlsCount = deviceUrls.size();
+                         boolean parallelDownload = false;
+                         if (threadsCountToExportOvf >= 0 && deviceUrlsCount > 1) {
+                             if (threadsCountToExportOvf == 0) {
+                                 threadsCountToExportOvf = deviceUrlsCount;
+                                 parallelDownload = true;
+                             } else if (threadsCountToExportOvf > 1) {
+                                 parallelDownload = true;
+                             }
+                         }
+                         OvfFile[] ovfFiles = new OvfFile[deviceUrlsCount];
+                         List<CompletableFuture<Long>> futures = new ArrayList<>();
+                         ExecutorService executor = null;
+                         for (int i = 0; i < deviceUrlsCount; i++) {
                              String deviceId = deviceUrls.get(i).getKey();
+                             Long diskFileSize = deviceUrls.get(i).getFileSize();
                              String deviceUrlStr = deviceUrls.get(i).getUrl();
                              String orgDiskFileName = deviceUrlStr.substring(deviceUrlStr.lastIndexOf("/") + 1);
                              String diskFileName = String.format("%s-disk%d%s", exportName, i, VmwareHelper.getFileExtension(orgDiskFileName, ".vmdk"));
@@@ -1845,25 -1867,77 +1865,77 @@@
                              String diskLocalPath = exportDir + File.separator + diskFileName;
                              fileNames.add(diskLocalPath);
  
-                             if (logger.isInfoEnabled()) {
-                                 logger.info("Download VMDK file for export. url: " + deviceUrlStr);
+                             if (!parallelDownload) {
 -                                if (s_logger.isInfoEnabled()) {
 -                                    s_logger.info("Download VMDK file for export url: " + deviceUrlStr + ", size: " + diskFileSize);
++                                if (logger.isInfoEnabled()) {
++                                    logger.info("Download VMDK file for export url: " + deviceUrlStr + ", size: " + diskFileSize);
+                                 }
+                                 long lengthOfDiskFile = _context.downloadVmdkFile(diskUrlStr, diskLocalPath, totalBytesDownloaded, new ActionDelegate<Long>() {
+                                     @Override
+                                     public void action(Long param) {
 -                                        if (s_logger.isTraceEnabled()) {
 -                                            s_logger.trace("Download progress " + param + "/" + toHumanReadableSize(totalBytes));
++                                        if (logger.isTraceEnabled()) {
++                                            logger.trace("Download progress " + param + "/" + toHumanReadableSize(totalBytes));
+                                         }
+                                         progressReporter.reportProgress((int)(param * 100 / totalBytes));
+                                     }
+                                 });
+                                 totalBytesDownloaded.addAndGet(lengthOfDiskFile);
+ 
+                                 OvfFile ovfFile = new OvfFile();
+                                 ovfFile.setPath(diskFileName);
+                                 ovfFile.setDeviceId(deviceId);
+                                 ovfFile.setSize(lengthOfDiskFile);
+                                 ovfFiles[i] = ovfFile;
+                             } else {
+                                 String diskUrl = diskUrlStr;
+                                 executor = Executors.newFixedThreadPool(Math.min(threadsCountToExportOvf, deviceUrlsCount));
 -                                if (s_logger.isInfoEnabled()) {
 -                                    s_logger.info("Download VMDK file for export url: " + deviceUrlStr + ", size: " + diskFileSize);
++                                if (logger.isInfoEnabled()) {
++                                    logger.info("Download VMDK file for export url: " + deviceUrlStr + ", size: " + diskFileSize);
+                                 }
+                                 CompletableFuture<Long> future = CompletableFuture.supplyAsync(() -> {
+                                     long lengthOfDiskFile = 0;
+                                     try {
+                                         lengthOfDiskFile = _context.downloadVmdkFile(diskUrl, diskLocalPath, totalBytesDownloaded, new ActionDelegate<Long>() {
+                                             @Override
+                                             public void action(Long param) {
 -                                                if (s_logger.isTraceEnabled()) {
 -                                                    s_logger.trace("Download progress " + param + "/" + toHumanReadableSize(totalBytes));
++                                                if (logger.isTraceEnabled()) {
++                                                    logger.trace("Download progress " + param + "/" + toHumanReadableSize(totalBytes));
+                                                 }
+                                                 progressReporter.reportProgress((int)(param * 100 / totalBytes));
+                                             }
+                                         });
+                                     } catch (Exception e) {
 -                                        s_logger.error("Error on downloading VMDK file for export url: " + diskUrl, e);
++                                        logger.error("Error on downloading VMDK file for export url: " + diskUrl, e);
+                                     }
+                                     return lengthOfDiskFile;
+                                 }, executor);
+                                 futures.add(future);
+ 
+                                 OvfFile ovfFile = new OvfFile();
+                                 ovfFile.setPath(diskFileName);
+                                 ovfFile.setDeviceId(deviceId);
+                                 ovfFile.setSize(0L);
+                                 ovfFiles[i] = ovfFile;
                              }
-                             long lengthOfDiskFile = _context.downloadVmdkFile(diskUrlStr, diskLocalPath, totalBytesDownloaded, new ActionDelegate<Long>() {
-                                 @Override
-                                 public void action(Long param) {
-                                     if (logger.isTraceEnabled()) {
-                                         logger.trace("Download progress " + param + "/" + toHumanReadableSize(totalBytes));
+                         }
+ 
+                         if (parallelDownload) {
+                             CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]));
+                             List<Long> diskFileLengths = allFutures.exceptionally(e -> {
 -                                        s_logger.error("Error on downloading VMDK files: " + e.getMessage());
++                                        logger.error("Error on downloading VMDK files: " + e.getMessage());
+                                         return null;
+                                     }).thenApply(v -> futures.stream().map(CompletableFuture::join).collect(Collectors.toList())).join();
+                             executor.shutdown();
+ 
+                             if (CollectionUtils.isNotEmpty(diskFileLengths)) {
+                                 int i = 0;
+                                 for (Long diskFileLength : diskFileLengths) {
+                                     if (diskFileLength != null) {
+                                         totalBytesDownloaded.addAndGet(diskFileLength);
+                                         ovfFiles[i].setSize(diskFileLength);
                                      }
-                                     progressReporter.reportProgress((int)(param * 100 / totalBytes));
+                                     i++;
                                  }
-                             });
-                             totalBytesDownloaded += lengthOfDiskFile;
- 
-                             OvfFile ovfFile = new OvfFile();
-                             ovfFile.setPath(diskFileName);
-                             ovfFile.setDeviceId(deviceId);
-                             ovfFile.setSize(lengthOfDiskFile);
-                             ovfFiles[i] = ovfFile;
+                             }
                          }
  
                          // write OVF descriptor file
@@@ -1890,10 -1964,12 +1962,12 @@@
                              command.add("-cf", exportName + ".ova");
                              command.add(exportName + ".ovf");        // OVF file should be the first file in OVA archive
                              for (String name : fileNames) {
-                                 command.add((new File(name).getName()));
+                                 if (!name.endsWith(".ovf")) {
+                                     command.add((new File(name).getName()));
+                                 }
                              }
  
 -                            s_logger.info("Package OVA with command: " + command.toString());
 +                            logger.info("Package OVA with command: " + command.toString());
                              command.execute();
  
                              // to be safe, physically test existence of the target OVA file
diff --cc vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java
index 0591fbed118,8a637721506..37b0d4d2444
--- a/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java
+++ b/vmware-base/src/main/java/com/cloud/hypervisor/vmware/util/VmwareHelper.java
@@@ -839,7 -843,7 +844,7 @@@ public class VmwareHelper 
              instance.setDisks(getUnmanageInstanceDisks(vmMo));
              instance.setNics(getUnmanageInstanceNics(hyperHost, vmMo));
          } catch (Exception e) {
-             LOGGER.info("Unable to retrieve unmanaged instance info. " + e.getMessage());
 -            s_logger.error("Unable to retrieve unmanaged instance info, due to: " + e.getMessage());
++            LOGGER.error("Unable to retrieve unmanaged instance info, due to: " + e.getMessage());
          }
          return instance;
      }