This is an automated email from the ASF dual-hosted git repository. dahn pushed a commit to branch 4.20-main-merge-try in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit dc7bb32606212ae59e87d7aa2f2f16571b7be4ce Merge: 0d4147f3f63 6ad17393be9 Author: Daan Hoogland <d...@apache.org> AuthorDate: Fri Jul 25 18:42:17 2025 +0200 Merge branch '4.20' .../main/java/com/cloud/user/AccountService.java | 2 + .../api/command/admin/cluster/ListClustersCmd.java | 6 +- .../api/command/admin/pod/ListPodsByCmd.java | 4 +- .../command/user/config/ListCapabilitiesCmd.java | 1 + .../command/user/userdata/RegisterUserDataCmd.java | 54 +- .../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 + .../cloudstack/backup/NASBackupProvider.java | 13 +- .../kvm/resource/LibvirtComputingResource.java | 11 +- .../LibvirtCheckConvertInstanceCommandWrapper.java | 2 +- .../wrapper/LibvirtCheckVolumeCommandWrapper.java | 80 ++- .../LibvirtConvertInstanceCommandWrapper.java | 2 +- .../LibvirtCopyRemoteVolumeCommandWrapper.java | 81 ++- .../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 | 514 +++++++------ .../cloud/api/query/dao/TemplateJoinDaoImpl.java | 7 +- .../consoleproxy/ConsoleProxyManagerImpl.java | 6 +- .../resourcelimit/ResourceLimitManagerImpl.java | 113 ++- .../com/cloud/server/ManagementServerImpl.java | 154 +--- .../com/cloud/storage/VolumeApiServiceImpl.java | 16 +- .../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 | 792 ++++++++++----------- 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 | 7 +- 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/views/compute/AutoScaleVmProfile.vue | 14 +- ui/src/views/compute/CreateAutoScaleVmGroup.vue | 14 +- ui/src/views/compute/DeployVM.vue | 48 +- ui/src/views/compute/DeployVnfAppliance.vue | 73 +- ui/src/views/compute/EditVM.vue | 18 +- ui/src/views/compute/RegisterUserData.vue | 42 +- ui/src/views/compute/ResetUserData.vue | 14 +- ui/src/views/compute/wizard/UserDataSelection.vue | 4 +- 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 +- .../{AclListRulesTab.vue => AclRulesTab.vue} | 2 +- ui/src/views/network/VpcTab.vue | 8 +- ui/src/views/offering/AddNetworkOffering.vue | 2 +- .../com/cloud/hypervisor/vmware/mo/HostMO.java | 392 +--------- 104 files changed, 1916 insertions(+), 1894 deletions(-) diff --cc api/src/main/java/org/apache/cloudstack/api/command/user/config/ListCapabilitiesCmd.java index 77a7a7fd8ea,bd3f39a09aa..318bed11ad9 --- 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,7 -72,7 +72,8 @@@ 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.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/command/user/userdata/RegisterUserDataCmd.java index fabf8827796,4588d734847..3fc555fa51a --- a/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/userdata/RegisterUserDataCmd.java @@@ -19,8 -19,15 +19,10 @@@ package org.apache.cloudstack.api.comma import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.Parameter; import org.apache.cloudstack.api.ServerApiException; + import org.apache.cloudstack.api.response.DomainResponse; + import org.apache.cloudstack.api.response.ProjectResponse; import org.apache.cloudstack.api.response.SuccessResponse; import org.apache.cloudstack.api.response.UserDataResponse; import org.apache.cloudstack.context.CallContext; diff --cc api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java index 74dbfa15a43,ff2e33b1389..910dc5fa38d --- a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java @@@ -136,10 -136,10 +136,14 @@@ 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.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; } @@@ -252,7 -252,7 +256,11 @@@ this.sharedFsVmMinRamSize = sharedFsVmMinRamSize; } + public void setInstanceLeaseEnabled(Boolean instanceLeaseEnabled) { + this.instanceLeaseEnabled = instanceLeaseEnabled; + } ++ + 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 cdedd830c0a,1a2090a6a77..f87ff7530ac --- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java +++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java @@@ -167,8 -161,6 +168,7 @@@ 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; @@@ -629,63 -604,6 +620,24 @@@ public class QueryManagerImpl extends M @Inject ManagementServerHostPeerJoinDao mshostPeerJoinDao; + @Inject + private AsyncJobManager jobManager; + + @Inject + private StoragePoolAndAccessGroupMapDao storagePoolAndAccessGroupMapDao; + + @Inject + public ManagementService managementService; + + @Inject + DataCenterDao dataCenterDao; + + @Inject + HostPodDao podDao; + + @Inject + GuestOSDao guestOSDao; + - 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) * @@@ -728,10 -642,10 +676,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; } @@@ -769,8 -682,8 +717,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(); @@@ -3235,17 -3095,25 +3182,17 @@@ 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()); @@@ -3319,100 -3157,7 +3266,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(); @@@ -4497,7 -4227,11 +4443,11 @@@ 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(); @@@ -4675,6 -4299,12 +4523,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()); @@@ -4796,7 -4536,7 +4768,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); - templateType, isVnf, domainId, isRecursive, cmd.getArch()); ++ templateType, isVnf, domainId, isRecursive, cmd.getArch(), cmd.getOsCategoryId(), forCks); } private Pair<List<TemplateJoinVO>, Integer> searchForTemplatesInternal(Long templateId, String name, String keyword, @@@ -4805,7 -4545,7 +4777,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) { - Boolean isVnf, Long domainId, boolean isRecursive, CPU.CPUArch arch) { ++ Boolean isVnf, Long domainId, boolean isRecursive, CPU.CPUArch arch, Long osCategoryId, Boolean forCks) { // check if zone is configured, if not, just return empty list List<HypervisorType> hypers = null; @@@ -5253,7 -4983,7 +5231,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); - domainId, isRecursive, cmd.getArch()); ++ domainId, isRecursive, cmd.getArch(), cmd.getOsCategoryId(), null); } @Override diff --cc server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index 37d74445776,85cca63546c..47f650da2cc --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@@ -615,9 -587,9 +610,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; } @@@ -1327,11 -1274,12 +1325,12 @@@ _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) && !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."); + if (newCount != null && !newCount.equals(oldCount) && + type != Resource.ResourceType.primary_storage && type != Resource.ResourceType.secondary_storage) { + 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 650028e1247,271372bf656..e5f8557c812 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@@ -627,7 -604,7 +627,6 @@@ import org.apache.cloudstack.api.comman import org.apache.cloudstack.api.command.user.vpn.UpdateVpnGatewayCmd; import org.apache.cloudstack.api.command.user.zone.ListZonesCmd; import org.apache.cloudstack.auth.UserAuthenticator; --import org.apache.cloudstack.auth.UserTwoFactorAuthenticator; import org.apache.cloudstack.config.ApiServiceConfiguration; import org.apache.cloudstack.config.Configuration; import org.apache.cloudstack.config.ConfigurationGroup; @@@ -770,14 -744,14 +769,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; @@@ -841,11 -815,12 +838,9 @@@ import com.cloud.utils.db.SearchCriteri import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionStatus; --import com.cloud.utils.db.UUIDManager; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.fsm.StateMachine2; import com.cloud.utils.net.MacAddress; --import com.cloud.utils.net.NetUtils; -import com.cloud.utils.security.CertificateHelper; import com.cloud.utils.ssh.SSHKeysHelper; import com.cloud.vm.ConsoleProxyVO; import com.cloud.vm.DiskProfile; @@@ -879,9 -854,6 +874,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); @@@ -985,8 -955,8 +980,6 @@@ @Inject private ProjectManager _projectMgr; @Inject -- private ResourceManager _resourceMgr; -- @Inject private HighAvailabilityManager _haMgr; @Inject private HostTagsDao _hostTagsDao; @@@ -1031,8 -1001,8 +1024,6 @@@ @Inject DomainRouterDao routerDao; @Inject -- public UUIDManager uuidMgr; -- @Inject protected UserDataDao userDataDao; @Inject protected VMTemplateDao templateDao; @@@ -1042,12 -1012,10 +1033,8 @@@ UserDataManager userDataManager; @Inject StoragePoolTagsDao storagePoolTagsDao; - @Inject - private PublicIpQuarantineDao publicIpQuarantineDao; - + protected ManagementServerJoinDao managementServerJoinDao; - - @Inject - private PublicIpQuarantineDao publicIpQuarantineDao; - @Inject ClusterManager _clusterMgr; @@@ -1069,10 -1033,10 +1056,7 @@@ private Map<String, Boolean> _availableIdsMap; -- private List<UserAuthenticator> _userAuthenticators; -- private List<UserTwoFactorAuthenticator> _userTwoFactorAuthenticators; private List<UserAuthenticator> _userPasswordEncoders; -- protected boolean _executeInSequence; protected List<DeploymentPlanner> _planners; @@@ -1088,51 -1052,51 +1072,11 @@@ protected List<AffinityGroupProcessor> _affinityProcessors; -- public List<AffinityGroupProcessor> getAffinityGroupProcessors() { -- return _affinityProcessors; -- } -- -- public void setAffinityGroupProcessors(final List<AffinityGroupProcessor> affinityProcessors) { -- _affinityProcessors = affinityProcessors; -- } -- public ManagementServerImpl() { setRunLevel(ComponentLifecycle.RUN_LEVEL_APPLICATION_MAINLOOP); setStateMachine(); } -- public List<UserAuthenticator> getUserAuthenticators() { -- return _userAuthenticators; -- } -- -- public void setUserAuthenticators(final List<UserAuthenticator> authenticators) { -- _userAuthenticators = authenticators; -- } -- -- public List<UserTwoFactorAuthenticator> getUserTwoFactorAuthenticators() { -- return _userTwoFactorAuthenticators; -- } -- -- public void setUserTwoFactorAuthenticators(final List<UserTwoFactorAuthenticator> userTwoFactorAuthenticators) { -- _userTwoFactorAuthenticators = userTwoFactorAuthenticators; -- } -- -- public List<UserAuthenticator> getUserPasswordEncoders() { -- return _userPasswordEncoders; -- } -- -- public void setUserPasswordEncoders(final List<UserAuthenticator> encoders) { -- _userPasswordEncoders = encoders; -- } -- -- public List<HostAllocator> getHostAllocators() { -- return hostAllocators; -- } -- -- public void setHostAllocators(final List<HostAllocator> hostAllocators) { -- this.hostAllocators = hostAllocators; -- } -- @Override public boolean configure(final String name, final Map<String, Object> params) throws ConfigurationException { @@@ -1185,10 -1149,10 +1129,6 @@@ return true; } -- protected Map<String, String> getConfigs() { -- return _configs; -- } -- @Override public String generateRandomPassword() { final Integer passwordLength = vmPasswordLength.value(); @@@ -1210,29 -1174,29 +1150,6 @@@ return MacAddress.getMacAddress().toLong(); } -- protected void checkPortParameters(final String publicPort, final String privatePort, final String privateIp, final String proto) { -- -- if (!NetUtils.isValidPort(publicPort)) { -- throw new InvalidParameterValueException("publicPort is an invalid value"); -- } -- if (!NetUtils.isValidPort(privatePort)) { -- throw new InvalidParameterValueException("privatePort is an invalid value"); -- } -- -- // logger.debug("Checking if " + privateIp + -- // " is a valid private IP address. Guest IP address is: " + -- // _configs.get("guest.ip.network")); -- // -- // if (!NetUtils.isValidPrivateIp(privateIp, -- // _configs.get("guest.ip.network"))) { -- // throw new -- // InvalidParameterValueException("Invalid private ip address"); -- // } -- if (!NetUtils.isValidProto(proto)) { -- throw new InvalidParameterValueException("Invalid protocol"); -- } -- } -- @Override public boolean archiveEvents(final ArchiveEventsCmd cmd) { final Account caller = getCaller(); @@@ -1306,7 -1270,7 +1223,7 @@@ final Object name = cmd.getClusterName(); final Object podId = cmd.getPodId(); Long zoneId = cmd.getZoneId(); -- final Object hypervisorType = cmd.getHypervisorType(); ++ final String hypervisorType = cmd.getHypervisorType(); final Object clusterType = cmd.getClusterType(); final Object allocationState = cmd.getAllocationState(); final String keyword = cmd.getKeyword(); @@@ -1351,8 -1307,8 +1268,7 @@@ } if (hypervisorType != null) { -- String hypervisorStr = (String) hypervisorType; -- String hypervisorSearch = HypervisorType.getType(hypervisorStr).toString(); ++ String hypervisorSearch = HypervisorType.getType(hypervisorType).toString(); sc.setParameters("hypervisorType", hypervisorSearch); } @@@ -1489,7 -1438,7 +1405,7 @@@ protected boolean zoneWideVolumeRequiresStorageMotion(PrimaryDataStore volumeDataStore, final Host sourceHost, final Host destinationHost) { -- if (volumeDataStore.isManaged() && sourceHost.getClusterId() != destinationHost.getClusterId()) { ++ if (volumeDataStore.isManaged() && !Objects.equals(sourceHost.getClusterId(), destinationHost.getClusterId())) { PrimaryDataStoreDriver driver = (PrimaryDataStoreDriver)volumeDataStore.getDriver(); // Depends on the storage driver. For some storages simply // changing volume access to host should work: grant access on destination @@@ -1533,7 -1482,7 +1449,7 @@@ // Check if the vm can be migrated with storage. boolean canMigrateWithStorage = false; -- List<HypervisorType> hypervisorTypes = Arrays.asList(new HypervisorType[]{HypervisorType.VMware, HypervisorType.KVM}); ++ List<HypervisorType> hypervisorTypes = Arrays.asList(HypervisorType.VMware, HypervisorType.KVM); if (VirtualMachine.Type.User.equals(vm.getType()) || hypervisorTypes.contains(vm.getHypervisorType())) { canMigrateWithStorage = _hypervisorCapabilitiesDao.isStorageMotionSupported(srcHost.getHypervisorType(), srcHostVersion); } @@@ -1556,11 -1505,11 +1472,11 @@@ } final Type hostType = srcHost.getType(); -- Pair<List<HostVO>, Integer> allHostsPair = null; -- List<HostVO> allHosts = null; ++ Pair<List<HostVO>, Integer> allHostsPair; ++ List<HostVO> allHosts; List<HostVO> filteredHosts = null; final Map<Host, Boolean> requiresStorageMotion = new HashMap<>(); -- DataCenterDeployment plan = null; ++ DataCenterDeployment plan; if (canMigrateWithStorage) { Long podId = !VirtualMachine.Type.User.equals(vm.getType()) ? srcHost.getPodId() : null; allHostsPair = searchForServers(startIndex, pageSize, null, hostType, null, srcHost.getDataCenterId(), podId, null, null, keyword, @@@ -1989,7 -1938,7 +1905,7 @@@ sb.and("hypervisorVersion", sb.entity().getHypervisorVersion(), SearchCriteria.Op.GTEQ); final String haTag = _haMgr.getHaTag(); -- SearchBuilder<HostTagVO> hostTagSearch = null; ++ SearchBuilder<HostTagVO> hostTagSearch; if (haHosts != null && StringUtils.isNotEmpty(haTag)) { hostTagSearch = _hostTagsDao.createSearchBuilder(); if ((Boolean)haHosts) { @@@ -2453,7 -2391,7 +2369,7 @@@ } boolean isAllocatedTemp = isAllocated; -- VlanType vlanType = null; ++ VlanType vlanType; if (forVirtualNetwork != null) { vlanType = forVirtualNetwork ? VlanType.VirtualNetwork : VlanType.DirectAttached; } else { @@@ -2526,7 -2464,7 +2442,7 @@@ Boolean isRecursive = cmd.isRecursive(); final List<Long> permittedAccounts = new ArrayList<>(); ListProjectResourcesCriteria listProjectResourcesCriteria = null; -- Boolean isAllocatedOrReserved = false; ++ boolean isAllocatedOrReserved = false; if (isAllocated || IpAddress.State.Reserved.name().equalsIgnoreCase(state)) { isAllocatedOrReserved = true; } @@@ -2618,7 -2556,7 +2534,7 @@@ } } -- if (freeAddrIds.size() > 0) { ++ if (!freeAddrIds.isEmpty()) { final SearchBuilder<IPAddressVO> sb2 = _publicIpAddressDao.createSearchBuilder(); buildParameters(sb2, cmd, false); sb2.and("ids", sb2.entity().getId(), SearchCriteria.Op.IN); @@@ -2672,8 -2609,8 +2588,8 @@@ if (tags != null && !tags.isEmpty()) { final SearchBuilder<ResourceTagVO> tagSearch = _resourceTagDao.createSearchBuilder(); for (int count = 0; count < tags.size(); count++) { -- tagSearch.or().op("key" + String.valueOf(count), tagSearch.entity().getKey(), SearchCriteria.Op.EQ); -- tagSearch.and("value" + String.valueOf(count), tagSearch.entity().getValue(), SearchCriteria.Op.EQ); ++ tagSearch.or().op("key" + count, tagSearch.entity().getKey(), SearchCriteria.Op.EQ); ++ tagSearch.and("value" + count, tagSearch.entity().getValue(), SearchCriteria.Op.EQ); tagSearch.cp(); } tagSearch.and("resourceType", tagSearch.entity().getResourceType(), SearchCriteria.Op.EQ); @@@ -2702,8 -2632,7 +2618,7 @@@ final Object keyword = cmd.getKeyword(); final Long physicalNetworkId = cmd.getPhysicalNetworkId(); final Long sourceNetworkId = cmd.getNetworkId(); - final Long vpcId = cmd.getVpcId(); - final Long zone = cmd.getZoneId(); + Long zone = cmd.getZoneId(); final String address = cmd.getIpAddress(); final Long vlan = cmd.getVlanId(); final Long ipId = cmd.getId(); @@@ -2721,8 -2649,8 +2636,8 @@@ int count = 0; sc.setJoinParameters("tagSearch", "resourceType", ResourceObjectType.PublicIpAddress.toString()); for (final String key : tags.keySet()) { -- sc.setJoinParameters("tagSearch", "key" + String.valueOf(count), key); -- sc.setJoinParameters("tagSearch", "value" + String.valueOf(count), tags.get(key)); ++ sc.setJoinParameters("tagSearch", "key" + count, key); ++ sc.setJoinParameters("tagSearch", "value" + count, tags.get(key)); count++; } } @@@ -3003,7 -2841,7 +2918,7 @@@ if (duplicate != null) { if (!cmd.isForced()) { throw new InvalidParameterValueException( -- "Mapping from hypervisor : " + hypervisorType.toString() + ", version : " + hypervisorVersion + " and guest OS : " + guestOs.getDisplayName() + " already exists!"); ++ "Mapping from hypervisor : " + hypervisorType + ", version : " + hypervisorVersion + " and guest OS : " + guestOs.getDisplayName() + " already exists!"); } if (Boolean.TRUE.equals(cmd.getOsMappingCheckEnabled())) { @@@ -3095,7 -2933,7 +3010,7 @@@ logger.debug("GuestOSDetails"); final GuestOSVO guestOsVo = new GuestOSVO(); -- guestOsVo.setCategoryId(categoryId.longValue()); ++ guestOsVo.setCategoryId(categoryId); guestOsVo.setDisplayName(displayName); guestOsVo.setName(name); guestOsVo.setIsUserDefined(true); @@@ -3108,8 -2946,8 +3023,8 @@@ } private void persistGuestOsDetails(Map<String, String> details, long guestOsPersistedId) { -- for (Object key : details.keySet()) { -- _guestOsDetailsDao.addDetail(guestOsPersistedId, (String)key, details.get(key), false); ++ for (String key : details.keySet()) { ++ _guestOsDetailsDao.addDetail(guestOsPersistedId, key, details.get(key), false); } } @@@ -3354,7 -3169,7 +3269,7 @@@ logger.trace("Trying to retrieve VNC port from agent about VM " + vm.getHostName()); } -- GetVncPortAnswer answer = null; ++ GetVncPortAnswer answer; if (vm.getState() == State.Migrating && vm.getLastHostId() != null) { answer = (GetVncPortAnswer)_agentMgr.easySend(vm.getLastHostId(), new GetVncPortCommand(vm.getId(), vm.getInstanceName())); } else { @@@ -3528,9 -3343,9 +3443,9 @@@ }); -- Integer pageSize = null; ++ int pageSize; try { -- pageSize = Integer.valueOf(cmd.getPageSizeVal().toString()); ++ pageSize = Integer.parseInt(cmd.getPageSizeVal().toString()); } catch (final IllegalArgumentException e) { throw new InvalidParameterValueException("pageSize " + cmd.getPageSizeVal() + " is out of Integer range is not supported for this call"); } @@@ -4284,7 -4086,7 +4199,7 @@@ final Calendar purgeCal = Calendar.getInstance(); purgeCal.add(Calendar.DAY_OF_YEAR, -_purgeDelay); final Date purgeTime = purgeCal.getTime(); -- logger.debug("Deleting events older than: " + purgeTime.toString()); ++ logger.debug("Deleting events older than: " + purgeTime); final List<EventVO> oldEvents = _eventDao.listOlderEvents(purgeTime); logger.debug("Found " + oldEvents.size() + " events to be purged"); for (final EventVO event : oldEvents) { @@@ -4318,7 -4120,7 +4233,7 @@@ final Calendar purgeCal = Calendar.getInstance(); purgeCal.add(Calendar.DAY_OF_YEAR, -_alertPurgeDelay); final Date purgeTime = purgeCal.getTime(); -- logger.debug("Deleting alerts older than: " + purgeTime.toString()); ++ logger.debug("Deleting alerts older than: " + purgeTime); final List<AlertVO> oldAlerts = _alertDao.listOlderAlerts(purgeTime); logger.debug("Found " + oldAlerts.size() + " events to be purged"); for (final AlertVO alert : oldAlerts) { @@@ -4656,8 -4458,8 +4571,8 @@@ final Account caller = getCaller(); boolean securityGroupsEnabled = false; -- boolean elasticLoadBalancerEnabled = false; -- boolean KVMSnapshotEnabled = false; ++ boolean elasticLoadBalancerEnabled; ++ boolean KVMSnapshotEnabled; String supportELB = "false"; final List<NetworkVO> networks = networkDao.listSecurityGroupEnabledNetworks(); if (networks != null && !networks.isEmpty()) { @@@ -4728,7 -4528,7 +4643,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); @@@ -4756,7 -4556,7 +4672,7 @@@ final String groupName = cmd.getGroupName(); // Verify input parameters -- final InstanceGroupVO group = _vmGroupDao.findById(groupId.longValue()); ++ final InstanceGroupVO group = _vmGroupDao.findById(groupId); if (group == null) { final InvalidParameterValueException ex = new InvalidParameterValueException("unable to find a vm group with specified groupId"); ex.addProxyObject(groupId.toString(), "groupId"); @@@ -4856,7 -4656,7 +4772,7 @@@ final String[] hypervisors = hypers.split(","); if (zoneId != null) { -- if (zoneId.longValue() == -1L) { ++ if (zoneId == -1L) { final List<DataCenterVO> zones = _dcDao.listAll(); for (final String hypervisor : hypervisors) { @@@ -5739,32 -5513,12 +5655,24 @@@ _storagePoolAllocators = storagePoolAllocators; } -- public LockControllerListener getLockControllerListener() { -- return _lockControllerListener; - } - - public void setLockControllerListener(final LockControllerListener lockControllerListener) { - _lockControllerListener = lockControllerListener; -- } - + @Override + @DB + @ActionEvent(eventType = EventTypes.EVENT_MANAGEMENT_SERVER_REMOVE, eventDescription = "removing Management Server") + public boolean removeManagementServer(RemoveManagementServerCmd cmd) { + final Long id = cmd.getId(); + ManagementServerJoinVO managementServer = managementServerJoinDao.findById(id); + + if (managementServer == null) { + throw new InvalidParameterValueException(String.format("Unable to find a Management Server with ID equal to [%s].", managementServer.getUuid())); + } + + if (!ManagementServerHost.State.Down.equals(managementServer.getState())) { + throw new InvalidParameterValueException(String.format("Unable to remove Management Server with ID [%s]. It can only be removed when it is in the [%s] state, however currently it is in the [%s] state.", managementServer.getUuid(), ManagementServerHost.State.Down.name(), managementServer.getState().name())); + } + + managementServer.setRemoved(new Date()); + return managementServerJoinDao.update(id, managementServer); - public void setLockControllerListener(final LockControllerListener lockControllerListener) { - _lockControllerListener = lockControllerListener; } } diff --cc ui/public/locales/en.json index e26cd4d494e,c36b96cc961..5f19a807548 --- 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,13 -81,12 +84,13 @@@ "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.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", @@@ -224,13 -220,9 +224,13 @@@ "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.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", @@@ -256,40 -248,37 +256,38 @@@ "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.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.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.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", @@@ -439,16 -427,13 +437,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.repository.add": "Add Backup Repository", ++"label.backup.repository.remove": "Remove 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.backuplimit": "Backup Limits", +"label.backup.storage": "Backup Storage", ++"label.backuplimit": "Backup Limits", + "label.backupofferingid": "Backup Offering", + "label.backupofferingname": "Backup Offering", -"label.backup.repository.add": "Add Backup Repository", -"label.backup.repository.remove": "Remove Backup Repository", +"label.backupstoragelimit": "Backup Storage Limits (GiB)", "label.balance": "Balance", "label.bandwidth": "Bandwidth", "label.baremetal.dhcp.devices": "Bare metal DHCP devices", @@@ -477,14 -462,13 +475,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", @@@ -683,10 -653,8 +681,10 @@@ "label.daily": "Daily", "label.dark.mode": "Dark mode", "label.dashboard": "Dashboard", - "label.data.disk": "Data disk", + "label.data.disk": "Data Disk", +"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.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>.", @@@ -1033,9 -1000,8 +1031,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)", @@@ -1085,9 -1050,7 +1083,9 @@@ "label.guest.netmask": "Guest netmask", "label.guest.networks": "Guest Networks", "label.guest.os": "Guest OS", +"label.guest.os.category": "Guest OS Category", +"label.guest.os.categories": "Guest OS Categories", - "label.guest.os.hypervisor.mappings": "Guest OS mappings", + "label.guest.os.hypervisor.mappings": "Guest OS Mappings", "label.guest.start.ip": "Guest start IP", "label.guest.traffic": "Guest traffic", "label.guestcidraddress": "Guest CIDR", @@@ -1163,14 -1126,12 +1161,14 @@@ "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.in.progress": "in progress", @@@ -1312,18 -1272,16 +1310,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": "Kubernetes Cluster", + "label.kubernetes.cluster.create": "Create Kubernetes Cluster", + "label.kubernetes.cluster.delete": "Delete Kubernetes Cluster", +"label.kubernetes.cluster.remove.nodes.from.cluster": "Remove nodes from Kubernetes cluster", - "label.kubernetes.cluster.upgrade": "Upgrade 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.", @@@ -1368,9 -1326,8 +1366,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", @@@ -1456,9 -1408,8 +1453,9 @@@ "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.maxsecondarystorage": "Max. secondary storage (GiB)", "label.maxsize": "Maximum size", @@@ -1753,9 -1688,8 +1750,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", @@@ -1934,7 -1863,7 +1931,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", @@@ -1960,9 -1888,8 +1958,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", @@@ -2130,20 -2054,16 +2127,19 @@@ "label.sequence": "Sequence", "label.server": "Server", "label.server.certificate": "Server certificate", - "label.serviceip": "Service IP", ++"label.serviceip": "Management 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.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": "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.staticnat.associatepublicip": "Associate public IP", "label.service.staticnat.elasticipcheckbox": "Elastic IP", "label.servicegroupuuid": "Service Group", @@@ -2219,24 -2138,21 +2215,25 @@@ "label.srctaguuid": "Source Tag", "label.srx": "SRX", "label.srx.firewall": "Juniper SRX firewall", - "label.ssh.key.pairs": "SSH key pairs", + "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.uefi.supported": "UEFI supported", "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", @@@ -2518,14 -2433,12 +2517,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", @@@ -2629,10 -2542,8 +2626,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", @@@ -2650,10 -2561,9 +2647,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", @@@ -2721,9 -2631,9 +2718,10 @@@ "label.oobm.username": "Out-of-band management username", "label.bucket.update": "Update Bucket", "label.bucket.delete": "Delete Bucket", -"label.quotagb": "Quota in GB", +"label.quotagib": "Quota in GiB", + "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", @@@ -2875,11 -2772,10 +2873,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.", @@@ -3119,17 -3009,15 +3117,17 @@@ "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.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.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.user.data": "Please fill in the following to register new User Data.", "message.desc.registered.user.data": "Registered a User Data.", - "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?", @@@ -3344,9 -3232,8 +3342,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 ", @@@ -3354,21 -3241,14 +3352,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", @@@ -3391,16 -3270,14 +3389,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.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.remove.nodes": "Please confirm that you want to remove the following nodes from 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.", @@@ -3475,17 -3352,15 +3473,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.", @@@ -3571,13 -3446,12 +3569,13 @@@ "message.set.default.nic.manual": "Please manually update the default NIC on the Instance now.", "message.setting.updated": "Setting Updated:", "message.setting.update.delay": "The new value will take effect within 30 seconds.", - "message.setup.physical.network.during.zone.creation": "When adding a zone, you need to set up one or more physical networks. Each physical network can carry one or more types of traffic, with certain restrictions on how they may be combined. Add or remove one or more traffic types onto each physical network.", - "message.setup.physical.network.during.zone.creation.basic": "When adding a basic zone, you can set up one physical Network, which corresponds to a NIC on the hypervisor. The Network carries several types of traffic.<br/><br/>You may also <strong>add</strong> other traffic types onto the physical Network.", + "message.setup.physical.network.during.zone.creation": "When adding a Zone, you need to set up one or more physical networks. Each physical network can carry one or more types of traffic, with certain restrictions on how they may be combined. Add or remove one or more traffic types onto each physical network.", + "message.setup.physical.network.during.zone.creation.basic": "When adding a basic Zone, you can set up one physical Network, which corresponds to a NIC on the hypervisor. The Network carries several types of traffic.<br/><br/>You may also <strong>add</strong> other traffic types onto the physical Network.", "message.shared.network.offering.warning": "Domain admins and regular Users can only create shared Networks from Network offering with the setting specifyvlan=false. Please contact an administrator to create a Network offering if this list is empty.", - "message.shared.network.unsupported.for.nsx": "Shared networks aren't supported for NSX enabled zones", - "message.shutdown.triggered": "Shutdown has been triggered. This Management Server will not accept new jobs", + "message.shared.network.unsupported.for.nsx": "Shared networks aren't supported for NSX enabled Zones", + "message.shutdown.triggered": "A shutdown has been triggered. CloudStack will not accept new jobs", +"message.maintenance.initiated": "Maintenance has been initiated. This Management Server will not accept new jobs", - "message.snapshot.additional.zones": "Snapshots will always be created in its native zone - %x, here you can select additional zone(s) where it will be copied to at creation time", + "message.snapshot.additional.zones": "Snapshots will always be created in its native Zone - %x, here you can select additional zone(s) where it will be copied to at creation time", "message.sourcenatip.change.warning": "WARNING: Changing the sourcenat IP address of the network will cause connectivity downtime for the Instances with NICs in the Network.", "message.sourcenatip.change.inhibited": "Changing the sourcenat to this IP of the Network to this address is inhibited as firewall rules are defined for it. This can include port forwarding or load balancing rules.\n - If this is an Isolated Network, please use updateNetwork/click the edit button.\n - If this is a VPC, first clear all other rules for this address.", "message.specify.tag.key": "Please specify a tag key.", @@@ -3599,11 -3473,9 +3597,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", @@@ -3842,12 -3713,11 +3840,12 @@@ "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.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>", diff --cc ui/src/components/view/InfoCard.vue index c5e771de5a6,f1efcaef281..1824b549a4b --- 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)" @@@ -912,7 -891,7 +927,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', @@@ -925,7 -904,7 +941,8 @@@ UploadResourceIcon, ResourceIcon, ResourceLabel, - ImageDeployInstanceButton ++ ImageDeployInstanceButton, + FileTextOutlined }, props: { resource: { diff --cc ui/src/views/compute/DeployVM.vue index ac98f35d806,a604fe68fe4..1d954a81266 --- a/ui/src/views/compute/DeployVM.vue +++ b/ui/src/views/compute/DeployVM.vue @@@ -583,44 -603,16 +583,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" @@@ -1283,25 -1274,6 +1282,28 @@@ export default type: 'Routing' }, field: 'hostid' ++<<<<<<< HEAD + }, + dynamicScalingVmConfig: { + list: 'listConfigurations', + options: { + zoneid: _.get(this.zone, 'id'), + name: 'enable.dynamic.scale.vm' + } + }, + guestOsCategories: { + list: 'listOsCategories', + options: { + zoneid: _.get(this.zone, 'id'), + isfeatured: true, + isiso: _.get(this, 'imageType') === 'isoid', + arch: this.selectedArchitecture, + isvnf: false, + showicon: true + }, + field: 'guestoscategoryid' ++======= ++>>>>>>> 4.20 } } }, @@@ -2496,68 -2349,66 +2498,94 @@@ }) }, 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) + } ++<<<<<<< HEAD + 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) => { + 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 ++>>>>>>> 4.20 } - 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 a5618f5d9d4,1117413d710..19a87ba8664 --- a/ui/src/views/compute/DeployVnfAppliance.vue +++ b/ui/src/views/compute/DeployVnfAppliance.vue @@@ -549,16 -564,16 +549,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" @@@ -1288,18 -1277,20 +1287,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 @@@ -2467,68 -2386,66 +2467,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/RegisterUserData.vue index 4631ad70e7d,3fb54962f2b..9094e04c2a7 --- a/ui/src/views/compute/RegisterUserData.vue +++ b/ui/src/views/compute/RegisterUserData.vue @@@ -35,26 -35,14 +35,22 @@@ :placeholder="apiParams.name.description" v-focus="true" /> </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"/> - </template> - <a-textarea - v-model:value="form.userdata" - :placeholder="apiParams.userdata.description"/> - </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="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> ++ <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> <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> 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/compute/wizard/UserDataSelection.vue index 06eaefcc060,0b8fdc06952..b8739e81e53 --- a/ui/src/views/compute/wizard/UserDataSelection.vue +++ b/ui/src/views/compute/wizard/UserDataSelection.vue @@@ -34,8 -33,7 +34,8 @@@ :scroll="{ y: 225 }" > <template #headerCell="{ column }"> - <template v-if="column.key === 'name'"><solution-outlined /> {{ $t('label.userdata') }}</template> + <template v-if="column.key === 'name'"><solution-outlined /> {{ $t('label.user.data') }}</template> + <template v-if="column.key === 'AS_NUMBER'"><user-outlined /> {{ $t('label.account') }}</template> <template v-if="column.key === 'account'"><user-outlined /> {{ $t('label.account') }}</template> <template v-if="column.key === 'domain'"><block-outlined /> {{ $t('label.domain') }}</template> </template>