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' })