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

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

commit 0b3959221b5b0a24b9574f2a4bc7394d52ac5a22
Merge: f2bda464193 be08fff32b7
Author: Daan Hoogland <d...@apache.org>
AuthorDate: Tue Jul 29 16:50:55 2025 +0200

    Merge branch '4.20'

 .../main/java/com/cloud/user/AccountService.java   |   2 +
 .../api/command/admin/cluster/ListClustersCmd.java |   2 +-
 .../api/command/admin/pod/ListPodsByCmd.java       |   4 +-
 .../command/user/config/ListCapabilitiesCmd.java   |   1 +
 .../command/user/template/CreateTemplateCmd.java   |  10 +
 .../command/user/userdata/RegisterUserDataCmd.java |  52 +-
 .../api/command/user/zone/ListZonesCmd.java        |   7 -
 .../api/response/CapabilitiesResponse.java         |   8 +
 .../cloudstack/api/response/UserDataResponse.java  |  22 +-
 .../org/apache/cloudstack/query/QueryService.java  |  12 +-
 .../com/cloud/agent/api/CheckVolumeAnswer.java     |  15 +-
 .../cloud/agent/api/CopyRemoteVolumeAnswer.java    |  15 +-
 .../main/java/com/cloud/vm/VirtualMachineGuru.java |   4 +
 .../network/as/dao/AutoScaleVmGroupVmMapDao.java   |   2 +
 .../as/dao/AutoScaleVmGroupVmMapDaoImpl.java       |   9 +
 .../cloudstack/backup/NASBackupProvider.java       |  13 +-
 .../kvm/resource/LibvirtComputingResource.java     |  11 +-
 .../LibvirtCheckConvertInstanceCommandWrapper.java |   2 +-
 .../wrapper/LibvirtCheckVolumeCommandWrapper.java  |  80 ++-
 .../LibvirtConvertInstanceCommandWrapper.java      |   2 +-
 .../LibvirtCopyRemoteVolumeCommandWrapper.java     |  76 +-
 .../LibvirtGetVolumesOnStorageCommandWrapper.java  |  66 +-
 .../wrapper/LibvirtReadyCommandWrapper.java        |   6 +-
 .../wrapper/LibvirtResizeVolumeCommandWrapper.java |  19 +-
 .../LibvirtRestoreBackupCommandWrapper.java        |  60 +-
 .../hypervisor/kvm/storage/KVMPhysicalDisk.java    |  32 +
 .../contrail/management/MockAccountManager.java    |   6 +
 .../lifecycle/StorageVmSharedFSLifeCycle.java      |  25 +-
 .../lifecycle/StorageVmSharedFSLifeCycleTest.java  |  11 +-
 plugins/storage/volume/linstor/CHANGELOG.md        |   6 +
 .../kvm/storage/LinstorStorageAdaptor.java         |   2 +-
 .../driver/LinstorPrimaryDataStoreDriverImpl.java  |  12 +-
 .../driver/ScaleIOPrimaryDataStoreDriver.java      |  16 +-
 .../driver/ScaleIOPrimaryDataStoreDriverTest.java  |  12 +
 .../kvm/storage/StorPoolStorageAdaptor.java        |   3 +
 .../cloudstack/api/command/LdapListUsersCmd.java   |   5 +-
 .../apache/cloudstack/ldap/LdapAuthenticator.java  |  58 +-
 .../apache/cloudstack/ldap/LdapManagerImpl.java    |  22 +-
 .../java/com/cloud/api/query/QueryManagerImpl.java | 540 +++++++-------
 .../cloud/api/query/dao/TemplateJoinDaoImpl.java   |   7 +-
 .../configuration/ConfigurationManagerImpl.java    |  12 +-
 .../consoleproxy/ConsoleProxyManagerImpl.java      |   6 +-
 .../com/cloud/network/as/AutoScaleManager.java     |   6 +
 .../com/cloud/network/as/AutoScaleManagerImpl.java |   8 +-
 .../com/cloud/resource/ResourceManagerImpl.java    |   2 +-
 .../resourcelimit/ResourceLimitManagerImpl.java    | 112 ++-
 .../com/cloud/server/ManagementServerImpl.java     |  14 +-
 .../com/cloud/storage/VolumeApiServiceImpl.java    |  16 +-
 .../com/cloud/template/TemplateManagerImpl.java    |   2 +-
 .../java/com/cloud/user/AccountManagerImpl.java    |   1 +
 .../cloudstack/backup/BackupManagerImpl.java       |   2 +
 .../cloudstack/vm/UnmanagedVMsManagerImpl.java     |  45 +-
 .../cloud/storage/VolumeApiServiceImplTest.java    |   7 +-
 .../com/cloud/user/MockAccountManagerImpl.java     |   5 +
 .../java/com/cloud/consoleproxy/ConsoleProxy.java  |   7 +
 .../consoleproxy/ConsoleProxyNoVncClient.java      |  84 ++-
 .../com/cloud/consoleproxy/vnc/NoVncClient.java    |   8 +-
 .../cloud/consoleproxy/vnc/network/NioSocket.java  |   3 +-
 .../consoleproxy/vnc/network/NioSocketHandler.java |   3 +-
 .../vnc/network/NioSocketHandlerImpl.java          |  16 +-
 .../vnc/network/NioSocketInputStream.java          |  46 +-
 .../vnc/network/NioSocketSSLEngineManager.java     |  40 +-
 .../vnc/network/NioSocketTLSInputStream.java       |   3 +-
 .../vnc/network/NioSocketTLSOutputStream.java      |   5 +-
 .../SecondaryStorageManagerImpl.java               |   5 +-
 .../storage/resource/HttpUploadServerHandler.java  |  12 +-
 systemvm/agent/conf/consoleproxy.properties        |   1 +
 systemvm/debian/opt/cloud/bin/setup/common.sh      |   6 +-
 .../debian/opt/cloud/bin/setup/consoleproxy.sh     |   4 +
 systemvm/debian/opt/cloud/bin/setup/secstorage.sh  |   1 +
 .../plugins/linstor/test_linstor_volumes.py        |  73 +-
 ui/public/locales/ar.json                          |  24 +-
 ui/public/locales/ca.json                          |  22 +-
 ui/public/locales/de_DE.json                       |  26 +-
 ui/public/locales/el_GR.json                       |  51 +-
 ui/public/locales/en.json                          | 793 +++++++++++----------
 ui/public/locales/es.json                          |  24 +-
 ui/public/locales/fr_FR.json                       |  24 +-
 ui/public/locales/hi.json                          |   4 +-
 ui/public/locales/hu.json                          |  26 +-
 ui/public/locales/it_IT.json                       |  24 +-
 ui/public/locales/ja_JP.json                       |  24 +-
 ui/public/locales/ko_KR.json                       |  24 +-
 ui/public/locales/nb_NO.json                       |  24 +-
 ui/public/locales/nl_NL.json                       |  24 +-
 ui/public/locales/pl.json                          |  24 +-
 ui/public/locales/pt_BR.json                       |  24 +-
 ui/public/locales/ru_RU.json                       |  24 +-
 ui/public/locales/zh_CN.json                       |  24 +-
 ui/src/components/view/InfoCard.vue                |  24 +-
 ui/src/components/view/ListView.vue                |  14 +-
 ui/src/config/section/account.js                   |   2 +-
 ui/src/config/section/compute.js                   |   6 +-
 ui/src/config/section/domain.js                    |   5 +
 ui/src/config/section/network.js                   |  21 +-
 ui/src/config/section/storage.js                   |   2 +-
 ui/src/views/compute/AutoScaleVmProfile.vue        |  14 +-
 ui/src/views/compute/CreateAutoScaleVmGroup.vue    |  14 +-
 ui/src/views/compute/DeployVM.vue                  |  56 +-
 ui/src/views/compute/DeployVnfAppliance.vue        |  73 +-
 ui/src/views/compute/EditVM.vue                    |   7 +-
 ui/src/views/compute/RegisterUserData.vue          |   8 +-
 ui/src/views/compute/ResetUserData.vue             |  14 +-
 ui/src/views/compute/wizard/UserDataSelection.vue  |   5 +-
 ui/src/views/image/RegisterOrUploadIso.vue         |   4 +-
 ui/src/views/image/RegisterOrUploadTemplate.vue    |   4 +-
 ui/src/views/image/UpdateISO.vue                   |   4 +-
 ui/src/views/image/UpdateTemplate.vue              |   4 +-
 ui/src/views/infra/network/ServiceProvidersTab.vue |   2 +-
 .../views/infra/zone/ZoneWizardZoneDetailsStep.vue |  32 +-
 .../{AclListRulesTab.vue => AclRulesTab.vue}       |   2 +-
 ui/src/views/network/VpcTab.vue                    |   8 +-
 ui/src/views/offering/AddComputeOffering.vue       |   7 +
 ui/src/views/offering/AddDiskOffering.vue          |   4 +
 ui/src/views/offering/AddNetworkOffering.vue       |   5 +-
 ui/src/views/offering/AddVpcOffering.vue           |   3 +
 ui/src/views/storage/CreateTemplate.vue            |  25 +-
 .../com/cloud/hypervisor/vmware/mo/HostMO.java     | 152 +---
 118 files changed, 1992 insertions(+), 1551 deletions(-)

diff --cc 
api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java
index 91632b910ff,bd3f39a09aa..e73bb97a21b
--- 
a/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java
@@@ -72,8 -72,7 +72,9 @@@ public class ListCapabilitiesCmd extend
          response.setInstancesDisksStatsRetentionTime((Integer) 
capabilities.get(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME));
          
response.setSharedFsVmMinCpuCount((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_CPU_COUNT));
          
response.setSharedFsVmMinRamSize((Integer)capabilities.get(ApiConstants.SHAREDFSVM_MIN_RAM_SIZE));
 +        response.setInstanceLeaseEnabled((Boolean) 
capabilities.get(ApiConstants.INSTANCE_LEASE_ENABLED));
 +        
response.setExtensionsPath((String)capabilities.get(ApiConstants.EXTENSIONS_PATH));
+         response.setDynamicScalingEnabled((Boolean) 
capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED));
          response.setObjectName("capability");
          response.setResponseName(getCommandName());
          this.setResponseObject(response);
diff --cc 
api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java
index b0a82c86fb5,ff2e33b1389..eb0daf75148
--- 
a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java
+++ 
b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java
@@@ -137,14 -136,10 +137,18 @@@ public class CapabilitiesResponse exten
      @Param(description = "the min Ram size for the service offering used by 
the shared filesystem instance", since = "4.20.0")
      private Integer sharedFsVmMinRamSize;
  
 +    @SerializedName(ApiConstants.INSTANCE_LEASE_ENABLED)
 +    @Param(description = "true if instance lease feature is enabled", since = 
"4.21.0")
 +    private Boolean instanceLeaseEnabled;
 +
 +    @SerializedName(ApiConstants.EXTENSIONS_PATH)
 +    @Param(description = "The path of the extensions directory", since = 
"4.21.0", authorized = {RoleType.Admin})
 +    private String extensionsPath;
 +
+     @SerializedName(ApiConstants.DYNAMIC_SCALING_ENABLED)
+     @Param(description = "true if dynamically scaling for instances is 
enabled", since = "4.21.0")
+     private Boolean dynamicScalingEnabled;
+ 
      public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) {
          this.securityGroupsEnabled = securityGroupsEnabled;
      }
@@@ -257,11 -252,7 +261,15 @@@
          this.sharedFsVmMinRamSize = sharedFsVmMinRamSize;
      }
  
 +    public void setInstanceLeaseEnabled(Boolean instanceLeaseEnabled) {
 +        this.instanceLeaseEnabled = instanceLeaseEnabled;
 +    }
 +
 +    public void setExtensionsPath(String extensionsPath) {
 +        this.extensionsPath = extensionsPath;
 +    }
++
+     public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) {
+         this.dynamicScalingEnabled = dynamicScalingEnabled;
+     }
  }
diff --cc 
plugins/storage/sharedfs/storagevm/src/test/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycleTest.java
index 813d8978697,2cc909ce0d7..21753257f75
--- 
a/plugins/storage/sharedfs/storagevm/src/test/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycleTest.java
+++ 
b/plugins/storage/sharedfs/storagevm/src/test/java/org/apache/cloudstack/storage/sharedfs/lifecycle/StorageVmSharedFSLifeCycleTest.java
@@@ -257,11 -258,16 +257,16 @@@ public class StorageVmSharedFSLifeCycle
                  anyString(), anyLong(), anyLong(), isNull(), 
any(Hypervisor.HypervisorType.class), any(BaseCmd.HTTPMethod.class), 
anyString(),
                  isNull(), isNull(), anyList(), isNull(), 
any(Network.IpAddresses.class), isNull(), isNull(), isNull(),
                  anyMap(), isNull(), isNull(), isNull(), isNull(),
 -                anyBoolean(), anyString(), isNull())).thenReturn(vm);
 +                anyBoolean(), anyString(), isNull(), isNull(), 
isNull())).thenReturn(vm);
  
-         VolumeVO volume = mock(VolumeVO.class);
-         when(volume.getId()).thenReturn(s_volumeId);
-         when(volumeDao.findByInstanceAndType(s_vmId, 
Volume.Type.DATADISK)).thenReturn(List.of(volume));
+         VolumeVO rootVol = mock(VolumeVO.class);
+         when(rootVol.getVolumeType()).thenReturn(Volume.Type.ROOT);
+         when(rootVol.getName()).thenReturn("ROOT-1");
+         VolumeVO dataVol = mock(VolumeVO.class);
+         when(dataVol.getId()).thenReturn(s_volumeId);
+         when(dataVol.getName()).thenReturn("DATA-1");
+         when(dataVol.getVolumeType()).thenReturn(Volume.Type.DATADISK);
+         when(volumeDao.findByInstance(s_vmId)).thenReturn(List.of(rootVol, 
dataVol));
  
           Pair<Long, Long> result = lifeCycle.deploySharedFS(sharedFS, 
s_networkId, s_diskOfferingId, s_size, s_minIops, s_maxIops);
           Assert.assertEquals(Optional.ofNullable(result.first()), 
Optional.ofNullable(s_volumeId));
diff --cc server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
index 1643c1962ce,6fb9ab515cb..59e37c9f4df
--- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
@@@ -169,13 -161,11 +170,13 @@@ import org.apache.cloudstack.storage.da
  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.cloudstack.storage.datastore.db.TemplateDataStoreDao;
 +import org.apache.cloudstack.vm.lease.VMLeaseManager;
  import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.collections.MapUtils;
  import org.apache.commons.lang3.EnumUtils;
 +import org.apache.commons.lang3.ObjectUtils;
  import org.apache.commons.lang3.StringUtils;
+ import org.jetbrains.annotations.NotNull;
  import org.springframework.stereotype.Component;
  
  import com.cloud.api.query.dao.AccountJoinDao;
@@@ -632,68 -605,6 +623,27 @@@ public class QueryManagerImpl extends M
      @Inject
      ManagementServerHostPeerJoinDao mshostPeerJoinDao;
  
 +    @Inject
 +    private AsyncJobManager jobManager;
-     @Inject
-     private VMTemplateDetailsDao templateDetailsDao;
 +
 +    @Inject
 +    private StoragePoolAndAccessGroupMapDao storagePoolAndAccessGroupMapDao;
 +
 +    @Inject
 +    public ManagementService managementService;
 +
 +    @Inject
 +    DataCenterDao dataCenterDao;
 +
 +    @Inject
 +    HostPodDao podDao;
 +
 +    @Inject
 +    GuestOSDao guestOSDao;
 +
 +    @Inject
 +    ExtensionHelper extensionHelper;
 +
-     private SearchCriteria<ServiceOfferingJoinVO> 
getMinimumCpuServiceOfferingJoinSearchCriteria(int cpu) {
-         SearchCriteria<ServiceOfferingJoinVO> sc = 
_srvOfferingJoinDao.createSearchCriteria();
-         SearchCriteria<ServiceOfferingJoinVO> sc1 = 
_srvOfferingJoinDao.createSearchCriteria();
-         sc1.addAnd("cpu", Op.GTEQ, cpu);
-         sc.addOr("cpu", Op.SC, sc1);
-         SearchCriteria<ServiceOfferingJoinVO> sc2 = 
_srvOfferingJoinDao.createSearchCriteria();
-         sc2.addAnd("cpu", Op.NULL);
-         sc2.addAnd("maxCpu", Op.NULL);
-         sc.addOr("cpu", Op.SC, sc2);
-         SearchCriteria<ServiceOfferingJoinVO> sc3 = 
_srvOfferingJoinDao.createSearchCriteria();
-         sc3.addAnd("cpu", Op.NULL);
-         sc3.addAnd("maxCpu", Op.GTEQ, cpu);
-         sc.addOr("cpu", Op.SC, sc3);
-         return sc;
-     }
- 
-     private SearchCriteria<ServiceOfferingJoinVO> 
getMinimumMemoryServiceOfferingJoinSearchCriteria(int memory) {
-         SearchCriteria<ServiceOfferingJoinVO> sc = 
_srvOfferingJoinDao.createSearchCriteria();
-         SearchCriteria<ServiceOfferingJoinVO> sc1 = 
_srvOfferingJoinDao.createSearchCriteria();
-         sc1.addAnd("ramSize", Op.GTEQ, memory);
-         sc.addOr("ramSize", Op.SC, sc1);
-         SearchCriteria<ServiceOfferingJoinVO> sc2 = 
_srvOfferingJoinDao.createSearchCriteria();
-         sc2.addAnd("ramSize", Op.NULL);
-         sc2.addAnd("maxMemory", Op.NULL);
-         sc.addOr("ramSize", Op.SC, sc2);
-         SearchCriteria<ServiceOfferingJoinVO> sc3 = 
_srvOfferingJoinDao.createSearchCriteria();
-         sc3.addAnd("ramSize", Op.NULL);
-         sc3.addAnd("maxMemory", Op.GTEQ, memory);
-         sc.addOr("ramSize", Op.SC, sc3);
-         return sc;
-     }
- 
-     private SearchCriteria<ServiceOfferingJoinVO> 
getMinimumCpuSpeedServiceOfferingJoinSearchCriteria(int speed) {
-         SearchCriteria<ServiceOfferingJoinVO> sc = 
_srvOfferingJoinDao.createSearchCriteria();
-         sc.addOr("speed", Op.GTEQ, speed);
-         sc.addOr("speed", Op.NULL);
-         return sc;
-     }
- 
      /*
       * (non-Javadoc)
       *
@@@ -736,10 -643,10 +682,10 @@@
          String keyword = null;
  
          Pair<List<UserAccountJoinVO>, Integer> result =  
getUserListInternal(caller, permittedAccounts, listAll, id,
 -                username, type, accountName, state, keyword, null, domainId, 
recursive, null);
 +                username, type, accountName, state, keyword, null, domainId, 
recursive, null, null);
-         ListResponse<UserResponse> response = new 
ListResponse<UserResponse>();
+         ListResponse<UserResponse> response = new ListResponse<>();
          List<UserResponse> userResponses = 
ViewResponseHelper.createUserResponse(ResponseView.Restricted, 
CallContext.current().getCallingAccount().getDomainId(),
-                 result.first().toArray(new 
UserAccountJoinVO[result.first().size()]));
+                 result.first().toArray(new UserAccountJoinVO[0]));
          response.setResponses(userResponses, result.second());
          return response;
      }
@@@ -777,8 -683,8 +723,8 @@@
      }
  
      private Pair<List<UserAccountJoinVO>, Integer> 
getUserListInternal(Account caller, List<Long> permittedAccounts, boolean 
listAll, Long id, Object username, Object type,
 -            String accountName, Object state, String keyword, String 
apiKeyAccess, Long domainId, boolean recursive, Filter searchFilter) {
 +            String accountName, Object state, String keyword, String 
apiKeyAccess, Long domainId, boolean recursive, Filter searchFilter, 
User.Source userSource) {
-         Ternary<Long, Boolean, ListProjectResourcesCriteria> 
domainIdRecursiveListProject = new Ternary<Long, Boolean, 
ListProjectResourcesCriteria>(domainId, recursive, null);
+         Ternary<Long, Boolean, ListProjectResourcesCriteria> 
domainIdRecursiveListProject = new Ternary<>(domainId, recursive, null);
          accountMgr.buildACLSearchParameters(caller, id, accountName, null, 
permittedAccounts, domainIdRecursiveListProject, listAll, false);
          domainId = domainIdRecursiveListProject.first();
          Boolean isRecursive = domainIdRecursiveListProject.second();
@@@ -2418,18 -2274,7 +2363,18 @@@
          Pair<List<HostJoinVO>, Integer> hosts = searchForServersInternal(cmd);
          ListResponse<HostResponse> response = new ListResponse<>();
          logger.debug(">>>Generating Response>>>");
-         List<HostResponse> hostResponses = 
ViewResponseHelper.createHostResponse(cmd.getDetails(), 
hosts.first().toArray(new HostJoinVO[hosts.first().size()]));
+         List<HostResponse> hostResponses = 
ViewResponseHelper.createHostResponse(cmd.getDetails(), 
hosts.first().toArray(new HostJoinVO[0]));
 +        updateHostsExtensions(hostResponses);
 +        response.setResponses(hostResponses, hosts.second());
 +        return response;
 +    }
 +
 +    private ListResponse<HostResponse> 
searchForServersWithMinimalResponse(ListHostsCmd cmd) {
 +        logger.debug(">>>Searching for hosts>>>");
 +        Pair<List<HostJoinVO>, Integer> hosts = searchForServersInternal(cmd);
-         ListResponse<HostResponse> response = new 
ListResponse<HostResponse>();
++        ListResponse<HostResponse> response = new ListResponse<>();
 +        logger.debug(">>>Generating Response>>>");
-         List<HostResponse> hostResponses = 
ViewResponseHelper.createMinimalHostResponse(hosts.first().toArray(new 
HostJoinVO[hosts.first().size()]));
++        List<HostResponse> hostResponses = 
ViewResponseHelper.createMinimalHostResponse(hosts.first().toArray(new 
HostJoinVO[0]));
          response.setResponses(hostResponses, hosts.second());
          return response;
      }
@@@ -3275,51 -3096,31 +3220,51 @@@
      private ListResponse<StoragePoolResponse> 
createStoragesPoolResponse(Pair<List<StoragePoolJoinVO>, Integer> storagePools, 
boolean getCustomStats) {
          ListResponse<StoragePoolResponse> response = new ListResponse<>();
  
-         List<StoragePoolResponse> poolResponses = 
ViewResponseHelper.createStoragePoolResponse(getCustomStats, 
storagePools.first().toArray(new 
StoragePoolJoinVO[storagePools.first().size()]));
+         List<StoragePoolResponse> poolResponses = 
ViewResponseHelper.createStoragePoolResponse(getCustomStats, 
storagePools.first().toArray(new StoragePoolJoinVO[0]));
          Map<String, Long> poolUuidToIdMap = 
storagePools.first().stream().collect(Collectors.toMap(StoragePoolJoinVO::getUuid,
 StoragePoolJoinVO::getId, (a, b) -> a));
          for (StoragePoolResponse poolResponse : poolResponses) {
 +            Long poolId = poolUuidToIdMap.get(poolResponse.getId());
              DataStore store = 
dataStoreManager.getPrimaryDataStore(poolResponse.getId());
 +
              if (store != null) {
 -                DataStoreDriver driver = store.getDriver();
 -                if (driver != null && driver.getCapabilities() != null) {
 -                    Map<String, String> caps = driver.getCapabilities();
 -                    if 
(Storage.StoragePoolType.NetworkFilesystem.toString().equals(poolResponse.getType())
 &&
 -                        
HypervisorType.VMware.toString().equals(poolResponse.getHypervisor())) {
 -                        StoragePoolDetailVO detail = 
_storagePoolDetailsDao.findDetail(poolUuidToIdMap.get(poolResponse.getId()), 
Storage.Capability.HARDWARE_ACCELERATION.toString());
 -                        if (detail != null) {
 -                            
caps.put(Storage.Capability.HARDWARE_ACCELERATION.toString(), 
detail.getValue());
 -                        }
 -                    }
 -                    poolResponse.setCaps(caps);
 -                }
 +                addPoolDetailsAndCapabilities(poolResponse, store, poolId);
              }
 -            setPoolResponseNFSMountOptions(poolResponse, 
poolUuidToIdMap.get(poolResponse.getId()));
 +
 +            setPoolResponseNFSMountOptions(poolResponse, poolId);
          }
  
          response.setResponses(poolResponses, storagePools.second());
          return response;
      }
  
 +    private void addPoolDetailsAndCapabilities(StoragePoolResponse 
poolResponse, DataStore store, Long poolId) {
 +        Map<String, String> details = 
_storagePoolDetailsDao.listDetailsKeyPairs(store.getId(), true);
 +        poolResponse.setDetails(details);
 +
 +        DataStoreDriver driver = store.getDriver();
 +        if (ObjectUtils.anyNull(driver, driver.getCapabilities())) {
 +            return;
 +        }
 +
 +        Map<String, String> caps = driver.getCapabilities();
 +        if 
(Storage.StoragePoolType.NetworkFilesystem.toString().equals(poolResponse.getType())
 && HypervisorType.VMware.toString().equals(poolResponse.getHypervisor())) {
 +            StoragePoolDetailVO detail = 
_storagePoolDetailsDao.findDetail(poolId, 
Storage.Capability.HARDWARE_ACCELERATION.toString());
 +            if (detail != null) {
 +                caps.put(Storage.Capability.HARDWARE_ACCELERATION.toString(), 
detail.getValue());
 +            }
 +        }
 +        poolResponse.setCaps(caps);
 +    }
 +
 +    private ListResponse<StoragePoolResponse> 
searchForStoragePoolsWithMinimalResponse(ListStoragePoolsCmd cmd) {
 +        Pair<List<StoragePoolJoinVO>, Integer> result = 
searchForStoragePoolsInternal(cmd);
 +        ListResponse<StoragePoolResponse> response = new ListResponse<>();
 +
-         List<StoragePoolResponse> poolResponses = 
ViewResponseHelper.createMinimalStoragePoolResponse(result.first().toArray(new 
StoragePoolJoinVO[result.first().size()]));
++        List<StoragePoolResponse> poolResponses = 
ViewResponseHelper.createMinimalStoragePoolResponse(result.first().toArray(new 
StoragePoolJoinVO[0]));
 +        response.setResponses(poolResponses, result.second());
 +        return response;
 +    }
 +
      private Pair<List<StoragePoolJoinVO>, Integer> 
searchForStoragePoolsInternal(ListStoragePoolsCmd cmd) {
          ScopeType scopeType = 
ScopeType.validateAndGetScopeType(cmd.getScope());
          StoragePoolStatus status = 
StoragePoolStatus.validateAndGetStatus(cmd.getStatus());
@@@ -3359,100 -3158,7 +3304,100 @@@
          return response;
      }
  
 +    @Override
 +    public ListResponse<StorageAccessGroupResponse> 
searchForStorageAccessGroups(ListStorageAccessGroupsCmd cmd) {
 +        String name = cmd.getName();
 +        String keyword = cmd.getKeyword();
 +        Set<String> storageAccessGroups = new HashSet<>();
 +
 +        addStorageAccessGroups(storageAccessGroups, 
storagePoolAndAccessGroupMapDao.listDistinctStorageAccessGroups(name, keyword));
 +        addStorageAccessGroups(storageAccessGroups, 
hostDao.listDistinctStorageAccessGroups(name, keyword));
 +        addStorageAccessGroups(storageAccessGroups, 
clusterDao.listDistinctStorageAccessGroups(name, keyword));
 +        addStorageAccessGroups(storageAccessGroups, 
podDao.listDistinctStorageAccessGroups(name, keyword));
 +        addStorageAccessGroups(storageAccessGroups, 
dataCenterDao.listDistinctStorageAccessGroups(name, keyword));
 +
 +        if (StringUtils.isNotEmpty(name) && 
storageAccessGroups.contains(name)) {
 +            storageAccessGroups = Collections.singleton(name);
 +        }
 +
 +        if (StringUtils.isNotEmpty(keyword)) {
 +            storageAccessGroups = storageAccessGroups.stream()
 +                    .filter(group -> group.contains(keyword))
 +                    .collect(Collectors.toSet());
 +        }
 +
 +        List<StorageAccessGroupResponse> responseList = 
buildStorageAccessGroupResponses(storageAccessGroups, name);
 +
 +        ListResponse<StorageAccessGroupResponse> response = new 
ListResponse<>();
 +        response.setResponses(responseList, storageAccessGroups.size());
 +        return response;
 +    }
 +
 +    private void addStorageAccessGroups(Set<String> storageAccessGroups, 
List<String> groups) {
 +        for (String group : groups) {
 +            if (group != null && !group.isEmpty()) {
 +                storageAccessGroups.addAll(Arrays.asList(group.split(",")));
 +            }
 +        }
 +    }
 +
 +    private List<StorageAccessGroupResponse> buildStorageAccessGroupResponses(
 +            Set<String> storageAccessGroups, String name) {
 +        List<StorageAccessGroupResponse> responseList = new ArrayList<>();
 +
 +        for (String sag : storageAccessGroups) {
 +            StorageAccessGroupResponse sagResponse = new 
StorageAccessGroupResponse();
 +            sagResponse.setName(sag);
 +            sagResponse.setObjectName(ApiConstants.STORAGE_ACCESS_GROUP);
 +
 +            if (StringUtils.isNotBlank(name)) {
 +                fetchStorageAccessGroupResponse(sagResponse, name);
 +            }
 +
 +            responseList.add(sagResponse);
 +        }
 +        return responseList;
 +    }
 +
 +    private void fetchStorageAccessGroupResponse(StorageAccessGroupResponse 
sagResponse, String name) {
 +        
sagResponse.setHostResponseList(searchForServersWithMinimalResponse(new 
ListHostsCmd(name)));
 +        
sagResponse.setZoneResponseList(listDataCentersWithMinimalResponse(new 
ListZonesCmd(name)));
 +        sagResponse.setPodResponseList(fetchPodsByStorageAccessGroup(name));
 +        
sagResponse.setClusterResponseList(fetchClustersByStorageAccessGroup(name));
 +        
sagResponse.setStoragePoolResponseList(searchForStoragePoolsWithMinimalResponse(new
 ListStoragePoolsCmd(name)));
 +    }
 +
 +    private ListResponse<PodResponse> fetchPodsByStorageAccessGroup(String 
name) {
 +        ListPodsByCmd listPodsByCmd = new ListPodsByCmd(name);
 +        Pair<List<? extends Pod>, Integer> podResponsePair = 
managementService.searchForPods(listPodsByCmd);
 +        List<PodResponse> podResponses = podResponsePair.first().stream()
 +                .map(pod -> {
 +                    PodResponse podResponse = 
responseGenerator.createMinimalPodResponse(pod);
 +                    podResponse.setObjectName("pod");
 +                    return podResponse;
 +                }).collect(Collectors.toList());
 +
 +        ListResponse<PodResponse> podResponse = new ListResponse<>();
 +        podResponse.setResponses(podResponses, podResponsePair.second());
 +        return podResponse;
 +    }
 +
 +    private ListResponse<ClusterResponse> 
fetchClustersByStorageAccessGroup(String name) {
 +        ListClustersCmd listClustersCmd = new ListClustersCmd(name);
 +        Pair<List<? extends Cluster>, Integer> clusterResponsePair = 
managementService.searchForClusters(listClustersCmd);
 +        List<ClusterResponse> clusterResponses = 
clusterResponsePair.first().stream()
 +                .map(cluster -> {
 +                    ClusterResponse clusterResponse = 
responseGenerator.createMinimalClusterResponse(cluster);
 +                    clusterResponse.setObjectName("cluster");
 +                    return clusterResponse;
 +                }).collect(Collectors.toList());
 +
 +        ListResponse<ClusterResponse> clusterResponse = new ListResponse<>();
 +        clusterResponse.setResponses(clusterResponses, 
clusterResponsePair.second());
 +        return clusterResponse;
 +    }
 +
-     private Pair<List<StoragePoolTagVO>, Integer> 
searchForStorageTagsInternal(ListStorageTagsCmd cmd) {
+     private Pair<List<StoragePoolTagVO>, Integer> 
searchForStorageTagsInternal() {
          Filter searchFilter = new Filter(StoragePoolTagVO.class, "id", 
Boolean.TRUE, null, null);
  
          SearchBuilder<StoragePoolTagVO> sb = 
_storageTagDao.createSearchBuilder();
@@@ -4049,9 -3755,6 +3994,8 @@@
          Boolean encryptRoot = cmd.getEncryptRoot();
          String storageType = cmd.getStorageType();
          ServiceOffering.State state = cmd.getState();
-         final Long templateId = cmd.getTemplateId();
 +        final Long vgpuProfileId = cmd.getVgpuProfileId();
 +        final Boolean gpuEnabled = cmd.getGpuEnabled();
  
          final Account owner = accountMgr.finalizeOwner(caller, accountName, 
domainId, projectId);
  
@@@ -4534,24 -4225,14 +4478,28 @@@
          return response;
      }
  
 +    private ListResponse<ZoneResponse> 
listDataCentersWithMinimalResponse(ListZonesCmd cmd) {
 +        Pair<List<DataCenterJoinVO>, Integer> result = 
listDataCentersInternal(cmd);
-         ListResponse<ZoneResponse> response = new 
ListResponse<ZoneResponse>();
++        ListResponse<ZoneResponse> response = new ListResponse<>();
 +
 +        ResponseView respView = ResponseView.Restricted;
 +        if (cmd instanceof ListZonesCmdByAdmin || 
CallContext.current().getCallingAccount().getType() == Account.Type.ADMIN) {
 +            respView = ResponseView.Full;
 +        }
 +
-         List<ZoneResponse> dcResponses = 
ViewResponseHelper.createMinimalDataCenterResponse(respView, 
result.first().toArray(new DataCenterJoinVO[result.first().size()]));
++        List<ZoneResponse> dcResponses = 
ViewResponseHelper.createMinimalDataCenterResponse(respView, 
result.first().toArray(new DataCenterJoinVO[0]));
 +        response.setResponses(dcResponses, result.second());
 +        return response;
 +    }
 +
      private Pair<List<DataCenterJoinVO>, Integer> 
listDataCentersInternal(ListZonesCmd cmd) {
          Account account = CallContext.current().getCallingAccount();
          Long domainId = cmd.getDomainId();
-         Long id = cmd.getId();
+         Long zoneId = cmd.getId();
 -        if( ! 
AllowUserViewAllDataCenters.valueInDomain(account.getDomainId())) {
++        if( ! 
AllowUserViewAllDataCenters.valueInScope(ConfigKey.Scope.Domain, 
account.getDomainId())) {
+             zoneId = 
accountMgr.checkAccessAndSpecifyAuthority(CallContext.current().getCallingAccount(),
 zoneId);
+             logger.debug("not allowing users to view all zones ; selected 
zone is = {}", zoneId);
+         }
          List<Long> ids = getIdsListFromCmd(cmd.getId(), cmd.getIds());
          String keyword = cmd.getKeyword();
          String name = cmd.getName();
@@@ -4729,6 -4300,12 +4575,19 @@@
              }
          }
  
+         buildSearchCriteriaForTags(resourceTags, sc);
+ 
++        if (storageAccessGroup != null) {
++            sc.setParameters("storageAccessGroupExact", storageAccessGroup);
++            sc.setParameters("storageAccessGroupPrefix", storageAccessGroup + 
",%");
++            sc.setParameters("storageAccessGroupSuffix", "%," + 
storageAccessGroup);
++            sc.setParameters("storageAccessGroupMiddle", "%," + 
storageAccessGroup + ",%");
++        }
++
+         return _dcJoinDao.searchAndCount(sc, searchFilter);
+     }
+ 
+     private static void buildSearchCriteriaForTags(Map<String, String> 
resourceTags, SearchCriteria<DataCenterJoinVO> sc) {
          if (resourceTags != null && !resourceTags.isEmpty()) {
              int count = 0;
              sc.setJoinParameters("tagSearch", "resourceType", 
ResourceObjectType.Zone.toString());
@@@ -4850,7 -4539,7 +4822,7 @@@
                  null, cmd.getPageSizeVal(), cmd.getStartIndex(), 
cmd.getZoneId(), cmd.getStoragePoolId(),
                  cmd.getImageStoreId(), hypervisorType, showDomr, 
cmd.listInReadyState(), permittedAccounts, caller,
                  listProjectResourcesCriteria, tags, showRemovedTmpl, 
cmd.getIds(), parentTemplateId, cmd.getShowUnique(),
-                 templateType, isVnf, cmd.getArch(), cmd.getOsCategoryId(), 
forCks, cmd.getExtensionId());
 -                templateType, isVnf, domainId, isRecursive, cmd.getArch());
++                templateType, isVnf, domainId, isRecursive, cmd.getArch(), 
cmd.getOsCategoryId(), forCks, cmd.getExtensionId());
      }
  
      private Pair<List<TemplateJoinVO>, Integer> 
searchForTemplatesInternal(Long templateId, String name, String keyword,
@@@ -4859,7 -4548,7 +4831,7 @@@
              boolean showDomr, boolean onlyReady, List<Account> 
permittedAccounts, Account caller,
              ListProjectResourcesCriteria listProjectResourcesCriteria, 
Map<String, String> tags,
              boolean showRemovedTmpl, List<Long> ids, Long parentTemplateId, 
Boolean showUnique, String templateType,
-             Boolean isVnf, CPU.CPUArch arch, Long osCategoryId, Boolean 
forCks, Long extensionId) {
 -            Boolean isVnf, Long domainId, boolean isRecursive, CPU.CPUArch 
arch) {
++            Boolean isVnf, Long domainId, boolean isRecursive, CPU.CPUArch 
arch, Long osCategoryId, Boolean forCks, Long extensionId) {
  
          // check if zone is configured, if not, just return empty list
          List<HypervisorType> hypers = null;
@@@ -5228,7 -4899,7 +5204,7 @@@
          if (showRemovedTmpl) {
              uniqueTmplPair = 
_templateJoinDao.searchIncludingRemovedAndCount(sc, searchFilter);
          } else {
--            sc.addAnd("templateState", SearchCriteria.Op.IN, new State[] 
{State.Active, State.UploadAbandoned, State.UploadError, State.NotUploaded, 
State.UploadInProgress});
++            sc.addAnd("templateState", SearchCriteria.Op.IN, State.Active, 
State.UploadAbandoned, State.UploadError, State.NotUploaded, 
State.UploadInProgress);
              if (showUnique) {
                  final String[] distinctColumns = {"template_view.id"};
                  uniqueTmplPair = _templateJoinDao.searchAndDistinctCount(sc, 
searchFilter, distinctColumns);
@@@ -5313,7 -4986,7 +5291,7 @@@
                  cmd.getPageSizeVal(), cmd.getStartIndex(), cmd.getZoneId(), 
cmd.getStoragePoolId(), cmd.getImageStoreId(),
                  hypervisorType, true, cmd.listInReadyState(), 
permittedAccounts, caller, listProjectResourcesCriteria,
                  tags, showRemovedISO, null, null, cmd.getShowUnique(), null, 
null,
-                 cmd.getArch(), cmd.getOsCategoryId(), null, null);
 -                domainId, isRecursive, cmd.getArch());
++                domainId, isRecursive, cmd.getArch(), cmd.getOsCategoryId(), 
null, null);
      }
  
      @Override
@@@ -5345,9 -5018,9 +5323,7 @@@
                      .collect(Collectors.toList());
              
userDenyListedSettings.addAll(QueryService.RootAdminOnlyVmSettings);
              for (final String detail : userDenyListedSettings) {
--                if (options.containsKey(detail)) {
--                    options.remove(detail);
--                }
++                options.remove(detail);
              }
          }
          return new DetailOptionsResponse(options);
diff --cc 
server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 03b97af4e10,f0e485a2f46..64b2a66f452
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@@ -50,12 -50,10 +50,7 @@@ import java.util.stream.Collectors
  import javax.inject.Inject;
  import javax.naming.ConfigurationException;
  
- import com.cloud.network.dao.NetrisProviderDao;
- import com.cloud.network.element.NetrisProviderVO;
- import com.cloud.network.netris.NetrisService;
 -
 -import com.cloud.network.as.AutoScaleManager;
 -import com.cloud.user.AccountManagerImpl;
  import org.apache.cloudstack.acl.RoleType;
- import com.cloud.gpu.VgpuProfileVO;
- import com.cloud.gpu.dao.VgpuProfileDao;
  import org.apache.cloudstack.acl.SecurityChecker;
  import org.apache.cloudstack.affinity.AffinityGroup;
  import org.apache.cloudstack.affinity.AffinityGroupService;
@@@ -207,6 -205,6 +202,8 @@@ import com.cloud.exception.PermissionDe
  import com.cloud.exception.ResourceAllocationException;
  import com.cloud.exception.ResourceUnavailableException;
  import com.cloud.gpu.GPU;
++import com.cloud.gpu.VgpuProfileVO;
++import com.cloud.gpu.dao.VgpuProfileDao;
  import com.cloud.host.HostTagVO;
  import com.cloud.host.HostVO;
  import com.cloud.host.dao.HostDao;
@@@ -230,10 -227,10 +227,12 @@@ import com.cloud.network.Networks.Broad
  import com.cloud.network.Networks.TrafficType;
  import com.cloud.network.PhysicalNetwork;
  import com.cloud.network.UserIpv6AddressVO;
++import com.cloud.network.as.AutoScaleManager;
  import com.cloud.network.dao.FirewallRulesDao;
  import com.cloud.network.dao.IPAddressDao;
  import com.cloud.network.dao.IPAddressVO;
  import com.cloud.network.dao.Ipv6GuestPrefixSubnetNetworkMapDao;
++import com.cloud.network.dao.NetrisProviderDao;
  import com.cloud.network.dao.NetworkDao;
  import com.cloud.network.dao.NetworkVO;
  import com.cloud.network.dao.NsxProviderDao;
@@@ -242,7 -239,7 +241,9 @@@ import com.cloud.network.dao.PhysicalNe
  import com.cloud.network.dao.PhysicalNetworkTrafficTypeVO;
  import com.cloud.network.dao.PhysicalNetworkVO;
  import com.cloud.network.dao.UserIpv6AddressDao;
++import com.cloud.network.element.NetrisProviderVO;
  import com.cloud.network.element.NsxProviderVO;
++import com.cloud.network.netris.NetrisService;
  import com.cloud.network.rules.LoadBalancerContainer.Scheme;
  import com.cloud.network.vpc.VpcManager;
  import com.cloud.offering.DiskOffering;
@@@ -597,9 -584,7 +598,10 @@@ public class ConfigurationManagerImpl e
          
configValuesForValidation.add(UserDataManager.VM_USERDATA_MAX_LENGTH_STRING);
          
configValuesForValidation.add(UnmanagedVMsManager.RemoteKvmInstanceDisksCopyTimeout.key());
          
configValuesForValidation.add(UnmanagedVMsManager.ConvertVmwareInstanceToKvmTimeout.key());
 +        
configValuesForValidation.add(VMLeaseManager.InstanceLeaseSchedulerInterval.key());
 +        
configValuesForValidation.add(VMLeaseManager.InstanceLeaseExpiryEventSchedulerInterval.key());
 +        
configValuesForValidation.add(VMLeaseManager.InstanceLeaseExpiryEventDaysBefore.key());
+         
configValuesForValidation.add(AutoScaleManager.AutoScaleErroredInstanceThreshold.key());
      }
  
      protected void weightBasedParametersForValidation() {
diff --cc 
server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java
index 7ada4b7f4cc,85cca63546c..9a6c8a85f18
--- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java
+++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java
@@@ -618,9 -587,9 +613,9 @@@ public class ResourceLimitManagerImpl e
  
      @Override
      public long findDefaultResourceLimitForDomain(ResourceType resourceType) {
-         Long resourceLimit = null;
+         Long resourceLimit;
          resourceLimit = domainResourceLimitMap.get(resourceType.getName());
 -        if (resourceLimit != null && (resourceType == 
ResourceType.primary_storage || resourceType == 
ResourceType.secondary_storage)) {
 +        if (resourceLimit != null && 
ResourceType.isStorageType(resourceType)) {
              if (! 
Long.valueOf(Resource.RESOURCE_UNLIMITED).equals(resourceLimit)) {
                  resourceLimit = resourceLimit * ResourceType.bytesToGiB;
              }
@@@ -1028,12 -983,6 +1023,12 @@@
          }
  
          ResourceLimitVO limit = 
_resourceLimitDao.findByOwnerIdAndTypeAndTag(ownerId, ownerType, resourceType, 
tag);
 +
 +        ActionEventUtils.onActionEvent(caller.getId(), caller.getAccountId(),
 +                caller.getDomainId(), EventTypes.EVENT_RESOURCE_LIMIT_UPDATE,
-                 "Resource limit updated. Resource Type: " + 
resourceType.toString() + ", New Value: " + max,
++                "Resource limit updated. Resource Type: " + resourceType + ", 
New Value: " + max,
 +                ownerResourceId, ownerResourceType.toString());
 +
          if (limit != null) {
              // Update the existing limit
              _resourceLimitDao.update(limit.getId(), max);
@@@ -1332,11 -1274,12 +1330,11 @@@
              _resourceCountDao.persist(new ResourceCountVO(type, newCount, 
accountId, ResourceOwnerType.Account, tag));
          }
  
 -        // No need to log message for primary and secondary storage because 
both are recalculating the
 +        // No need to log message for storage type resources because both are 
recalculating the
          // resource count which will not lead to any discrepancy.
 -        if (newCount != null && !newCount.equals(oldCount) &&
 -                type != Resource.ResourceType.primary_storage && type != 
Resource.ResourceType.secondary_storage) {
 +        if (newCount != null && !newCount.equals(oldCount) && 
!ResourceType.isStorageType(type)) {
-             logger.warn("Discrepancy in the resource count " + "(original 
count=" + oldCount + " correct count = " + newCount + ") for type " + type +
-                     " for account ID " + accountId + " is fixed during 
resource count recalculation.");
+             logger.warn("Discrepancy in the resource count (original count={} 
correct count = {}) for type {} for account ID {} is fixed during resource 
count recalculation.",
+                     oldCount, newCount, type, accountId);
          }
  
          return (newCount == null) ? 0 : newCount;
diff --cc server/src/main/java/com/cloud/server/ManagementServerImpl.java
index 21e8c2eac8b,271372bf656..b09a675aa58
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@@ -774,14 -744,14 +774,12 @@@ import com.cloud.network.dao.NetworkDao
  import com.cloud.network.dao.NetworkDomainDao;
  import com.cloud.network.dao.NetworkDomainVO;
  import com.cloud.network.dao.NetworkVO;
--import com.cloud.network.dao.PublicIpQuarantineDao;
  import com.cloud.network.vpc.dao.VpcDao;
  import com.cloud.org.Cluster;
  import com.cloud.org.Grouping.AllocationState;
  import com.cloud.projects.Project;
  import com.cloud.projects.Project.ListProjectResourcesCriteria;
  import com.cloud.projects.ProjectManager;
--import com.cloud.resource.ResourceManager;
  import com.cloud.server.ResourceTag.ResourceObjectType;
  import com.cloud.service.ServiceOfferingVO;
  import com.cloud.service.dao.ServiceOfferingDao;
@@@ -883,9 -854,6 +881,9 @@@ public class ManagementServerImpl exten
      static final ConfigKey<Integer> sshKeyLength = new 
ConfigKey<>("Advanced", Integer.class, "ssh.key.length", "2048", "Specifies 
custom SSH key length (bit)", true, ConfigKey.Scope.Global);
      static final ConfigKey<Boolean> humanReadableSizes = new 
ConfigKey<>("Advanced", Boolean.class, "display.human.readable.sizes", "true", 
"Enables outputting human readable byte sizes to logs and usage records.", 
false, ConfigKey.Scope.Global);
      public static final ConfigKey<String> customCsIdentifier = new 
ConfigKey<>("Advanced", String.class, "custom.cs.identifier", 
UUID.randomUUID().toString().split("-")[0].substring(4), "Custom identifier for 
the cloudstack installation", true, ConfigKey.Scope.Global);
-     public static final ConfigKey<Boolean> 
exposeCloudStackVersionInApiXmlResponse = new ConfigKey<Boolean>("Advanced", 
Boolean.class, "expose.cloudstack.version.api.xml.response", "true", "Indicates 
whether ACS version should appear in the root element of an API XML response.", 
true, ConfigKey.Scope.Global);
-     public static final ConfigKey<Boolean> 
exposeCloudStackVersionInApiListCapabilities = new 
ConfigKey<Boolean>("Advanced", Boolean.class, 
"expose.cloudstack.version.api.list.capabilities", "true", "Indicates whether 
ACS version should show in the listCapabilities API.", true, 
ConfigKey.Scope.Global);
++    public static final ConfigKey<Boolean> 
exposeCloudStackVersionInApiXmlResponse = new ConfigKey<>("Advanced", 
Boolean.class, "expose.cloudstack.version.api.xml.response", "true", "Indicates 
whether ACS version should appear in the root element of an API XML response.", 
true, ConfigKey.Scope.Global);
++    public static final ConfigKey<Boolean> 
exposeCloudStackVersionInApiListCapabilities = new ConfigKey<>("Advanced", 
Boolean.class, "expose.cloudstack.version.api.list.capabilities", "true", 
"Indicates whether ACS version should show in the listCapabilities API.", true, 
ConfigKey.Scope.Global);
 +
      private static final VirtualMachine.Type []systemVmTypes = { 
VirtualMachine.Type.SecondaryStorageVm, VirtualMachine.Type.ConsoleProxy};
      private static final List<HypervisorType> 
LIVE_MIGRATION_SUPPORTING_HYPERVISORS = List.of(HypervisorType.Hyperv, 
HypervisorType.KVM,
              HypervisorType.LXC, HypervisorType.Ovm, HypervisorType.Ovm3, 
HypervisorType.Simulator, HypervisorType.VMware, HypervisorType.XenServer);
@@@ -991,8 -955,8 +989,6 @@@
      @Inject
      private ProjectManager _projectMgr;
      @Inject
--    private ResourceManager _resourceMgr;
--    @Inject
      private HighAvailabilityManager _haMgr;
      @Inject
      private HostTagsDao _hostTagsDao;
@@@ -1048,12 -1012,10 +1044,8 @@@
      UserDataManager userDataManager;
      @Inject
      StoragePoolTagsDao storagePoolTagsDao;
 -
      @Inject
 -    private PublicIpQuarantineDao publicIpQuarantineDao;
 -
 +    protected ManagementServerJoinDao managementServerJoinDao;
- 
-     @Inject
-     private PublicIpQuarantineDao publicIpQuarantineDao;
- 
      @Inject
      ClusterManager _clusterMgr;
  
@@@ -1080,7 -1036,7 +1072,6 @@@
      private List<UserAuthenticator> _userAuthenticators;
      private List<UserTwoFactorAuthenticator> _userTwoFactorAuthenticators;
      private List<UserAuthenticator> _userPasswordEncoders;
--    protected boolean _executeInSequence;
  
      protected List<DeploymentPlanner> _planners;
  
@@@ -4750,7 -4528,7 +4741,8 @@@
          capabilities.put(ApiConstants.INSTANCES_STATS_USER_ONLY, 
StatsCollector.vmStatsCollectUserVMOnly.value());
          
capabilities.put(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_ENABLED, 
StatsCollector.vmDiskStatsRetentionEnabled.value());
          capabilities.put(ApiConstants.INSTANCES_DISKS_STATS_RETENTION_TIME, 
StatsCollector.vmDiskStatsMaxRetentionTime.value());
 +        capabilities.put(ApiConstants.INSTANCE_LEASE_ENABLED, 
VMLeaseManager.InstanceLeaseEnabled.value());
+         capabilities.put(ApiConstants.DYNAMIC_SCALING_ENABLED, 
UserVmManager.EnableDynamicallyScaleVm.value());
          if (apiLimitEnabled) {
              capabilities.put("apiLimitInterval", apiLimitInterval);
              capabilities.put("apiLimitMax", apiLimitMax);
diff --cc ui/public/locales/en.json
index e644acb4e07,79ea9bb9d8f..d3a44789489
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@@ -53,9 -53,7 +53,9 @@@
  "label.acquiring.ip": "Acquiring IP",
  "label.associated.resource": "Associated resource",
  "label.action": "Action",
 +"label.action.add.nodes.to.kubernetes.cluster": "Add nodes to Kubernetes 
cluster",
 +"label.action.remove.nodes.from.kubernetes.cluster": "Remove nodes from 
Kubernetes cluster",
- "label.action.attach.disk": "Attach disk",
+ "label.action.attach.disk": "Attach Disk",
  "label.action.attach.iso": "Attach ISO",
  "label.action.attach.to.instance": "Attach to Instance",
  "label.action.bulk.delete.egress.firewall.rules": "Bulk delete egress 
firewall rules",
@@@ -72,9 -70,8 +72,9 @@@
  "label.action.change.password": "Change password",
  "label.action.clear.webhook.deliveries": "Clear deliveries",
  "label.action.delete.webhook.deliveries": "Delete deliveries",
- "label.action.change.primary.storage.scope": "Change primary storage scope",
+ "label.action.change.primary.storage.scope": "Change Primary Storage scope",
  "label.action.configure.stickiness": "Stickiness",
 +"label.action.configure.storage.access.group": "Update storage access group",
  "label.action.copy.iso": "Copy ISO",
  "label.action.copy.snapshot": "Copy Snapshot",
  "label.action.copy.template": "Copy Template",
@@@ -84,14 -81,12 +84,14 @@@
  "label.action.create.volume.add": "Create and Add Volume",
  "label.action.delete.account": "Delete Account",
  "label.action.delete.backup.offering": "Delete backup offering",
- "label.action.delete.cluster": "Delete cluster",
- "label.action.delete.domain": "Delete domain",
- "label.action.delete.egress.firewall": "Delete egress firewall rule",
- "label.action.delete.firewall": "Delete firewall rule",
+ "label.action.delete.cluster": "Delete Cluster",
+ "label.action.delete.domain": "Delete Domain",
+ "label.action.delete.egress.firewall": "Delete Egress Firewall Rule",
+ "label.action.delete.firewall": "Delete Firewall Rule",
  "label.action.delete.interface.static.route": "Remove Tungsten Fabric 
interface static route",
 +"label.action.delete.gpu.card": "Delete GPU card",
  "label.action.delete.guest.os": "Delete guest OS",
 +"label.action.delete.guest.os.category": "Delete guest OS category",
  "label.action.delete.guest.os.hypervisor.mapping": "Delete guest OS 
hypervisor mapping",
  "label.action.delete.ip.range": "Delete IP range",
  "label.action.delete.iso": "Delete ISO",
@@@ -111,25 -106,24 +111,25 @@@
  "label.action.delete.template": "Delete Template",
  "label.action.delete.tungsten.router.table": "Remove Tungsten Fabric route 
table from Network",
  "label.action.delete.user": "Delete User",
 +"label.action.delete.vgpu.profile": "Delete vGPU profile",
- "label.action.delete.volume": "Delete volume",
- "label.action.delete.zone": "Delete zone",
+ "label.action.delete.volume": "Delete Volume",
+ "label.action.delete.zone": "Delete Zone",
  "label.action.destroy.instance": "Destroy Instance",
- "label.action.destroy.systemvm": "Destroy system VM",
- "label.action.destroy.volume": "Destroy volume",
- "label.action.detach.disk": "Detach disk",
+ "label.action.destroy.systemvm": "Destroy System VM",
+ "label.action.destroy.volume": "Destroy Volume",
+ "label.action.detach.disk": "Detach Disk",
  "label.action.detach.iso": "Detach ISO",
  "label.action.disable.account": "Disable Account",
- "label.action.disable.cluster": "Disable cluster",
- "label.action.disable.disk.offering": "Disable disk offering",
+ "label.action.disable.cluster": "Disable Cluster",
+ "label.action.disable.disk.offering": "Disable Disk Offering",
  "label.action.disable.physical.network": "Disable physical Network",
- "label.action.disable.pod": "Disable pod",
+ "label.action.disable.pod": "Disable Pod",
  "label.action.disable.role": "Disable Role",
  "label.action.disable.static.nat": "Disable static NAT",
- "label.action.disable.service.offering": "Disable service offering",
- "label.action.disable.system.service.offering": "Disable system service 
offering",
+ "label.action.disable.service.offering": "Disable Service Offering",
+ "label.action.disable.system.service.offering": "Disable System Service 
Offering",
  "label.action.disable.user": "Disable User",
- "label.action.disable.zone": "Disable zone",
+ "label.action.disable.zone": "Disable Zone",
  "label.action.download.iso": "Download ISO",
  "label.action.download.snapshot": "Download Snapshot",
  "label.action.download.template": "Download Template",
@@@ -226,19 -220,14 +226,19 @@@
  "label.action.unmanage.instance": "Unmanage Instance",
  "label.action.unmanage.instances": "Unmanage Instances",
  "label.action.unmanage.virtualmachine": "Unmanage Instance",
 +"label.action.update.cluster": "Update cluster",
 +"label.action.update.pod": "Update pod",
 +"label.action.update.zone": "Update zone",
 +"label.action.update.storage.pool": "Update storage pool",
  "label.action.unmanage.volume": "Unmanage Volume",
  "label.action.unmanage.volumes": "Unmanage Volumes",
 +"label.action.unregister.extension.resource": "Unregister extension resource",
- "label.action.update.host": "Update host",
+ "label.action.update.host": "Update Host",
  "label.action.update.security.groups": "Update security groups",
  "label.action.update.offering.access": "Update offering access",
  "label.action.update.resource.count": "Update resource count",
- "label.action.userdata.reset": "Reset Userdata",
 -"label.action.value": "Action/Value",
+ "label.action.user.data.reset": "Reset User Data",
 +"label.action.value": "Action/Value",
  "label.action.vmsnapshot.create": "Take Instance Snapshot",
  "label.action.vmsnapshot.delete": "Delete Instance Snapshot",
  "label.action.vmsnapshot.revert": "Revert to Instance Snapshot",
@@@ -259,44 -248,37 +259,43 @@@
  "label.add.by": "Add by",
  "label.add.certificate": "Add certificate",
  "label.add.ciscoasa1000v": "Add CiscoASA1000v resource",
- "label.add.cluster": "Add cluster",
- "label.add.compute.offering": "Add compute offering",
+ "label.add.cluster": "Add Cluster",
+ "label.add.compute.offering": "Add Compute Offering",
  "label.add.condition": "Add condition",
 +"label.add.custom.action": "Add Custom Action",
- "label.add.disk.offering": "Add disk offering",
- "label.add.domain": "Add domain",
- "label.add.egress.rule": "Add egress rule",
+ "label.add.disk.offering": "Add Disk Offering",
+ "label.add.domain": "Add Domain",
+ "label.add.egress.rule": "Add Egress Rule",
 +"label.add.external.details": "Add external details",
  "label.add.f5.device": "Add F5 device",
- "label.add.firewall": "Add firewall rule",
+ "label.add.firewall": "Add Firewall Rule",
  "label.add.firewallrule": "Add Firewall Rule",
 +"label.add.gpu.card": "Add GPU card",
 +"label.add.gpu.device": "Add GPU device",
- "label.add.guest.network": "Add guest Network",
- "label.add.guest.os": "Add guest OS",
 +"label.add.guest.os.category": "Add guest OS category",
- "label.add.guest.os.hypervisor.mapping": "Add guest OS hypervisor mapping",
- "label.add.host": "Add host",
- "label.add.ingress.rule": "Add ingress rule",
+ "label.add.guest.network": "Add Guest Network",
+ "label.add.guest.os": "Add Guest OS",
+ "label.add.guest.os.hypervisor.mapping": "Add guest os hypervisor mapping",
+ "label.add.host": "Add Host",
+ "label.add.ingress.rule": "Add Ingress Rule",
  "label.add.intermediate.certificate": "Add intermediate certificate",
  "label.add.internal.lb": "Add internal LB",
- "label.add.ip.range": "Add IP range",
- "label.add.ipv4.subnet": "Add IPv4 subnet for Routed networks",
+ "label.add.ip.range": "Add IP Range",
+ "label.add.ipv4.subnet": "Add IPv4 Subnet for Routed Networks",
  "label.add.ip.v6.prefix": "Add IPv6 prefix",
- "label.add.isolated.network": "Add isolated Network",
- "label.add.kubernetes.cluster": "Add Kubernetes cluster",
+ "label.add.isolated.network": "Add Isolated Network",
+ "label.add.kubernetes.cluster": "Add Kubernetes Cluster",
+ "label.add.acl.name": "ACL name",
  "label.add.ldap.account": "Add LDAP Account",
- "label.add.list.name": "ACL List name",
  "label.add.logical.router": "Add Logical Router to this Network",
  "label.add.more": "Add more",
 +"label.add.nodes": "Add Nodes to Kubernetes Cluster",
- "label.add.netscaler.device": "Add Netscaler device",
+ "label.add.netscaler.device": "Add Netscaler Device",
  "label.add.network": "Add Network",
  "label.add.network.acl": "Add Network ACL",
- "label.add.network.acl.list": "Add Network ACL list",
- "label.add.network.offering": "Add Network offering",
+ "label.add.network.offering": "Add Network Offering",
  "label.add.network.permission": "Add Network permission",
- "label.add.new.gateway": "Add new gateway",
+ "label.add.new.gateway": "Add new Gateway",
  "label.add.new.tier": "Add new Network Tier",
  "label.add.niciranvp.device": "Add Nvp controller",
  "label.add.note": "Add comment",
@@@ -343,17 -325,16 +342,17 @@@
  "label.add.user": "Add User",
  "label.add.upstream.ipv4.routes": "Add upstream IPv4 routes",
  "label.add.upstream.ipv6.routes": "Add upstream IPv6 routes",
 +"label.add.vgpu.profile": "Add profile",
  "label.add.vm": "Add Instance",
  "label.add.vms": "Add Instances",
- "label.add.vmware.datacenter": "Add VMware datacenter",
- "label.add.vnmc.device": "Add VNMC device",
+ "label.add.vmware.datacenter": "Add VMware Datacenter",
+ "label.add.vnmc.device": "Add VNMC Device",
  "label.add.vpc": "Add VPC",
- "label.add.vpc.offering": "Add VPC offering",
- "label.add.vpn.customer.gateway": "Add VPN customer gateway",
- "label.add.vpn.gateway": "Add VPN gateway",
+ "label.add.vpc.offering": "Add VPC Offering",
+ "label.add.vpn.customer.gateway": "Add VPN Customer Gateway",
+ "label.add.vpn.gateway": "Add VPN Gateway",
  "label.add.vpn.user": "Add VPN User",
- "label.add.zone": "Add zone",
+ "label.add.zone": "Add Zone",
  "label.adding": "Adding",
  "label.adding.user": "Adding User...",
  "label.address": "Address",
@@@ -448,16 -427,13 +447,16 @@@
  "label.backup.configure.schedule": "Configure Backup Schedule",
  "label.backup.offering.assign": "Assign Instance to backup offering",
  "label.backup.offering.remove": "Remove Instance from backup offering",
- "label.backup.offerings": "Backup offerings",
+ "label.backup.offerings": "Backup Offerings",
  "label.backup.repository": "Backup Repository",
  "label.backup.restore": "Restore Instance backup",
- "label.backupofferingid": "Backup offering",
- "label.backupofferingname": "Backup offering",
- "label.backup.repository.add": "Add backup repository",
- "label.backup.repository.remove": "Remove backup repository",
 -"label.backupofferingid": "Backup Offering",
 -"label.backupofferingname": "Backup Offering",
 +"label.backuplimit": "Backup Limits",
 +"label.backup.storage": "Backup Storage",
 +"label.backupstoragelimit": "Backup Storage Limits (GiB)",
++"label.backupofferingid": "Backup Offering ID",
++"label.backupofferingname": "Backup Offering Name",
+ "label.backup.repository.add": "Add Backup Repository",
+ "label.backup.repository.remove": "Remove Backup Repository",
  "label.balance": "Balance",
  "label.bandwidth": "Bandwidth",
  "label.baremetal.dhcp.devices": "Bare metal DHCP devices",
@@@ -486,14 -462,13 +485,14 @@@
  "label.brocade.vcs.address": "Vcs switch address",
  "label.browser": "Browser",
  "label.bucket": "Bucket",
 +"label.bucketlimit": "Bucket Limits",
  "label.by.account": "By Account",
- "label.by.domain": "By domain",
+ "label.by.domain": "By Domain",
  "label.by.level": "By level",
- "label.by.pod": "By pod",
+ "label.by.pod": "By Pod",
  "label.by.state": "By state",
  "label.by.type": "By type",
- "label.by.zone": "By zone",
+ "label.by.zone": "By Zone",
  "label.bypassvlanoverlapcheck": "Bypass VLAN id/range overlap",
  "label.cachemode": "Write-cache type",
  "label.cancel": "Cancel",
@@@ -630,8 -589,7 +629,8 @@@
  "label.copyid": "Copy ID",
  "label.copy.password": "Copy password",
  "label.core": "Core",
- "label.core.zone.type": "Core zone type",
+ "label.core.zone.type": "Core Zone type",
 +"label.count": "Count",
  "label.counter": "Counter",
  "label.counter.name": "Name of the counter for which the policy will be 
evaluated",
  "label.cpu": "CPU",
@@@ -650,11 -608,10 +649,11 @@@
  "label.cpuused": "CPU utilized",
  "label.cpuusedghz": "CPU used",
  "label.create": "Create",
- "label.create.instance": "Create cloud server",
+ "label.create.instance": "Create Cloud Server",
  "label.create.account": "Create Account",
  "label.create.asnrange": "Create AS Range",
- "label.create.backup": "Start backup",
+ "label.create.backup": "Start Backup",
 +"label.create.extension": "Create Extension",
  "label.create.sharedfs": "Create Shared FileSystem",
  "label.create.network": "Create new Network",
  "label.create.nfs.secondary.staging.storage": "Create NFS secondary staging 
storage",
@@@ -699,10 -653,8 +698,10 @@@
  "label.daily": "Daily",
  "label.dark.mode": "Dark mode",
  "label.dashboard": "Dashboard",
- "label.data.disk": "Data disk",
+ "label.data.disk": "Data Disk",
+ "label.data.disk.offering": "Data Disk Offering",
 +"label.data.pool": "Data pool",
 +"label.data.pool.description": "Data pool is required when using a Ceph pool 
with erasure code",
- "label.data.disk.offering": "Data disk offering",
  "label.date": "Date",
  "label.datetime.filter.period": "From <b>{startDate}</b> to <b>{endDate}</b>",
  "label.datetime.filter.starting": "Starting <b>{startDate}</b>.",
@@@ -844,20 -793,17 +843,20 @@@
  "label.directdownload": "Direct download",
  "label.direction": "Direction",
  "label.disable.autoscale.vmgroup": "Disable AutoScaling Group",
 +"label.disable.custom.action": "Disable Custom Action",
 +"label.disable.extension": "Disable Extension",
  "label.disable.host": "Disable host",
- "label.disable.network.offering": "Disable Network offering",
+ "label.disable.network.offering": "Disable Network Offering",
  "label.disable.provider": "Disable provider",
  "label.disable.storage": "Disable storage pool",
- "label.disable.vpc.offering": "Disable VPC offering",
+ "label.disable.vpc.offering": "Disable VPC Offering",
  "label.disable.vpn": "Disable remote access VPN",
  "label.disable.webhook": "Disable Webhook",
  "label.disabled": "Disabled",
  "label.disconnected": "Last disconnected",
 +"label.discover.gpu.devices": "Discover GPU devices",
  "label.disk": "Disk",
- "label.disk.offerings": "Disk offerings",
+ "label.disk.offerings": "Disk Offerings",
  "label.disk.path": "Disk Path",
  "label.disk.tooltip": "Disk Image filename in the selected Storage Pool",
  "label.disk.selection": "Disk selection",
@@@ -959,14 -905,12 +958,14 @@@
  "label.elastic": "Elastic",
  "label.email": "Email",
  "label.enable.autoscale.vmgroup": "Enable AutoScaling Group",
 +"label.enable.custom.action": "Enable Custom Action",
 +"label.enable.extension": "Enable Extension",
  "label.enable.host": "Enable Host",
- "label.enable.network.offering": "Enable Network offering",
+ "label.enable.network.offering": "Enable Network Offering",
  "label.enable.oauth": "Enable OAuth Login",
  "label.enable.provider": "Enable provider",
- "label.enable.storage": "Enable storage pool",
- "label.enable.vpc.offering": "Enable VPC offering",
+ "label.enable.storage": "Enable Storage Pool",
+ "label.enable.vpc.offering": "Enable VPC Offering",
  "label.enable.vpn": "Enable remote access VPN",
  "label.enable.webhook": "Enable Webhook",
  "label.enabled": "Enabled",
@@@ -1066,9 -1000,8 +1065,9 @@@
  "label.firstname": "First name",
  "label.firstname.lower": "firstname",
  "label.fix.errors": "Fix errors",
- "label.fixed": "Fixed offering",
+ "label.fixed": "Fixed Offering",
  "label.for": "for",
 +"label.forcks": "For CKS",
  "label.forbidden": "Forbidden",
  "label.forced": "Force",
  "label.force.ms.to.import.vm.files": "Enable to force OVF Download via 
Management Server. Disable to use KVM Host ovftool (if installed)",
@@@ -1216,17 -1126,14 +1215,17 @@@
  "label.ikelifetime": "IKE lifetime (second)",
  "label.ikepolicy": "IKE policy",
  "label.ikeversion": "IKE version",
 +"label.image": "Image",
 +"label.image.type": "Image type",
  "label.images": "Images",
  "label.imagestoreid": "Secondary Storage",
- "label.import.backup.offering": "Import backup offering",
+ "label.import.backup.offering": "Import Backup Offering",
  "label.import.instance": "Import Instance",
- "label.import.offering": "Import offering",
- "label.import.role": "Import role",
+ "label.import.offering": "Import Offering",
+ "label.import.role": "Import Role",
  "label.import.volume": "Import Volume",
  "label.inactive": "Inactive",
 +"label.inbuilt": "Inbuilt",
  "label.in.progress": "in progress",
  "label.in.progress.for": "in progress for",
  "label.info": "Info",
@@@ -1366,18 -1272,16 +1365,18 @@@
  "label.keyboardtype": "Keyboard type",
  "label.keypair": "SSH key pair",
  "label.keypairs": "SSH key pair(s)",
- "label.kubeconfig.cluster": "Kubernetes cluster config",
+ "label.kubeconfig.cluster": "Kubernetes Cluster config",
  "label.kubernetes": "Kubernetes",
- "label.kubernetes.access.details": "The kubernetes nodes can be accessed via 
ssh using: <br> <code><b> ssh -i [ssh_key] -p [port_number] 
cloud@[public_ip_address] </b></code> <br><br> where, <br> 
<code><b>ssh_key:</b></code> points to the ssh private key file corresponding 
to the key that was associated while creating the Kubernetes cluster. If no ssh 
key was provided during Kubernetes cluster creation, use the ssh private key of 
the management server. <br> <code><b>port_number:</b></co [...]
- "label.kubernetes.cluster": "Kubernetes cluster",
+ "label.kubernetes.access.details": "The kubernetes nodes can be accessed via 
ssh using: <br> <code><b> ssh -i [ssh_key] -p [port_number] 
cloud@[public_ip_address] </b></code> <br><br> where, <br> 
<code><b>ssh_key:</b></code> points to the ssh private key file corresponding 
to the key that was associated while creating the Kubernetes Cluster. If no ssh 
key was provided during Kubernetes cluster creation, use the ssh private key of 
the management server. <br> <code><b>port_number:</b></co [...]
 +"label.kubernetes.cluster.add.nodes.to.cluster": "Add nodes to Kubernetes 
cluster",
- "label.kubernetes.cluster.create": "Create Kubernetes cluster",
- "label.kubernetes.cluster.delete": "Delete Kubernetes cluster",
- "label.kubernetes.cluster.scale": "Scale Kubernetes cluster",
- "label.kubernetes.cluster.start": "Start Kubernetes cluster",
- "label.kubernetes.cluster.stop": "Stop Kubernetes cluster",
 +"label.kubernetes.cluster.remove.nodes.from.cluster": "Remove nodes from 
Kubernetes cluster",
- "label.kubernetes.cluster.upgrade": "Upgrade Kubernetes cluster",
+ "label.kubernetes.cluster": "Kubernetes Cluster",
+ "label.kubernetes.cluster.create": "Create Kubernetes Cluster",
+ "label.kubernetes.cluster.delete": "Delete Kubernetes Cluster",
+ "label.kubernetes.cluster.scale": "Scale Kubernetes Cluster",
+ "label.kubernetes.cluster.start": "Start Kubernetes Cluster",
+ "label.kubernetes.cluster.stop": "Stop Kubernetes Cluster",
+ "label.kubernetes.cluster.upgrade": "Upgrade Kubernetes Cluster",
  "label.kubernetes.dashboard": "Kubernetes dashboard UI",
  "label.kubernetes.dashboard.create.token": "Create token for Kubernetes 
dashboard",
  "label.kubernetes.dashboard.create.token.desc": "Since Kubernetes v1.24.0, 
there is no auto-generation of secret-based service Account token due to 
security reason. You need to create a service Account and an optional 
long-lived Bearer Token for the service Account.",
@@@ -1422,9 -1326,8 +1421,9 @@@
  "label.lbprovider": "Load balancer provider",
  "label.lbruleid": "Load balancer ID",
  "label.lbtype": "Load balancer type",
 +"label.ldap": "LDAP",
- "label.ldap.configuration": "LDAP configuration",
- "label.ldap.group.name": "LDAP group",
+ "label.ldap.configuration": "LDAP Configuration",
+ "label.ldap.group.name": "LDAP Group",
  "label.level": "Level",
  "label.license.agreements": "License agreements",
  "label.limit": "Limit",
@@@ -1501,29 -1400,23 +1500,29 @@@
  "label.maxcpunumber": "Max CPU cores",
  "label.maxdatavolumeslimit": "Max data volumes limit",
  "label.maxerrorretry": "Max error retry",
 +"label.maxgpu": "Max. GPUs",
  "label.maxguestslimit": "Max guest limit",
 +"label.maxheads": "Max. heads",
- "label.maxhostspercluster": "Max hosts per cluster",
+ "label.maxhostspercluster": "Max hosts per Cluster",
  "label.maximum": "Maximum",
  "label.maxinstance": "Max Instances",
  "label.maxiops": "Max IOPS",
  "label.maxmembers": "Max members",
  "label.maxmemory": "Max. memory (MiB)",
  "label.maxnetwork": "Max. Networks",
 +"label.maxobjectstorage": "Max. Object Storage (GiB)",
  "label.maxprimarystorage": "Max. primary storage (GiB)",
- "label.maxproject": "Max. projects",
+ "label.maxproject": "Max. Projects",
  "label.maxpublicip": "Max. public IPs",
 +"label.maxresolutionx": "Max. resolution X",
 +"label.maxresolutiony": "Max. resolution Y",
  "label.maxsecondarystorage": "Max. secondary storage (GiB)",
  "label.maxsize": "Maximum size",
  "label.maxsnapshot": "Max. Snapshots",
  "label.maxtemplate": "Max. Templates",
  "label.maxuservm": "Max. User Instances",
- "label.maxvolume": "Max. volumes",
 +"label.maxvgpuperphysicalgpu": "Max. vGPUs per physical GPU",
+ "label.maxvolume": "Max. Volumes",
  "label.maxvpc": "Max. VPCs",
  "label.may.continue": "You may now continue.",
  "label.mb.memory": "MB memory",
@@@ -1804,10 -1675,8 +1803,10 @@@
  "label.password.tooltip": "The password for the Host",
  "label.passwordenabled": "Password enabled",
  "label.path": "Path",
 +"label.pathready": "Path Ready",
 +"label.pathstate": "Path State",
  "label.patp": "Palo Alto threat profile",
- "label.pavr": "Virtual router",
+ "label.pavr": "Virtual Router",
  "label.payload": "Payload",
  "label.payloadurl": "Payload URL",
  "label.pcidevice": "GPU",
@@@ -1819,9 -1688,8 +1818,9 @@@
  "label.peerstate": "Peer State",
  "label.peerstate.lastupdated": "Peer State Updated Time",
  "label.pending.jobs": "Pending Jobs",
 +"label.pendingjobscount": "Number Of pending jobs",
  "label.per.account": "Per Account",
- "label.per.zone": "Per zone",
+ "label.per.zone": "Per Zone",
  "label.percentage": "Percentage",
  "label.perfectforwardsecrecy": "Perfect forward secrecy",
  "label.perform.fresh.checks": "Perform fresh checks",
@@@ -2001,7 -1863,7 +2000,8 @@@
  "label.register.oauth": "Register OAuth",
  "label.register.template": "Register Template",
  "label.register.user.data": "Register User Data",
 +"label.register.cni.config": "Register CNI Configuration",
+ "label.register.user.data.details": "Enter the User Data in plain text or in 
Base64 encoding. Up to 32KB of Base64 encoded User Data can be sent by default. 
The setting vm.userdata.max.length can be used to increase the limit to upto 
1MB.",
  "label.reinstall.vm": "Reinstall Instance",
  "label.reject": "Reject",
  "label.related": "Related",
@@@ -2027,9 -1888,8 +2027,9 @@@
  "label.remove.ldap": "Remove LDAP",
  "label.remove.logical.network": "Remove Network from logical router",
  "label.remove.logical.router": "Remove logical router",
- "label.remove.network.offering": "Remove Network offering",
+ "label.remove.network.offering": "Remove Network Offering",
  "label.remove.network.route.table": "Remove Tungsten Fabric Network routing 
table",
 +"label.remove.nodes": "Remove nodes from Kubernetes cluster",
  "label.remove.pf": "Remove port forwarding rule",
  "label.remove.policy": "Remove policy",
  "label.remove.project.account": "Remove Account from project",
@@@ -2066,10 -1925,9 +2065,10 @@@
  "label.reset.config.value": "Reset to default value",
  "label.reset.ssh.key.pair": "Reset SSH key pair",
  "label.reset.to.default": "Reset to default",
- "label.reset.userdata.on.autoscale.vm.group": "Reset Userdata on AutoScale VM 
Group",
- "label.reset.userdata.on.vm": "Reset Userdata on Instance",
+ "label.reset.user.data.on.autoscale.vm.group": "Reset User Data on AutoScale 
VM Group",
+ "label.reset.user.data.on.vm": "Reset User Data on Instance",
  "label.reset.vpn.connection": "Reset VPN connection",
 +"label.resolution": "Resolution",
  "label.resource": "Resource",
  "label.resource.limit.exceeded": "Resource limit exceeded",
  "label.resource.name": "Resource name",
@@@ -2177,9 -2032,7 +2176,9 @@@
  "label.see.more.info.disk.usage": "See more info about disk usage",
  "label.see.more.info.shown.charts": "See more info about the shown charts",
  "label.select-view": "Select view",
- "label.select.a.zone": "Select a zone",
 +"label.select.all": "Select all",
 +"label.select.columns": "Select columns",
+ "label.select.a.zone": "Select a Zone",
  "label.select.deployment.infrastructure": "Select deployment infrastructure",
  "label.select.guest.os.type": "Please select the guest OS type",
  "label.select.network": "Select Network",
@@@ -2201,20 -2054,16 +2200,20 @@@
  "label.sequence": "Sequence",
  "label.server": "Server",
  "label.server.certificate": "Server certificate",
- "label.serviceip": "Service IP",
++"label.serviceip": "Management Service IP",
  "label.service.connectivity.distributedroutercapabilitycheckbox": 
"Distributed router",
  "label.service.connectivity.regionlevelvpccapabilitycheckbox": "Region level 
VPC",
- "label.service.group": "Service group",
- "label.serviceip": "Management IP",
+ "label.service.group": "Service Group",
 -"label.serviceip": "Management IP",
++"label.management.ip": "Management IP",
  "label.service.lb.elasticlbcheckbox": "Elastic LB",
  "label.service.lb.inlinemodedropdown": "Mode",
  "label.service.lb.lbisolationdropdown": "LB isolation",
  "label.service.lb.netscaler.servicepackages": "Netscaler service packages",
  "label.service.lb.netscaler.servicepackages.description": "Service package 
description",
- "label.service.offering": "Service offering",
 +"label.service.offering.controlnodes": "Compute offering for Control Nodes",
 +"label.service.offering.etcdnodes": "Compute offering for etcd Nodes",
 +"label.service.offering.workernodes": "Compute offering for Worker Nodes",
+ "label.service.offering": "Service Offering",
  "label.service.staticnat.associatepublicip": "Associate public IP",
  "label.service.staticnat.elasticipcheckbox": "Elastic IP",
  "label.servicegroupuuid": "Service Group",
@@@ -2291,25 -2138,21 +2290,26 @@@
  "label.srctaguuid": "Source Tag",
  "label.srx": "SRX",
  "label.srx.firewall": "Juniper SRX firewall",
- "label.ssh.key.pairs": "SSH key pairs",
 +"label.storageaccessgroups": "Storage Access Groups",
 +"label.clusterstorageaccessgroups": "Cluster Storage Access Groups",
 +"label.podstorageaccessgroups": "Pod Storage Access Groups",
 +"label.zonestorageaccessgroups": "Zone Storage Access Groups",
+ "label.ssh.key.pairs": "SSH Key Pairs",
  "label.uefi.supported": "UEFI supported",
 +"label.unregister.extension": "Unregister Extension",
  "label.usediops": "IOPS used",
- "label.userdataid": "Userdata ID",
- "label.userdataname": "Userdata name",
- "label.userdatadetails": "Userdata details",
- "label.userdataparams": "Userdata parameters",
- "label.userdatapolicy": "Userdata link policy",
- "label.userdata.text": "Manual Userdata entry",
- "label.userdata.registered": "Stored Userdata",
- "label.userdata.do.override": "Userdata override",
- "label.userdata.do.append": "Userdata append",
- "label.userdatapolicy.tooltip": "Userdata linked to the Template can be 
overridden by Userdata provided during Instance deploy. Select the override 
policy as required.",
+ "label.user.data.id": "User Data ID",
+ "label.user.data.name": "User Data name",
+ "label.user.data.details": "User Data details",
+ "label.user.data.params": "User Data parameters",
+ "label.user.data.policy": "User Data link policy",
+ "label.user.data.text": "Manual User Data entry",
+ "label.user.data.registered": "Stored User Data",
+ "label.user.data.do.override": "User Data override",
+ "label.user.data.do.append": "User Data append",
+ "label.user.data.policy.tooltip": "User Data linked to the Template can be 
overridden by User Data provided during Instance deploy. Select the override 
policy as required.",
  "label.user.data": "User Data",
+ "label.user.data.library": "User Data Library",
  "label.ssh.port": "SSH port",
  "label.sshkeypair": "New SSH key pair",
  "label.sshkeypairs": "SSH key pairs",
@@@ -2596,14 -2433,12 +2598,12 @@@
  "label.usagetypedescription": "Usage description",
  "label.use.kubectl.access.cluster": "<code><b>kubectl</b></code> and 
<code><b>kubeconfig</b></code> file to access cluster",
  "label.use.local.timezone": "Use local timezone",
 +"label.use.router.ip.resolver": "Use Virtual Router IP as resolver",
  "label.used": "Used",
  "label.usehttps": "Use HTTPS",
- "label.usenewdiskoffering": "Replace disk offering?",
+ "label.usenewdiskoffering": "Replace Disk Offering?",
  "label.user": "User",
  "label.user.conflict": "Conflict",
- "label.userdata": "Userdata",
- "label.userdatal2": "User data",
 -"label.user.data": "User Data",
  "label.username": "Username",
  "label.username.tooltip": "The Username for the Host",
  "label.users": "Users",
@@@ -2716,10 -2542,8 +2716,10 @@@
  "label.vnf.templates": "VNF templates",
  "label.vnf.template.register": "Register VNF template",
  "label.vnmc": "VNMC",
- "label.volgroup": "Volume group",
+ "label.volgroup": "Volume Group",
  "label.volume": "Volume",
 +"label.vms.empty": "No VMs available to be added to the Kubernetes cluster",
 +"label.vms.remove.empty": "No external VMs present in the Kubernetes cluster 
to be removed",
  "label.volume.empty": "No data volumes attached to this Instance",
  "label.volume.encryption.support": "Volume Encryption Supported",
  "label.volume.metrics": "Volume Metrics",
@@@ -2737,10 -2561,9 +2737,10 @@@
  "label.volumetype": "Volume Type",
  "label.vpc": "VPC",
  "label.vpcs": "VPCs",
 +"label.vpc.gateway.ip": "VPC Gateway IP",
  "label.vpc.id": "VPC ID",
- "label.vpc.offerings": "VPC offerings",
- "label.vpc.virtual.router": "VPC virtual router",
+ "label.vpc.offerings": "VPC Offerings",
+ "label.vpc.virtual.router": "VPC Virtual Router",
  "label.vpc.restart.required": "VPC restart required",
  "label.vpcid": "VPC",
  "label.vpclimit": "VPC limits",
@@@ -2752,8 -2575,7 +2752,8 @@@
  "label.vpn.gateway": "VPN gateway",
  "label.vpn.users": "VPN Users",
  "label.vpncustomergateway": "IP address of the remote gateway",
- "label.vpncustomergatewayid": "VPN customer gateway",
 +"label.vramsize": "VRAM Size",
+ "label.vpncustomergatewayid": "VPN Customer Gateway",
  "label.vsmipaddress": "Nexus 1000v IP address",
  "label.vsmpassword": "Nexus 1000v password",
  "label.vsmusername": "Nexus 1000v username",
@@@ -2810,9 -2631,9 +2810,11 @@@
  "label.oobm.username": "Out-of-band management username",
  "label.bucket.update": "Update Bucket",
  "label.bucket.delete": "Delete Bucket",
 +"label.quotagib": "Quota in GiB",
+ "label.quotagb": "Quota in GB",
+ "label.edgecluster": "Edge Cluster",
  "label.encryption": "Encryption",
 +"label.etcdnodes": "Number of etcd nodes",
  "label.versioning": "Versioning",
  "label.objectlocking": "Object Lock",
  "label.bucket.policy": "Bucket Policy",
@@@ -2839,10 -2648,8 +2841,10 @@@
  "message.action.delete.autoscale.vmgroup": "Please confirm that you want to 
delete this autoscaling group.",
  "message.action.delete.backup.offering": "Please confirm that you want to 
delete this backup offering?",
  "message.action.delete.backup.repository": "Please confirm that you want to 
delete this backup repository?",
- "message.action.delete.cluster": "Please confirm that you want to delete this 
cluster.",
+ "message.action.delete.cluster": "Please confirm that you want to delete this 
Cluster.",
 +"message.action.delete.custom.action": "Please confirm that you want to 
delete this custom action.",
  "message.action.delete.domain": "Please confirm that you want to delete this 
domain.",
 +"message.action.delete.extension": "Please confirm that you want to delete 
the extension",
  "message.action.delete.external.firewall": "Please confirm that you would 
like to remove this external firewall. Warning: If you are planning to add back 
the same external firewall, you must reset usage data on the device.",
  "message.action.delete.external.load.balancer": "Please confirm that you 
would like to remove this external load balancer. Warning: If you are planning 
to add back the same external load balancer, you must reset usage data on the 
device.",
  "message.action.delete.ingress.rule": "Please confirm that you want to delete 
this ingress rule.",
@@@ -2866,10 -2671,9 +2868,10 @@@
  "message.action.delete.snapshot": "Please confirm that you want to delete 
this Snapshot.",
  "message.action.delete.template": "Please confirm that you want to delete 
this Template.",
  "message.action.delete.tungsten.router.table": "Please confirm that you want 
to remove Route Table from this Network?",
 +"message.action.delete.vgpu.profile": "Please confirm that you want to delete 
this vGPU profile.",
  "message.action.delete.volume": "Please confirm that you want to delete this 
volume. Note: this will not delete any Snapshots of this volume.",
  "message.action.delete.vpn.user": "Please confirm that you want to delete the 
VPN user.",
- "message.action.delete.zone": "Please confirm that you want to delete this 
zone.",
+ "message.action.delete.zone": "Please confirm that you want to delete this 
Zone.",
  "message.action.destroy.sharedfs": "Please confirm that you want to destroy 
this Shared FileSystem.<br><b>Caution: This will delete all the data of the 
Shared FileSystem as well.</b>",
  "message.action.destroy.instance": "Please confirm that you want to destroy 
the Instance.",
  "message.action.destroy.instance.with.backups": "Please confirm that you want 
to destroy the Instance. There may be backups associated with the Instance 
which will not be deleted.",
@@@ -2882,10 -2686,9 +2884,10 @@@
  "message.action.disable.service.offering": "Please confirm that you want to 
disable this service offering.",
  "message.action.disable.system.service.offering": "Please confirm that you 
want to disable this system service offering.",
  "message.action.disable.physical.network": "Please confirm that you want to 
disable this physical Network.",
- "message.action.disable.pod": "Please confirm that you want to disable this 
pod.",
+ "message.action.disable.pod": "Please confirm that you want to disable this 
Pod.",
  "message.action.disable.static.nat": "Please confirm that you want to disable 
static NAT.",
- "message.action.disable.zone": "Please confirm that you want to disable this 
zone.",
+ "message.action.disable.zone": "Please confirm that you want to disable this 
Zone.",
 +"message.action.discover.gpu.devices": "Please confirm that you want to 
discover GPU devices.",
  "message.action.download.iso": "Please confirm that you want to download this 
ISO.",
  "message.action.download.snapshot": "Please confirm that you want to download 
this Snapshot.",
  "message.action.download.template": "Please confirm that you want to download 
this Template.",
@@@ -2975,11 -2772,10 +2977,11 @@@
  "message.remove.ip.v6.firewall.rule.failed": "Failed to remove IPv6 firewall 
rule",
  "message.remove.ip.v6.firewall.rule.processing": "Removing IPv6 firewall 
rule...",
  "message.remove.ip.v6.firewall.rule.success": "Removed IPv6 firewall rule",
 +"message.add.netris.controller": "Add Netris Provider",
  "message.add.nsx.controller": "Add NSX Provider",
- "message.add.network": "Add a new network for zone: <b><span 
id=\"zone_name\"></span></b>",
- "message.add.network.acl.failed": "Adding network ACL list failed.",
- "message.add.network.acl.processing": "Adding network ACL list...",
+ "message.add.network": "Add a new network for Zone: <b><span 
id=\"zone_name\"></span></b>",
+ "message.add.network.acl.failed": "Adding network ACL failed.",
+ "message.add.network.acl.processing": "Adding network ACL...",
  "message.add.network.failed": "Adding network failed.",
  "message.add.network.processing": "Adding network...",
  "message.add.new.gateway.to.vpc": "Please specify the information to add a 
new gateway to this VPC.",
@@@ -3216,31 -2997,27 +3218,31 @@@
  "message.deployasis": "Selected Template is Deploy As-Is i.e., the Instance 
is deployed by importing an OVA with vApps directly into vCenter. Root disk(s) 
resize is allowed only on stopped Instances for such Templates.",
  "message.desc.advanced.zone": "This is recommended and allows more 
sophisticated Network topologies. This Network model provides the most 
flexibility in defining guest Networks and providing custom Network offerings 
such as firewall, VPN, or load balancer support.",
  "message.desc.basic.zone": "Provide a single Network where each Instance is 
assigned an IP directly from the Network. Guest isolation can be provided 
through layer-3 means such as security groups (IP address source filtering).",
- "message.desc.core.zone": "Core Zones are intended for Datacenter based 
deployments and allow the full range of Networking and other functionality in 
Apache CloudStack. Core zones have a number of prerequisites and rely on the 
presence of shared storage and helper Instances.",
- "message.desc.edge.zone": "Edge Zones are lightweight zones, designed for 
deploying in edge computing scenarios. They are limited in functionality but 
have far fewer prerequisites than core zones.<br><br>Please refer to the Apache 
CloudStack documentation for more information on Zone Types<br><a 
href='http://docs.cloudstack.apache.org/en/latest/installguide/configuration.html#adding-a-zone'>http://docs.cloudstack.apache.org/en/latest/installguide/configuration.html#adding-a-zone</a>",
- "message.desc.cluster": "Each pod must contain one or more clusters. We will 
add the first cluster now. A cluster provides a way to group hosts. The hosts 
in a cluster all have identical hardware, run the same hypervisor, are on the 
same subnet, and access the same shared storage. Each cluster consists of one 
or more hosts and one or more primary storage servers.",
+ "message.desc.core.zone": "Core Zones are intended for Datacenter based 
deployments and allow the full range of Networking and other functionality in 
Apache CloudStack. Core Zones have a number of prerequisites and rely on the 
presence of shared storage and helper Instances.",
+ "message.desc.edge.zone": "Edge Zones are lightweight Zones, designed for 
deploying in edge computing scenarios. They are limited in functionality but 
have far fewer prerequisites than core zones.<br><br>Please refer to the Apache 
CloudStack documentation for more information on Zone Types<br><a 
href='http://docs.cloudstack.apache.org/en/latest/installguide/configuration.html#adding-a-zone'>http://docs.cloudstack.apache.org/en/latest/installguide/configuration.html#adding-a-zone</a>",
+ "message.desc.cluster": "Each Pod must contain one or more Clusters. We will 
add the first cluster now. A cluster provides a way to group hosts. The hosts 
in a cluster all have identical hardware, run the same hypervisor, are on the 
same subnet, and access the same shared storage. Each cluster consists of one 
or more hosts and one or more primary storage servers.",
  "message.desc.create.ssh.key.pair": "Please fill in the following data to 
create or register a ssh key pair.<br><br>(1) If public key is set, CloudStack 
will register the public key. You can use it through your private 
key.<br><br>(2) If public key is not set, CloudStack will create a new SSH key 
pair. In this case, please copy and save the private key. CloudStack will not 
keep it.<br>",
  "message.desc.created.ssh.key.pair": "Created a SSH key pair.",
- "message.desc.host": "Each cluster must contain at least one host (computer) 
for guest Instances to run on. We will add the first host now. For a host to 
function in CloudStack, you must install hypervisor software on the host, 
assign an IP address to the host, and ensure the host is connected to the 
CloudStack management server.<br/><br/>Give the host's DNS or IP address, the 
user name (usually root) and password, and any labels you use to categorize 
hosts.",
- "message.desc.importingestinstancewizard": "This feature only applies to 
libvirt based KVM instances. Only Stopped instances can be ingested",
+ "message.desc.host": "Each Cluster must contain at least one host (computer) 
for guest Instances to run on. We will add the first host now. For a host to 
function in CloudStack, you must install hypervisor software on the host, 
assign an IP address to the host, and ensure the host is connected to the 
CloudStack management server.<br/><br/>Give the host's DNS or IP address, the 
user name (usually root) and password, and any labels you use to categorize 
hosts.",
 -"message.desc.importingestinstancewizard": "This feature only applies to 
libvirt based KVM instances. Only Stopped instances can be ingested",
  "message.desc.import.ext.kvm.wizard": "Import libvirt domain from External 
KVM Host not managed by CloudStack",
  "message.desc.import.local.kvm.wizard": "Import QCOW2 image from Local 
Storage of selected KVM Host",
  "message.desc.import.shared.kvm.wizard": "Import QCOW2 image from selected 
Primary Storage Pool",
  "message.desc.import.unmanage.volume": "Please choose a storage pool that you 
want to import or unmanage volumes. The storage pool should be in Up status. 
<br>This feature only supports KVM.",
  "message.desc.importexportinstancewizard": "By choosing to manage an 
Instance, CloudStack takes over the orchestration of that Instance. Unmanaging 
an Instance removes CloudStack ability to manage it. In both cases, the 
Instance is left running and no changes are done to the VM on the 
hypervisor.<br><br>For KVM, managing a VM is an experimental feature.",
- "message.desc.importmigratefromvmwarewizard": "By selecting an existing or 
external VMware Datacenter and an instance to import, CloudStack migrates the 
selected instance from VMware to KVM on a conversion host using virt-v2v and 
imports it into a KVM cluster",
- "message.desc.primary.storage": "Each cluster must contain one or more 
primary storage servers. We will add the first one now. Primary storage 
contains the disk volumes for all the Instances running on hosts in the 
cluster. Use any standards-compliant protocol that is supported by the 
underlying hypervisor.",
++"message.desc.importingestinstancewizard": "This feature only applies to 
libvirt based KVM instances. Only Stopped instances can be ingested",
+ "message.desc.importmigratefromvmwarewizard": "By selecting an existing or 
external VMware Datacenter and an instance to import, CloudStack migrates the 
selected instance from VMware to KVM on a conversion host using virt-v2v and 
imports it into a KVM Cluster",
+ "message.desc.primary.storage": "Each Cluster must contain one or more 
primary storage servers. We will add the first one now. Primary storage 
contains the disk volumes for all the Instances running on hosts in the 
cluster. Use any standards-compliant protocol that is supported by the 
underlying hypervisor.",
 -"message.desc.reset.ssh.key.pair": "Please specify a ssh key pair that you 
would like to add to this Instance.",
 -"message.desc.secondary.storage": "Each Zone must have at least one NFS or 
secondary storage server. We will add the first one now. Secondary storage 
stores Instance Templates, ISO images, and Instance disk volume Snapshots. This 
server must be available to all hosts in the zone.<br/><br/>Provide the IP 
address and exported path.",
 +"message.desc.register.template": "Hosted on download.cloudstack.org, these 
templates can be easily registered directly within CloudStack. Simply click 
<strong>Register Template</strong> for the templates you wish to use.",
- "message.desc.reset.ssh.key.pair": "Please specify a ssh key pair that you 
would like to add to this Instance.",
- "message.desc.secondary.storage": "Each zone must have at least one NFS or 
secondary storage server. We will add the first one now. Secondary storage 
stores Instance Templates, ISO images, and Instance disk volume Snapshots. This 
server must be available to all hosts in the zone.<br/><br/>Provide the IP 
address and exported path.",
- "message.desc.register.user.data": "Please fill in the following data to 
register a User data.",
 +"message.desc.register.cni.config": "Please fill in the following data to 
register CNI Configuration as user data.",
+ "message.desc.register.user.data": "Please fill in the following to register 
new User Data.",
  "message.desc.registered.user.data": "Registered a User Data.",
++"message.desc.reset.ssh.key.pair": "Please specify a ssh key pair that you 
would like to add to this Instance.",
++"message.desc.secondary.storage": "Each Zone must have at least one NFS or 
secondary storage server. We will add the first one now. Secondary storage 
stores Instance Templates, ISO images, and Instance disk volume Snapshots. This 
server must be available to all hosts in the zone.<br/><br/>Provide the IP 
address and exported path.",
 +"message.desc.validationformat": "Specifies the format used to validate the 
parameter value, such as EMAIL, URL, UUID, DECIMAL, etc.",
 +"message.desc.valueoptions": "Provide a comma-separated list of values that 
will appear as selectable options for this parameter",
- "message.desc.zone": "A zone is the largest organizational unit in 
CloudStack, and it typically corresponds to a single datacenter. Zones provide 
physical isolation and redundancy. A zone consists of one or more pods (each of 
which contains hosts and primary storage servers) and a secondary storage 
server which is shared by all pods in the zone.",
- "message.desc.zone.edge": "A zone is the largest organizational unit in 
CloudStack, and it typically corresponds to a single datacenter. Zones provide 
physical isolation and redundancy. An edge zone consists of one or more hosts 
(each of which provides local storage as primary storage servers). Only shared 
and L2 Networks can be deployed in such zones and functionalities that require 
secondary storages are not supported.",
- "message.drs.plan.description": "The maximum number of live migrations 
allowed for DRS. Configure DRS under the settings tab before generating a plan 
or to enable automatic DRS for the cluster.",
+ "message.desc.zone": "A Zone is the largest organizational unit in 
CloudStack, and it typically corresponds to a single datacenter. Zones provide 
physical isolation and redundancy. A zone consists of one or more Pods (each of 
which contains hosts and primary storage servers) and a secondary storage 
server which is shared by all pods in the zone.",
+ "message.desc.zone.edge": "A Zone is the largest organizational unit in 
CloudStack, and it typically corresponds to a single datacenter. Zones provide 
physical isolation and redundancy. An edge zone consists of one or more hosts 
(each of which provides local storage as primary storage servers). Only shared 
and L2 Networks can be deployed in such zones and functionalities that require 
secondary storages are not supported.",
+ "message.drs.plan.description": "The maximum number of live migrations 
allowed for DRS. Configure DRS under the settings tab before generating a plan 
or to enable automatic DRS for the Cluster.",
  "message.drs.plan.executed": "DRS plan executed successfully.",
  "message.zone.edge.local.storage": "Local storage will be used by default for 
User Instances and virtual routers",
  "message.detach.disk": "Are you sure you want to detach this disk?",
@@@ -3459,9 -3233,8 +3462,9 @@@
  "message.import.volume": "Please specify the domain, account or project name. 
<br>If not set, the volume will be imported for the caller.",
  "message.info.cloudian.console": "Cloudian Management Console should open in 
another window.",
  "message.installwizard.cloudstack.helptext.website": " * Project website:\t ",
 +"message.infra.setup.netris.description": "This zone must contain a Netris 
provider because the isolation method is Netris",
- "message.infra.setup.nsx.description": "This zone must contain an NSX 
provider because the isolation method is NSX",
- "message.infra.setup.tungsten.description": "This zone must contain a 
Tungsten-Fabric provider because the isolation method is TF",
+ "message.infra.setup.nsx.description": "This Zone must contain an NSX 
provider because the isolation method is NSX",
+ "message.infra.setup.tungsten.description": "This Zone must contain a 
Tungsten-Fabric provider because the isolation method is TF",
  "message.installwizard.cloudstack.helptext.document": " * Documentation:\t ",
  "message.installwizard.cloudstack.helptext.header": "\nYou can find more 
information about Apache CloudStack™ on the pages listed below.\n",
  "message.installwizard.cloudstack.helptext.issues": " * Report issues:\t ",
@@@ -3469,21 -3242,14 +3472,21 @@@
  "message.installwizard.cloudstack.helptext.releasenotes": " * Release 
notes:\t ",
  "message.installwizard.cloudstack.helptext.survey": " * Take the survey:\t ",
  "message.installwizard.copy.whatiscloudstack": "CloudStack™ is a software 
platform that pools computing resources to build public, private, and hybrid 
Infrastructure as a Service (IaaS) clouds. CloudStack™ manages the Network, 
storage, and compute nodes that make up a cloud infrastructure. Use CloudStack™ 
to deploy, manage, and configure cloud computing environments.\n\nExtending 
beyond individual Instance images running on commodity hardware, CloudStack™ 
provides a turnkey cloud infras [...]
- "message.installwizard.tooltip.addpod.name": "A name for the pod.",
+ "message.installwizard.tooltip.addpod.name": "A name for the Pod.",
  "message.installwizard.tooltip.addpod.reservedsystemendip": "This is the IP 
range in the private Network that the CloudStack uses to manage Secondary 
Storage VMs and Console Proxy VMs. These IP addresses are taken from the same 
subnet as computing servers.",
- "message.installwizard.tooltip.addpod.reservedsystemgateway": "The gateway 
for the hosts in that pod.",
+ "message.installwizard.tooltip.addpod.reservedsystemgateway": "The gateway 
for the hosts in that Pod.",
  "message.installwizard.tooltip.addpod.reservedsystemstartip": "This is the IP 
range in the private Network that the CloudStack uses to manage Secondary 
Storage VMs and Console Proxy VMs. These IP addresses are taken from the same 
subnet as computing servers.",
- "message.installwizard.tooltip.configureguesttraffic.guestendip": "The range 
of IP addresses that will be available for allocation to guests in this zone. 
If one NIC is used, these IPs should be in the same CIDR as the pod CIDR.",
+ "message.installwizard.tooltip.configureguesttraffic.guestendip": "The range 
of IP addresses that will be available for allocation to guests in this Zone. 
If one NIC is used, these IPs should be in the same CIDR as the Pod CIDR.",
  "message.installwizard.tooltip.configureguesttraffic.guestgateway": "The 
gateway that the guests should use.",
  "message.installwizard.tooltip.configureguesttraffic.guestnetmask": "The 
netmask in use on the subnet that the guests should use.",
- "message.installwizard.tooltip.configureguesttraffic.gueststartip": "The 
range of IP addresses that will be available for allocation to guests in this 
zone. If one NIC is used, these IPs should be in the same CIDR as the pod 
CIDR.",
+ "message.installwizard.tooltip.configureguesttraffic.gueststartip": "The 
range of IP addresses that will be available for allocation to guests in this 
Zone. If one NIC is used, these IPs should be in the same CIDR as the Pod 
CIDR.",
 +"message.installwizard.tooltip.netris.provider.name": "Netris Provider name 
is required",
 +"message.installwizard.tooltip.netris.provider.url": "Netris Provider URL not 
provided",
 +"message.installwizard.tooltip.netris.provider.username": "Netris Provider 
username not provided",
 +"message.installwizard.tooltip.netris.provider.password": "Netris Provider 
password not provided",
 +"message.installwizard.tooltip.netris.provider.site": "Netris Provider Site 
name not provided",
 +"message.installwizard.tooltip.netris.provider.tag": "Netris Tag to be 
assigned to vNets",
 +"message.installwizard.tooltip.netris.provider.tenant.name": "Netris Provider 
Admin Tenant name not provided",
  "message.installwizard.tooltip.nsx.provider.hostname": "NSX Provider hostname 
/ IP address not provided",
  "message.installwizard.tooltip.nsx.provider.username": "NSX Provider username 
not provided",
  "message.installwizard.tooltip.nsx.provider.password": "NSX Provider password 
not provided",
@@@ -3506,16 -3271,14 +3509,16 @@@
  "message.ip.v6.prefix.delete": "IPv6 prefix deleted",
  "message.iso.arch": "Please select an ISO architecture",
  "message.iso.desc": "Disc image containing data or bootable media for OS.",
- "message.kubeconfig.cluster.not.available": "Kubernetes cluster kubeconfig 
not available currently.",
 -"message.kubeconfig.cluster.not.available": "Kubernetes Cluster kubeconfig 
not available currently.",
 +"message.kubernetes.cluster.add.nodes": "Please confirm that you want to add 
the following nodes to the cluster",
- "message.kubernetes.cluster.delete": "Please confirm that you want to destroy 
the cluster.",
- "message.kubernetes.cluster.scale": "Please select desired cluster 
configuration.",
- "message.kubernetes.cluster.start": "Please confirm that you want to start 
the cluster.",
- "message.kubernetes.cluster.stop": "Please confirm that you want to stop the 
cluster.",
+ "message.kubernetes.cluster.delete": "Please confirm that you want to destroy 
the Cluster.",
++"message.kubeconfig.cluster.not.available": "Kubernetes Cluster kubeconfig 
not available currently.",
 +"message.kubernetes.cluster.remove.nodes": "Please confirm that you want to 
remove the following nodes from the cluster",
+ "message.kubernetes.cluster.scale": "Please select desired Cluster 
configuration.",
+ "message.kubernetes.cluster.start": "Please confirm that you want to start 
the Cluster.",
+ "message.kubernetes.cluster.stop": "Please confirm that you want to stop the 
Cluster.",
  "message.kubernetes.cluster.upgrade": "Please select new Kubernetes version.",
  "message.kubernetes.version.delete": "Please confirm that you want to delete 
this Kubernetes version.",
- "message.l2.network.unsupported.for.nsx": "L2 networks aren't supported for 
NSX enabled zones",
+ "message.l2.network.unsupported.for.nsx": "L2 networks aren't supported for 
NSX enabled Zones",
  "message.launch.zone": "Zone is ready to launch; please proceed to the next 
step.",
  "message.launch.zone.description": "Zone is ready to launch; please proceed 
to the next step.",
  "message.launch.zone.hint": "Configure Network components and traffic 
including IP addresses.",
@@@ -3540,8 -3303,8 +3543,9 @@@
  "message.lock.user": "Please confirm that you want to lock the User 
\"{user}\". By locking this User, they will no longer be able to manage their 
cloud resources. Existing resources can still be accessed.",
  "message.lock.user.success": "Successfully locked User \"{user}\"",
  "message.login.failed": "Login Failed",
- "message.migrate.instance.host.auto.assign": "Host for the Instance will be 
automatically chosen based on the suitability within the same cluster",
- "message.migrate.instance.to.host": "Please confirm that you want to migrate 
this Instance to another host. When migration is between hosts of different 
clusters volume(s) of the Instance may get migrated to suitable storage pools.",
++"message.maintenance.initiated": "Maintenance has been initiated. This 
Management Server will not accept new jobs",
+ "message.migrate.instance.host.auto.assign": "Host for the Instance will be 
automatically chosen based on the suitability within the same Cluster",
+ "message.migrate.instance.to.host": "Please confirm that you want to migrate 
this Instance to another host. When migration is between hosts of different 
Clusters volume(s) of the Instance may get migrated to suitable storage pools.",
  "message.migrate.instance.to.ps": "Please confirm that you want to migrate 
this Instance to another primary storage.",
  "message.migrate.resource.to.ss": "Please confirm that you want to migrate 
this resource to another secondary storage.",
  "message.migrate.router.confirm": "Please confirm the host you wish to 
migrate the router to:",
@@@ -3590,17 -3353,15 +3594,17 @@@
  "message.password.reset.success": "Password has been reset successfully. 
Please login using your new credentials.",
  "message.path": "Path : ",
  "message.path.description": "NFS: exported path from the server. VMFS: 
/datacenter name/datastore name. SharedMountPoint: path where primary storage 
is mounted, such as /mnt/primary.",
 +"message.please.confirm.remove.cni.configuration": "Please confirm that you 
want to remove this CNI Configuration",
  "message.please.confirm.remove.ssh.key.pair": "Please confirm that you want 
to remove this SSH key pair.",
- "message.please.confirm.remove.user.data": "Please confirm that you want to 
remove this Userdata",
+ "message.please.confirm.remove.user.data": "Please confirm that you want to 
remove this User Data",
  "message.please.enter.valid.value": "Please enter a valid value.",
  "message.please.enter.value": "Please enter values.",
  "message.please.wait.while.autoscale.vmgroup.is.being.created": "Please wait 
while your AutoScaling Group is being created; this may take a while...",
- "message.please.wait.while.zone.is.being.created": "Please wait while your 
zone is being created; this may take a while...",
+ "message.please.wait.while.zone.is.being.created": "Please wait while your 
Zone is being created; this may take a while...",
  "message.pod.dedicated": "Pod dedicated.",
  "message.pod.dedication.released": "Pod dedication released.",
 -"message.prepare.for.shutdown": "Please confirm that you would like to prep 
this Management server for shutdown. It will not accept any new Async Jobs but 
will NOT terminate after there are no pending jobs.",
 +"message.prepare.for.shutdown": "Please confirm that you would like to 
prepare this Management Server for shutdown. It will not accept any new Async 
Jobs but will NOT terminate after there are no pending jobs.",
 +"message.prepare.for.maintenance": "Please confirm that you would like to 
prepare this Management Server for maintenance. It will not accept any new 
Async Jobs.",
  "message.primary.storage.invalid.state": "Primary storage is not in Up state",
  "message.processing.complete": "Processing complete!",
  "message.protocol.description": "For XenServer, choose NFS, iSCSI, or 
PreSetup. For KVM, choose NFS, SharedMountPoint, RDB, CLVM or Gluster. For 
vSphere, choose NFS, PreSetup (VMFS or iSCSI or FiberChannel or vSAN or vVols) 
or DatastoreCluster. For Hyper-V, choose SMB/CIFS. For LXC, choose NFS or 
SharedMountPoint. For OVM, choose NFS or OCFS2.",
@@@ -3716,11 -3474,9 +3719,11 @@@
  "message.success.add.kuberversion": "Successfully added Kubernetes version",
  "message.success.add.logical.router": "Successfully added Logical Router",
  "message.success.add.network": "Successfully added Network",
- "message.success.add.network.acl": "Successfully added Network ACL list",
+ "message.success.add.network.acl": "Successfully added Network ACL",
  "message.success.add.network.static.route": "Successfully added Network 
Static Route",
  "message.success.add.network.permissions": "Successfully added Network 
permissions",
 +"message.success.add.nodes.to.cluster": "Successfully added nodes to 
Kubernetes cluster",
 +"message.success.remove.nodes.from.cluster": "Successfully removed nodes from 
Kubernetes cluster",
  "message.success.add.physical.network": "Successfully added Physical Network",
  "message.success.add.object.storage": "Successfully added Object Storage",
  "message.success.add.policy.rule": "Successfully added Policy rule",
@@@ -3834,11 -3583,9 +3837,11 @@@
  "message.success.remove.tungsten.routing.policy": "Successfully removed 
Tungsten-Fabric Routing Policy from Network",
  "message.success.reset.network.permissions": "Successfully reset Network 
Permissions",
  "message.success.resize.volume": "Successfully resized volume",
- "message.success.scale.kubernetes": "Successfully scaled Kubernetes cluster",
+ "message.success.scale.kubernetes": "Successfully scaled Kubernetes Cluster",
 +"message.success.unmanage.gpu.devices": "Successfully unmanaged GPU 
device(s)",
  "message.success.unmanage.instance": "Successfully unmanaged Instance",
  "message.success.unmanage.volume": "Successfully unmanaged Volume",
 +"message.success.unregister.extension": "Successfull unregistered Extension",
  "message.success.update.account": "Successfully updated Account",
  "message.success.update.bgp.peer": "Successfully updated BGP peer",
  "message.success.update.bucket": "Successfully updated bucket",
@@@ -3973,15 -3714,13 +3976,15 @@@
  "message.warn.change.primary.storage.scope": "This feature is tested and 
supported for the following configurations:<br>KVM - NFS/Ceph - 
DefaultPrimary<br>VMware - NFS - DefaultPrimary<br>*There might be extra steps 
involved to make it work for other configurations.",
  "message.warn.filetype": "jpg, jpeg, png, bmp and svg are the only supported 
image formats.",
  "message.warn.importing.instance.without.nic": "WARNING: This Instance is 
being imported without NICs and many Network resources will not be available. 
Consider creating a NIC via vCenter before importing or as soon as the Instance 
is imported.",
 +"message.warn.select.template": "Please select a Template for Registration.",
- "message.warn.zone.mtu.update": "Please note that this limit won't affect 
pre-existing Network’s MTU settings",
+ "message.warn.zone.mtu.update": "Please note that this limit won't affect 
pre-existing Network's MTU settings",
  "message.webhook.deliveries.time.filter": "Webhook deliveries list can be 
filtered based on date-time. Select 'Custom' for specifying start and end date 
range.",
  "message.zone.creation.complete": "Zone creation complete.",
- "message.zone.detail.description": "Populate zone details.",
- "message.zone.detail.hint": "A zone is the largest organizational unit in 
CloudStack, and it typically corresponds to a single datacenter. Zones provide 
physical isolation and redundancy. A zone consists of one or more pods (each of 
which contains hosts and primary storage servers) and a secondary storage 
server which is shared by all pods in the zone.",
+ "message.zone.detail.description": "Populate Zone details.",
+ "message.zone.detail.hint": "A Zone is the largest organizational unit in 
CloudStack, and it typically corresponds to a single datacenter. Zones provide 
physical isolation and redundancy. A zone consists of one or more Pods (each of 
which contains hosts and primary storage servers) and a secondary storage 
server which is shared by all pods in the zone.",
  "message.validate.min": "Please enter a value greater than or equal to {0}.",
  "message.action.delete.object.storage": "Please confirm that you want to 
delete this Object Store",
 +"message.action.unregister.extension.resource": "Please confirm that you want 
to unregister extension with this resource",
  "message.bgp.peers.null": "Please note, if no BGP peers are selected, the VR 
will connect to <br> (1) dedicated BGP peers the owner can access, if the owner 
has dedicated BGP peers and account setting use.system.bgp.peers is set to 
false; <br> (2) all BGP peers the owner can access, otherwise.<br>",
  "message.bucket.delete": "Please confirm that you want to delete this Bucket",
  "migrate.from": "Migrate from",
diff --cc ui/src/components/view/InfoCard.vue
index 449172112a0,f1efcaef281..6cfd243c438
--- a/ui/src/components/view/InfoCard.vue
+++ b/ui/src/components/view/InfoCard.vue
@@@ -31,11 -31,14 +31,14 @@@
                      <edit-outlined class="upload-icon"/>
                    </div>
                    <slot name="avatar">
 -                    <span v-if="(resource.icon && resource.icon.base64image 
|| images.template || images.iso || resourceIcon) && !['router', 'systemvm', 
'volume'].includes($route.path.split('/')[1])">
 -                      <resource-icon :image="getImage(resource.icon && 
resource.icon.base64image || images.template || images.iso || resourceIcon)" 
size="4x" style="margin-right: 5px"/>
 +                    <span v-if="resourceIcon && !['router', 'systemvm', 
'volume'].includes($route.path.split('/')[1])">
 +                      <resource-icon :image="resourceIcon" size="4x" 
style="margin-right: 5px"/>
                      </span>
+                     <span v-else-if="resource.vmtype === 'sharedfsvm'">
+                       <file-text-outlined style="font-size: 36px;" />
+                     </span>
                      <span v-else>
 -                      <os-logo v-if="resource.ostypeid || resource.ostypename 
|| ['guestoscategory'].includes($route.path.split('/')[1])" 
:osId="resource.ostypeid" :osName="resource.ostypename || resource.name" 
size="3x" @update-osname="setResourceOsType"/>
 +                      <os-logo v-if="resource.ostypeid || resource.ostypename 
|| ['guestoscategory'].includes($route.path.split('/')[1])" 
:osId="resource.ostypeid" :osName="resource.ostypename || 
resource.osdisplayname || resource.name" size="3x" />
                        <render-icon v-else-if="typeof $route.meta.icon 
==='string'" style="font-size: 36px" :icon="$route.meta.icon" />
                        <font-awesome-icon
                          v-else-if="$route.meta.icon && 
Array.isArray($route.meta.icon)"
@@@ -975,7 -891,7 +990,8 @@@ import UploadResourceIcon from '@/compo
  import eventBus from '@/config/eventBus'
  import ResourceIcon from '@/components/view/ResourceIcon'
  import ResourceLabel from '@/components/widgets/ResourceLabel'
 +import ImageDeployInstanceButton from 
'@/components/view/ImageDeployInstanceButton'
+ import { FileTextOutlined } from '@ant-design/icons-vue'
  
  export default {
    name: 'InfoCard',
@@@ -988,7 -904,7 +1004,8 @@@
      UploadResourceIcon,
      ResourceIcon,
      ResourceLabel,
-     ImageDeployInstanceButton
++    ImageDeployInstanceButton,
+     FileTextOutlined
    },
    props: {
      resource: {
diff --cc ui/src/components/view/ListView.vue
index e5d843d4ce4,eca99dc0346..205e340652d
--- a/ui/src/components/view/ListView.vue
+++ b/ui/src/components/view/ListView.vue
@@@ -50,24 -40,16 +50,22 @@@
      </template>
      <template #bodyCell="{ column, text, record }">
        <template v-if="['name', 'provider'].includes(column.key) ">
 -        <span v-if="['vm', 'vnfapp'].includes($route.path.split('/')[1])" 
style="margin-right: 5px">
 +        <span
 +          v-if="['vm', 'vnfapp'].includes($route.path.split('/')[1])"
 +          style="margin-right: 5px"
 +        >
            <span v-if="record.icon && record.icon.base64image">
 -            <resource-icon :image="record.icon.base64image" size="2x"/>
 +            <resource-icon
 +              :image="record.icon.base64image"
 +              size="2x"
 +            />
            </span>
-           <os-logo
-             v-else
-             :osId="record.ostypeid"
-             :osName="record.osdisplayname"
-             size="xl"
-           />
+           <span v-else-if="record.vmtype === 'sharedfsvm'">
+             <file-text-outlined style="font-size: 18px;" />
+           </span>
+           <os-logo v-else :osId="record.ostypeid" 
:osName="record.osdisplayname" size="xl" />
          </span>
 -        <span style="min-width: 120px" >
 +        <span style="min-width: 120px">
            <QuickView
              style="margin-left: 5px"
              :actions="actions"
diff --cc ui/src/config/section/storage.js
index bcd48944497,12d1d547a90..5c98a81579e
--- a/ui/src/config/section/storage.js
+++ b/ui/src/config/section/storage.js
@@@ -39,7 -39,7 +39,7 @@@ export default 
          }
        },
        columns: () => {
-         const fields = ['name', 'state', 'sizegb', 'type', 'vmname', 
'vmstate']
 -        const fields = ['name', 'state', 'size', 'type', 'vmname']
++        const fields = ['name', 'state', 'size', 'type', 'vmname', 'vmstate']
          const metricsFields = ['diskkbsread', 'diskkbswrite', 'diskiopstotal']
  
          if (store.getters.userInfo.roletype === 'Admin') {
diff --cc ui/src/views/compute/DeployVM.vue
index 10c6a555a60,a604fe68fe4..674fe89f7fa
--- a/ui/src/views/compute/DeployVM.vue
+++ b/ui/src/views/compute/DeployVM.vue
@@@ -588,44 -603,16 +588,44 @@@
                            @change="val => { dynamicscalingenabled = val }"/>
                        </a-form-item>
                      </a-form-item>
 +                    <a-form-item name="showLeaseOptions" 
ref="showLeaseOptions" v-if="isLeaseFeatureEnabled">
 +                      <template #label>
 +                        <tooltip-label :title="$t('label.lease.enable')" 
:tooltip="$t('label.lease.enable.tooltip')"/>
 +                      </template>
 +                      <a-switch v-model:checked="showLeaseOptions" 
@change="onToggleLeaseData"/>
 +                    </a-form-item>
 +                    <a-row :gutter="12" v-if="isLeaseFeatureEnabled && 
showLeaseOptions">
 +                      <a-col :md="12" :lg="12">
 +                        <a-form-item name="leaseduration" ref="leaseduration">
 +                          <template #label>
 +                            <tooltip-label :title="$t('label.leaseduration')" 
/>
 +                          </template>
 +                          <a-input
 +                            v-model:value="form.leaseduration"
 +                            
:placeholder="$t('label.instance.lease.placeholder')"/>
 +                        </a-form-item>
 +                      </a-col>
 +                      <a-col :md="12" :lg="12">
 +                        <a-form-item name="leaseexpiryaction" 
ref="leaseexpiryaction">
 +                          <template #label>
 +                            <tooltip-label 
:title="$t('label.leaseexpiryaction')"  />
 +                          </template>
 +                          <a-select v-model:value="form.leaseexpiryaction" 
:defaultValue="leaseexpiryaction">
 +                            <a-select-option v-for="action in expiryActions" 
:key="action" :label="action" />
 +                          </a-select>
 +                        </a-form-item>
 +                      </a-col>
 +                    </a-row>
-                     <a-form-item :label="$t('label.userdata')">
+                     <a-form-item :label="$t('label.user.data')">
                        <a-card>
                          <div v-if="this.template && this.template.userdataid">
 -                          <a-text type="primary">
 -                              Userdata "{{ $t(this.template.userdataname) }}" 
is linked with template "{{ $t(this.template.name) }}" with override policy "{{ 
$t(this.template.userdatapolicy) }}"
 -                          </a-text><br/><br/>
 +                          <a-typography-text>
 +                            Userdata "{{ $t(this.template.userdataname) }}" 
is linked with template "{{ $t(this.template.name) }}" with override policy "{{ 
$t(this.template.userdatapolicy) }}"
 +                          </a-typography-text><br/><br/>
                            <div v-if="templateUserDataParams.length > 0 && 
!doUserdataOverride">
 -                            <a-text type="primary" v-if="this.template && 
this.template.userdataid && templateUserDataParams.length > 0">
 -                                Enter the values for the variables in userdata
 -                            </a-text>
 +                            <a-typography-text v-if="this.template && 
this.template.userdataid && templateUserDataParams.length > 0">
 +                              Enter the values for the variables in userdata
 +                            </a-typography-text>
                              <a-input-group>
                                <a-table
                                  size="small"
@@@ -2525,68 -2349,66 +2524,77 @@@ export default 
        })
      },
      fetchOptions (param, name, exclude) {
 -      if (exclude && exclude.length > 0) {
 -        if (exclude.includes(name)) {
 -          return
 +      return new Promise((resolve, reject) => {
 +        if (exclude && exclude.length > 0 && exclude.includes(name)) {
 +          return resolve(null)
          }
-         this.loading[name] = true
-         param.loading = true
-         param.opts = []
-         const options = param.options || {}
-         if (!('listall' in options) && !['zones', 'pods', 'clusters', 
'hosts', 'dynamicScalingVmConfig', 'hypervisors'].includes(name)) {
-           options.listall = true
-         }
-         postAPI(param.list, options).then((response) => {
-           param.loading = false
-           _.map(response, (responseItem, responseKey) => {
-             if (Object.keys(responseItem).length === 0) {
-               this.rowCount[name] = 0
-               this.options[name] = []
-               return resolve(null)
+       }
+       this.loading[name] = true
+       param.loading = true
+       param.opts = []
+       const options = param.options || {}
+       if (!('listall' in options) && !['zones', 'pods', 'clusters', 'hosts', 
'hypervisors'].includes(name)) {
+         options.listall = true
+       }
 -      api(param.list, options).then((response) => {
++      postApi(param.list, options).then((response) => {
+         param.loading = false
+         _.map(response, (responseItem, responseKey) => {
+           if (Object.keys(responseItem).length === 0) {
+             this.rowCount[name] = 0
+             this.options[name] = []
+             return
+           }
+           if (!responseKey.includes('response')) {
+             return
+           }
+           _.map(responseItem, (response, key) => {
+             if (key === 'count') {
+               this.rowCount[name] = response
+               return
              }
 -            param.opts = response
 -            this.options[name] = response
 -
 -            if (name === 'hypervisors') {
 -              const hypervisorFromResponse = response[0] && response[0].name 
? response[0].name : null
 -              this.dataPreFill.hypervisor = hypervisorFromResponse
 -              this.form.hypervisor = hypervisorFromResponse
 +            if (!responseKey.includes('response')) {
 +              return resolve(null)
              }
 +            _.map(responseItem, (response, key) => {
 +              if (key === 'count') {
 +                this.rowCount[name] = response
 +                return
 +              }
 +              param.opts = response
 +              this.options[name] = response
  
 -            if (param.field) {
 -              this.fillValue(param.field)
 -            }
 -          })
 +              if (name === 'hypervisors') {
 +                const hypervisorFromResponse = response[0] && 
response[0].name ? response[0].name : null
 +                this.dataPreFill.hypervisor = hypervisorFromResponse
 +                this.form.hypervisor = hypervisorFromResponse
 +              }
  
 -          if (name === 'zones') {
 -            let zoneid = ''
 -            if (this.$route.query.zoneid) {
 -              zoneid = this.$route.query.zoneid
 -            } else if (this.options.zones.length === 1) {
 -              zoneid = this.options.zones[0].id
 -            }
 -            if (zoneid) {
 -              this.form.zoneid = zoneid
 -              this.onSelectZoneId(zoneid)
 +              if (param.field) {
 +                this.fillValue(param.field)
 +              }
 +            })
 +
 +            if (name === 'zones') {
 +              let zoneid = ''
 +              if (this.$route.query.zoneid) {
 +                zoneid = this.$route.query.zoneid
 +              } else if (this.options.zones.length === 1) {
 +                zoneid = this.options.zones[0].id
 +              }
 +              if (zoneid) {
 +                this.form.zoneid = zoneid
 +                this.onSelectZoneId(zoneid)
 +              }
              }
 -          }
 +          })
 +          resolve(response)
 +        }).catch(function (error) {
 +          console.log(error.stack)
 +          param.loading = false
 +          reject(error)
 +        }).finally(() => {
 +          this.loading[name] = false
          })
 -      }).catch(function (error) {
 -        console.log(error.stack)
 -        param.loading = false
 -      }).finally(() => {
 -        this.loading[name] = false
        })
      },
      fetchTemplates (templateFilter, params) {
diff --cc ui/src/views/compute/DeployVnfAppliance.vue
index 06c41728a41,1117413d710..fd5a04d9b70
--- a/ui/src/views/compute/DeployVnfAppliance.vue
+++ b/ui/src/views/compute/DeployVnfAppliance.vue
@@@ -550,16 -564,16 +550,16 @@@
                            @change="val => { dynamicscalingenabled = val }"/>
                        </a-form-item>
                      </a-form-item>
-                     <a-form-item :label="$t('label.userdata')">
+                     <a-form-item :label="$t('label.user.data')">
                        <a-card>
                          <div v-if="this.template && this.template.userdataid">
 -                          <a-text type="primary">
 +                          <a-typography-text>
                                Userdata "{{ $t(this.template.userdataname) }}" 
is linked with template "{{ $t(this.template.name) }}" with override policy "{{ 
$t(this.template.userdatapolicy) }}"
 -                          </a-text><br/><br/>
 +                          </a-typography-text><br/><br/>
                            <div v-if="templateUserDataParams.length > 0 && 
!doUserdataOverride">
 -                            <a-text type="primary" v-if="this.template && 
this.template.userdataid && templateUserDataParams.length > 0">
 +                            <a-typography-text v-if="this.template && 
this.template.userdataid && templateUserDataParams.length > 0">
                                  Enter the values for the variables in userdata
 -                            </a-text>
 +                            </a-typography-text>
                              <a-input-group>
                                <a-table
                                  size="small"
@@@ -1289,18 -1277,20 +1288,19 @@@ export default 
          key: 'templateid',
          tab: this.$t('label.templates')
        }]
 -      return tabList
      },
      userdataTabList () {
-       return [
-         {
-           key: 'userdataregistered',
-           tab: this.$t('label.userdata.registered')
-         },
-         {
-           key: 'userdatatext',
-           tab: this.$t('label.userdata.text')
-         }
-       ]
+       let tabList = []
+       tabList = [{
+         key: 'userdataregistered',
+         tab: this.$t('label.user.data.registered')
+       },
+       {
+         key: 'userdatatext',
+         tab: this.$t('label.user.data.text')
+       }]
+ 
+       return tabList
      },
      showVnfNicsSection () {
        return this.networks && this.networks.length > 0 && this.vm.templateid 
&& this.templateVnfNics && this.templateVnfNics.length > 0
@@@ -2468,68 -2386,66 +2468,77 @@@
        })
      },
      fetchOptions (param, name, exclude) {
 -      if (exclude && exclude.length > 0) {
 -        if (exclude.includes(name)) {
 -          return
 +      return new Promise((resolve, reject) => {
 +        if (exclude && exclude.length > 0 && exclude.includes(name)) {
 +          return resolve(null)
          }
-         this.loading[name] = true
-         param.loading = true
-         param.opts = []
-         const options = param.options || {}
-         if (!('listall' in options) && !['zones', 'pods', 'clusters', 
'hosts', 'dynamicScalingVmConfig', 'hypervisors'].includes(name)) {
-           options.listall = true
-         }
-         postAPI(param.list, options).then((response) => {
-           param.loading = false
-           _.map(response, (responseItem, responseKey) => {
-             if (Object.keys(responseItem).length === 0) {
-               this.rowCount[name] = 0
-               this.options[name] = []
-               return resolve(null)
+       }
+       this.loading[name] = true
+       param.loading = true
+       param.opts = []
+       const options = param.options || {}
+       if (!('listall' in options) && !['zones', 'pods', 'clusters', 'hosts', 
'hypervisors'].includes(name)) {
+         options.listall = true
+       }
 -      api(param.list, options).then((response) => {
++      postApi(param.list, options).then((response) => {
+         param.loading = false
+         _.map(response, (responseItem, responseKey) => {
+           if (Object.keys(responseItem).length === 0) {
+             this.rowCount[name] = 0
+             this.options[name] = []
+             return
+           }
+           if (!responseKey.includes('response')) {
+             return
+           }
+           _.map(responseItem, (response, key) => {
+             if (key === 'count') {
+               this.rowCount[name] = response
+               return
              }
 -            param.opts = response
 -            this.options[name] = response
 -
 -            if (name === 'hypervisors') {
 -              const hypervisorFromResponse = response[0] && response[0].name 
? response[0].name : null
 -              this.dataPreFill.hypervisor = hypervisorFromResponse
 -              this.form.hypervisor = hypervisorFromResponse
 +            if (!responseKey.includes('response')) {
 +              return resolve(null)
              }
 +            _.map(responseItem, (response, key) => {
 +              if (key === 'count') {
 +                this.rowCount[name] = response
 +                return
 +              }
 +              param.opts = response
 +              this.options[name] = response
  
 -            if (param.field) {
 -              this.fillValue(param.field)
 -            }
 -          })
 +              if (name === 'hypervisors') {
 +                const hypervisorFromResponse = response[0] && 
response[0].name ? response[0].name : null
 +                this.dataPreFill.hypervisor = hypervisorFromResponse
 +                this.form.hypervisor = hypervisorFromResponse
 +              }
  
 -          if (name === 'zones') {
 -            let zoneid = ''
 -            if (this.$route.query.zoneid) {
 -              zoneid = this.$route.query.zoneid
 -            } else if (this.options.zones.length === 1) {
 -              zoneid = this.options.zones[0].id
 -            }
 -            if (zoneid) {
 -              this.form.zoneid = zoneid
 -              this.onSelectZoneId(zoneid)
 +              if (param.field) {
 +                this.fillValue(param.field)
 +              }
 +            })
 +
 +            if (name === 'zones') {
 +              let zoneid = ''
 +              if (this.$route.query.zoneid) {
 +                zoneid = this.$route.query.zoneid
 +              } else if (this.options.zones.length === 1) {
 +                zoneid = this.options.zones[0].id
 +              }
 +              if (zoneid) {
 +                this.form.zoneid = zoneid
 +                this.onSelectZoneId(zoneid)
 +              }
              }
 -          }
 +          })
 +          resolve(response)
 +        }).catch(function (error) {
 +          console.log(error.stack)
 +          param.loading = false
 +          reject(error)
 +        }).finally(() => {
 +          this.loading[name] = false
          })
 -      }).catch(function (error) {
 -        console.log(error.stack)
 -        param.loading = false
 -      }).finally(() => {
 -        this.loading[name] = false
        })
      },
      fetchTemplates (templateFilter, params) {
diff --cc ui/src/views/compute/EditVM.vue
index f7b7649b96b,d5e75fcc658..e35ca6dd49d
--- a/ui/src/views/compute/EditVM.vue
+++ b/ui/src/views/compute/EditVM.vue
@@@ -291,19 -247,9 +289,22 @@@ export default 
          this.template = templateResponses[0]
        })
      },
 +    fetchDynamicScalingVmConfig () {
 +      const params = {}
 +      params.name = 'enable.dynamic.scale.vm'
 +      params.zoneid = this.resource.zoneid
 +      var apiName = 'listConfigurations'
 +      getAPI(apiName, params).then(json => {
 +        const configResponse = json.listconfigurationsresponse.configuration
 +        this.dynamicScalingVmConfig = configResponse[0]?.value === 'true'
 +      })
 +    },
 +    canDynamicScalingEnabled () {
 +      return this.template.isdynamicallyscalable && 
this.serviceOffering.dynamicscalingenabled && this.dynamicScalingVmConfig
 +    },
+     isDynamicScalingEnabled () {
+       return this.template.isdynamicallyscalable && 
this.serviceOffering.dynamicscalingenabled && 
this.$store.getters.features.dynamicscalingenabled
+     },
      fetchOsTypes () {
        this.osTypes.loading = true
        this.osTypes.opts = []
diff --cc ui/src/views/compute/RegisterUserData.vue
index 4631ad70e7d,3fb54962f2b..3958db7160e
--- a/ui/src/views/compute/RegisterUserData.vue
+++ b/ui/src/views/compute/RegisterUserData.vue
@@@ -35,34 -35,20 +35,34 @@@
              :placeholder="apiParams.name.description"
              v-focus="true" />
          </a-form-item>
 -        <a-form-item name="userdata" ref="userdata">
 -          <template #label>
 -            <tooltip-label :title="$t('label.user.data')" 
:tooltip="$t('label.register.user.data.details')"/>
 -          </template>
 -          <a-textarea
 -            v-model:value="form.userdata"
 -            :placeholder="$t('label.register.user.data.details')"/>
 -        </a-form-item>
 +        <div v-if="$route.name === 'userdata'">
 +          <a-form-item name="userdata" ref="userdata">
 +            <template #label>
-               <tooltip-label :title="$t('label.userdata')" 
:tooltip="apiParams.userdata.description"/>
++              <tooltip-label :title="$t('label.user.data')" 
:tooltip="$t('label.register.user.data.details')"/>
 +            </template>
 +            <a-textarea
 +              v-model:value="form.userdata"
-               :placeholder="apiParams.userdata.description"/>
++              :placeholder="$t('label.register.user.data.details')"/>
 +          </a-form-item>
 +        </div>
 +        <div v-else>
 +          <a-form-item name="cniconfig" ref="cniconfig">
 +            <template #label>
 +              <tooltip-label :title="$t('label.cniconfiguration')" 
:tooltip="apiParams.cniconfig.description"/>
 +            </template>
 +            <a-textarea
 +              v-model:value="form.cniconfig"
 +              :placeholder="apiParams.cniconfig.description"/>
 +          </a-form-item>
 +        </div>
          <a-form-item name="isbase64" ref="isbase64" 
:label="$t('label.is.base64.encoded')">
            <a-checkbox v-model:checked="form.isbase64"></a-checkbox>
          </a-form-item>
          <a-form-item name="params" ref="params">
            <template #label>
 -            <tooltip-label :title="$t('label.user.data.params')" 
:tooltip="apiParams.params.description"/>
 +            <tooltip-label
-               :title="$route.name === 'userdata' ? $t('label.userdataparams') 
: $t('label.cniconfigparams')"
++              :title="$route.name === 'userdata' ? 
$t('label.user.data.params') : $t('label.cniconfigparams')"
 +              :tooltip="apiParams.params.description"/>
            </template>
            <a-select
              mode="tags"
diff --cc ui/src/views/compute/ResetUserData.vue
index 462a0901b88,8849db5dcca..c05c9452b2d
--- a/ui/src/views/compute/ResetUserData.vue
+++ b/ui/src/views/compute/ResetUserData.vue
@@@ -362,10 -362,13 +362,10 @@@ export default 
        params.id = this.resource.resetUserDataResourceId ? 
this.resource.resetUserDataResourceId : this.resource.id
  
        const resetUserDataApiName = this.resource.resetUserDataApiName ? 
this.resource.resetUserDataApiName : 'resetUserDataForVirtualMachine'
 -      const httpMethod = params.userdata ? 'POST' : 'GET'
 -      const args = httpMethod === 'POST' ? {} : params
 -      const data = httpMethod === 'POST' ? params : {}
  
 -      api(resetUserDataApiName, args, httpMethod, data).then(json => {
 +      postAPI(resetUserDataApiName, params).then(json => {
          this.$message.success({
-           content: `${this.$t('label.action.userdata.reset')} - 
${this.$t('label.success')}`,
+           content: `${this.$t('label.action.user.data.reset')} - 
${this.$t('label.success')}`,
            duration: 2
          })
          this.$emit('refresh-data')
diff --cc ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue
index 80d034b201b,584cbe6d1ba..5e5b635733b
--- a/ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue
+++ b/ui/src/views/infra/zone/ZoneWizardZoneDetailsStep.vue
@@@ -454,8 -469,19 +469,19 @@@ export default 
          hypervisor: [{ required: true, message: 
this.$t('message.error.hypervisor.type') }]
        })
      },
+     fetchDomainAccounts (domainid) {
+       api('listAccounts', {
+         domainid: domainid
+       }).then(response => {
+         // Clean up the selected account from a previous domain
+         this.form.account = null
+         this.selectedDomainAccounts = response.listaccountsresponse.account 
|| []
+       }).catch(error => {
+         this.$notifyError(error)
+       })
+     },
      fetchData () {
 -      api('listHypervisors').then(json => {
 +      getAPI('listHypervisors').then(json => {
          this.hypervisors = json.listhypervisorsresponse.hypervisor
          if ('listSimulatorHAStateTransitions' in this.$store.getters.apis) {
            this.hypervisors.push({ name: 'Simulator' })


Reply via email to