diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index f5861c257a1..475b845af9b 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -27,6 +27,7 @@ public class ApiConstants { public static final String ACTIVATION_RULE = "activationrule"; public static final String ACTIVITY = "activity"; public static final String ADAPTER_TYPE = "adaptertype"; + public static final String ADDITONAL_CONFIG_ENABLED = "additionalconfigenabled"; public static final String ADDRESS = "address"; public static final String ALGORITHM = "algorithm"; public static final String ALIAS = "alias"; diff --git 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 index e73bb97a21b..ed1bd7b063b 100644 --- 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 @@ -75,6 +75,7 @@ public class ListCapabilitiesCmd extends BaseCmd { response.setInstanceLeaseEnabled((Boolean) capabilities.get(ApiConstants.INSTANCE_LEASE_ENABLED)); response.setExtensionsPath((String)capabilities.get(ApiConstants.EXTENSIONS_PATH)); response.setDynamicScalingEnabled((Boolean) capabilities.get(ApiConstants.DYNAMIC_SCALING_ENABLED)); + response.setAdditionalConfigEnabled((Boolean) capabilities.get(ApiConstants.ADDITONAL_CONFIG_ENABLED)); response.setObjectName("capability"); response.setResponseName(getCommandName()); this.setResponseObject(response); diff --git a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java index eb0daf75148..d2c71b5f352 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/CapabilitiesResponse.java @@ -149,6 +149,10 @@ public class CapabilitiesResponse extends BaseResponse { @Param(description = "true if dynamically scaling for instances is enabled", since = "4.21.0") private Boolean dynamicScalingEnabled; + @SerializedName(ApiConstants.ADDITONAL_CONFIG_ENABLED) + @Param(description = "true if additional configurations or extraconfig can be passed to Instances", since = "4.20.2") + private Boolean additionalConfigEnabled; + public void setSecurityGroupsEnabled(boolean securityGroupsEnabled) { this.securityGroupsEnabled = securityGroupsEnabled; } @@ -272,4 +276,8 @@ public class CapabilitiesResponse extends BaseResponse { public void setDynamicScalingEnabled(Boolean dynamicScalingEnabled) { this.dynamicScalingEnabled = dynamicScalingEnabled; } + + public void setAdditionalConfigEnabled(Boolean additionalConfigEnabled) { + this.additionalConfigEnabled = additionalConfigEnabled; + } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java index 2f19d36c37a..6ac49967540 100755 --- a/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/VMInstanceDaoImpl.java @@ -116,17 +116,17 @@ public class VMInstanceDaoImpl extends GenericDaoBase implem protected Attribute _updateTimeAttr; - private static final String ORDER_CLUSTERS_NUMBER_OF_VMS_FOR_ACCOUNT_PART1 = "SELECT host.cluster_id, SUM(IF(vm.state='Running' AND vm.account_id = ?, 1, 0)) " + + private static final String ORDER_CLUSTERS_NUMBER_OF_VMS_FOR_ACCOUNT_PART1 = "SELECT host.cluster_id, SUM(IF(vm.state IN ('Running', 'Starting') AND vm.account_id = ?, 1, 0)) " + "FROM `cloud`.`host` host LEFT JOIN `cloud`.`vm_instance` vm ON host.id = vm.host_id WHERE "; private static final String ORDER_CLUSTERS_NUMBER_OF_VMS_FOR_ACCOUNT_PART2 = " AND host.type = 'Routing' AND host.removed is null GROUP BY host.cluster_id " + "ORDER BY 2 ASC "; - private static final String ORDER_PODS_NUMBER_OF_VMS_FOR_ACCOUNT = "SELECT pod.id, SUM(IF(vm.state='Running' AND vm.account_id = ?, 1, 0)) FROM `cloud`.`" + + private static final String ORDER_PODS_NUMBER_OF_VMS_FOR_ACCOUNT = "SELECT pod.id, SUM(IF(vm.state IN ('Running', 'Starting') AND vm.account_id = ?, 1, 0)) FROM `cloud`.`" + "host_pod_ref` pod LEFT JOIN `cloud`.`vm_instance` vm ON pod.id = vm.pod_id WHERE pod.data_center_id = ? AND pod.removed is null " + " GROUP BY pod.id ORDER BY 2 ASC "; private static final String ORDER_HOSTS_NUMBER_OF_VMS_FOR_ACCOUNT = - "SELECT host.id, SUM(IF(vm.state='Running' AND vm.account_id = ?, 1, 0)) FROM `cloud`.`host` host LEFT JOIN `cloud`.`vm_instance` vm ON host.id = vm.host_id " + + "SELECT host.id, SUM(IF(vm.state IN ('Running', 'Starting') AND vm.account_id = ?, 1, 0)) FROM `cloud`.`host` host LEFT JOIN `cloud`.`vm_instance` vm ON host.id = vm.host_id " + "WHERE host.data_center_id = ? AND host.type = 'Routing' AND host.removed is null "; private static final String ORDER_HOSTS_NUMBER_OF_VMS_FOR_ACCOUNT_PART2 = " GROUP BY host.id ORDER BY 2 ASC "; diff --git a/engine/storage/src/main/java/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java b/engine/storage/src/main/java/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java index 5e9891ef989..32a321c2619 100644 --- a/engine/storage/src/main/java/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java +++ b/engine/storage/src/main/java/org/apache/cloudstack/storage/volume/datastore/PrimaryDataStoreHelper.java @@ -190,7 +190,9 @@ public class PrimaryDataStoreHelper { pool.setScope(scope.getScopeType()); pool.setUsedBytes(existingInfo.getCapacityBytes() - existingInfo.getAvailableBytes()); pool.setCapacityBytes(existingInfo.getCapacityBytes()); - pool.setStatus(StoragePoolStatus.Up); + if (pool.getStatus() != StoragePoolStatus.Disabled) { + pool.setStatus(StoragePoolStatus.Up); + } this.dataStoreDao.update(pool.getId(), pool); this.storageMgr.createCapacityEntry(pool, Capacity.CAPACITY_TYPE_LOCAL_STORAGE, pool.getUsedBytes()); return dataStoreMgr.getDataStore(pool.getId(), DataStoreRole.Primary); diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 289dae42e91..be5ffe1b96d 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -5838,11 +5838,20 @@ public class VmwareResource extends ServerResourceBase implements StoragePoolRes if (toolsStatus == VirtualMachineToolsStatus.TOOLS_NOT_INSTALLED) { details += "Vmware tools not installed."; } else { - ip = guestInfo.getIpAddress(); - if (ip != null) { - result = true; + var normalizedMac = cmd.getMacAddress().replaceAll("-", ":"); + for(var guestInfoNic : guestInfo.getNet()) { + var normalizedNicMac = guestInfoNic.getMacAddress().replaceAll("-", ":"); + if (!result && normalizedNicMac.equalsIgnoreCase(normalizedMac)) { + result = true; + details = null; + for (var ipAddr : guestInfoNic.getIpAddress()) { + if (NetUtils.isValidIp4(ipAddr) && (cmd.getVmNetworkCidr() == null || NetUtils.isIpWithInCidrRange(ipAddr, cmd.getVmNetworkCidr()))) { + details = ipAddr; + } + } + break; + } } - details = ip; } } else { details += "VM " + vmName + " no longer exists on vSphere host: " + hyperHost.getHyperHostName(); diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java index 1466fdf8782..c46b0c00fa1 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java @@ -430,6 +430,9 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne logger.warn("Unable to find the network with ID: {} passed for the Kubernetes cluster", networkId); return false; } + if (isDirectAccess(network)) { + return true; + } networkOffering = networkOfferingDao.findById(network.getNetworkOfferingId()); if (networkOffering == null) { logger.warn("Unable to find the network offering of the network: {} ({}) to be used for provisioning Kubernetes cluster", network.getName(), network.getUuid()); @@ -1870,7 +1873,7 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne try { Role role = getProjectKubernetesAccountRole(); UserAccount userAccount = accountService.createUserAccount(accountName, - UuidUtils.first(UUID.randomUUID().toString()), PROJECT_KUBERNETES_ACCOUNT_FIRST_NAME, + UUID.randomUUID().toString(), PROJECT_KUBERNETES_ACCOUNT_FIRST_NAME, PROJECT_KUBERNETES_ACCOUNT_LAST_NAME, null, null, accountName, Account.Type.NORMAL, role.getId(), project.getDomainId(), null, null, null, null, User.Source.NATIVE); projectManager.assignAccountToProject(project, userAccount.getAccountId(), ProjectAccount.Role.Regular, diff --git a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java index b3cf825ea59..2b44b0b8c53 100644 --- a/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java +++ b/plugins/storage/volume/default/src/main/java/org/apache/cloudstack/storage/datastore/lifecycle/CloudStackPrimaryDataStoreLifeCycleImpl.java @@ -504,7 +504,7 @@ public class CloudStackPrimaryDataStoreLifeCycleImpl extends BasePrimaryDataStor @Override public boolean attachHost(DataStore store, HostScope scope, StoragePoolInfo existingInfo) { DataStore dataStore = dataStoreHelper.attachHost(store, scope, existingInfo); - if(existingInfo.getCapacityBytes() == 0){ + if (existingInfo.getCapacityBytes() == 0) { try { storageMgr.connectHostToSharedPool(hostDao.findById(scope.getScopeId()), dataStore.getId()); } catch (StorageUnavailableException ex) { diff --git a/plugins/storage/volume/linstor/CHANGELOG.md b/plugins/storage/volume/linstor/CHANGELOG.md index c0991a9aa2b..7da3516955d 100644 --- a/plugins/storage/volume/linstor/CHANGELOG.md +++ b/plugins/storage/volume/linstor/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to Linstor CloudStack plugin will be documented in this file The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2025-10-03] + +### Changed + +- Revert qcow2 snapshot now use sparse/discard options to convert on thin devices. + ## [2025-08-05] ### Fixed diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorRevertBackupSnapshotCommandWrapper.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorRevertBackupSnapshotCommandWrapper.java index 252cb6c7327..2d6df5f2296 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorRevertBackupSnapshotCommandWrapper.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LinstorRevertBackupSnapshotCommandWrapper.java @@ -26,24 +26,39 @@ import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager; import com.cloud.resource.CommandWrapper; import com.cloud.resource.ResourceWrapper; import com.cloud.storage.Storage; +import com.cloud.utils.script.Script; import org.apache.cloudstack.storage.command.CopyCmdAnswer; +import org.apache.cloudstack.storage.datastore.util.LinstorUtil; import org.apache.cloudstack.storage.to.SnapshotObjectTO; import org.apache.cloudstack.storage.to.VolumeObjectTO; import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImgException; import org.apache.cloudstack.utils.qemu.QemuImgFile; +import org.joda.time.Duration; import org.libvirt.LibvirtException; @ResourceWrapper(handles = LinstorRevertBackupSnapshotCommand.class) public final class LinstorRevertBackupSnapshotCommandWrapper extends CommandWrapper { - private void convertQCow2ToRAW(final String srcPath, final String dstPath, int waitMilliSeconds) + + private void convertQCow2ToRAW( + KVMStoragePool pool, final String srcPath, final String dstUuid, int waitMilliSeconds) throws LibvirtException, QemuImgException { + final String dstPath = pool.getPhysicalDisk(dstUuid).getPath(); final QemuImgFile srcQemuFile = new QemuImgFile( srcPath, QemuImg.PhysicalDiskFormat.QCOW2); - final QemuImg qemu = new QemuImg(waitMilliSeconds); + boolean zeroedDevice = LinstorUtil.resourceSupportZeroBlocks(pool, LinstorUtil.RSC_PREFIX + dstUuid); + if (zeroedDevice) + { + // blockdiscard the device to ensure the device is filled with zeroes + Script blkDiscardScript = new Script("blkdiscard", Duration.millis(waitMilliSeconds)); + blkDiscardScript.add("-f"); + blkDiscardScript.add(dstPath); + blkDiscardScript.execute(); + } + final QemuImg qemu = new QemuImg(waitMilliSeconds, zeroedDevice, true); final QemuImgFile dstFile = new QemuImgFile(dstPath, QemuImg.PhysicalDiskFormat.RAW); qemu.convert(srcQemuFile, dstFile); } @@ -70,8 +85,9 @@ public final class LinstorRevertBackupSnapshotCommandWrapper srcDataStore.getUrl() + File.separator + srcFile.getParent()); convertQCow2ToRAW( + linstorPool, secondaryPool.getLocalPath() + File.separator + srcFile.getName(), - linstorPool.getPhysicalDisk(dst.getPath()).getPath(), + dst.getPath(), cmd.getWaitInMillSeconds()); final VolumeObjectTO dstVolume = new VolumeObjectTO(); diff --git a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java index 28928570732..a664e7ed03b 100644 --- a/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java +++ b/plugins/storage/volume/linstor/src/main/java/com/cloud/hypervisor/kvm/storage/LinstorStorageAdaptor.java @@ -30,7 +30,6 @@ import javax.annotation.Nonnull; import com.cloud.storage.Storage; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.script.Script; - import org.apache.cloudstack.storage.datastore.util.LinstorUtil; import org.apache.cloudstack.utils.qemu.QemuImg; import org.apache.cloudstack.utils.qemu.QemuImgException; @@ -57,7 +56,6 @@ import com.linbit.linstor.api.model.ResourceGroupSpawn; import com.linbit.linstor.api.model.ResourceMakeAvailable; import com.linbit.linstor.api.model.ResourceWithVolumes; import com.linbit.linstor.api.model.StoragePool; -import com.linbit.linstor.api.model.Volume; import com.linbit.linstor.api.model.VolumeDefinition; import java.io.File; @@ -573,40 +571,6 @@ public class LinstorStorageAdaptor implements StorageAdaptor { return copyPhysicalDisk(disk, name, destPool, timeout, null, null, null); } - /** - * Checks if all diskful resource are on a zeroed block device. - * @param destPool Linstor pool to use - * @param resName Linstor resource name - * @return true if all resources are on a provider with zeroed blocks. - */ - private boolean resourceSupportZeroBlocks(KVMStoragePool destPool, String resName) { - final DevelopersApi api = getLinstorAPI(destPool); - - try { - List resWithVols = api.viewResources( - Collections.emptyList(), - Collections.singletonList(resName), - Collections.emptyList(), - Collections.emptyList(), - null, - null); - - if (resWithVols != null) { - return resWithVols.stream() - .allMatch(res -> { - Volume vol0 = res.getVolumes().get(0); - return vol0 != null && (vol0.getProviderKind() == ProviderKind.LVM_THIN || - vol0.getProviderKind() == ProviderKind.ZFS || - vol0.getProviderKind() == ProviderKind.ZFS_THIN || - vol0.getProviderKind() == ProviderKind.DISKLESS); - } ); - } - } catch (ApiException apiExc) { - logger.error(apiExc.getMessage()); - } - return false; - } - /** * Checks if the given disk is the SystemVM template, by checking its properties file in the same directory. * The initial systemvm template resource isn't created on the management server, but @@ -677,7 +641,7 @@ public class LinstorStorageAdaptor implements StorageAdaptor { destFile.setFormat(dstDisk.getFormat()); destFile.setSize(disk.getVirtualSize()); - boolean zeroedDevice = resourceSupportZeroBlocks(destPools, getLinstorRscName(name)); + boolean zeroedDevice = LinstorUtil.resourceSupportZeroBlocks(destPools, getLinstorRscName(name)); try { final QemuImg qemu = new QemuImg(timeout, zeroedDevice, true); qemu.convert(srcFile, destFile); diff --git a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java index 40aca2fdd45..9c3c9d32611 100644 --- a/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java +++ b/plugins/storage/volume/linstor/src/main/java/org/apache/cloudstack/storage/datastore/util/LinstorUtil.java @@ -42,6 +42,7 @@ import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; +import com.cloud.hypervisor.kvm.storage.KVMStoragePool; import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import org.apache.logging.log4j.Logger; @@ -431,4 +432,37 @@ public class LinstorUtil { public static boolean isRscDiskless(ResourceWithVolumes rsc) { return rsc.getFlags() != null && rsc.getFlags().contains(ApiConsts.FLAG_DISKLESS); } + + /** + * Checks if all diskful resource are on a zeroed block device. + * @param pool Linstor pool to use + * @param resName Linstor resource name + * @return true if all resources are on a provider with zeroed blocks. + */ + public static boolean resourceSupportZeroBlocks(KVMStoragePool pool, String resName) { + final DevelopersApi api = getLinstorAPI(pool.getSourceHost()); + try { + List resWithVols = api.viewResources( + Collections.emptyList(), + Collections.singletonList(resName), + Collections.emptyList(), + Collections.emptyList(), + null, + null); + + if (resWithVols != null) { + return resWithVols.stream() + .allMatch(res -> { + Volume vol0 = res.getVolumes().get(0); + return vol0 != null && (vol0.getProviderKind() == ProviderKind.LVM_THIN || + vol0.getProviderKind() == ProviderKind.ZFS || + vol0.getProviderKind() == ProviderKind.ZFS_THIN || + vol0.getProviderKind() == ProviderKind.DISKLESS); + } ); + } + } catch (ApiException apiExc) { + LOGGER.error(apiExc.getMessage()); + } + return false; + } } diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 43f6a8a5b87..1b96cd4ef15 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -31,7 +31,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; -import java.util.TimeZone; import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; @@ -765,14 +764,12 @@ import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.LoadBalancerDao; import com.cloud.network.dao.LoadBalancerVO; -import com.cloud.network.dao.NetrisProviderDao; import com.cloud.network.dao.NetworkAccountDao; import com.cloud.network.dao.NetworkAccountVO; 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.NsxProviderDao; import com.cloud.network.vpc.dao.VpcDao; import com.cloud.offering.ServiceOffering; import com.cloud.org.Cluster; @@ -843,7 +840,6 @@ import com.cloud.utils.db.SearchCriteria; 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; @@ -1034,8 +1030,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject DomainRouterDao routerDao; @Inject - public UUIDManager uuidMgr; - @Inject protected UserDataDao userDataDao; @Inject protected VMTemplateDao templateDao; @@ -1057,10 +1051,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe @Inject ResourceLimitService resourceLimitService; @Inject - NsxProviderDao nsxProviderDao; - @Inject - NetrisProviderDao netrisProviderDao; - @Inject ExtensionsManager extensionsManager; private LockControllerListener _lockControllerListener; @@ -1070,8 +1060,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe private Map _configs; - private Map _availableIdsMap; - private List _userAuthenticators; private List _userTwoFactorAuthenticators; private List _userPasswordEncoders; @@ -1155,12 +1143,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe _alertExecutor.scheduleAtFixedRate(new AlertPurgeTask(), alertPurgeInterval, alertPurgeInterval, TimeUnit.SECONDS); } - final String[] availableIds = TimeZone.getAvailableIDs(); - _availableIdsMap = new HashMap<>(availableIds.length); - for (final String id : availableIds) { - _availableIdsMap.put(id, true); - } - supportedHypervisors.add(HypervisorType.KVM); supportedHypervisors.add(HypervisorType.XenServer); @@ -1221,15 +1203,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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"); } @@ -1308,7 +1281,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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(); @@ -1353,8 +1326,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } if (hypervisorType != null) { - String hypervisorStr = (String) hypervisorType; - String hypervisorSearch = HypervisorType.getType(hypervisorStr).toString(); + String hypervisorSearch = HypervisorType.getType(hypervisorType).toString(); sc.setParameters("hypervisorType", hypervisorSearch); } @@ -1491,7 +1463,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 @@ -1560,11 +1532,11 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe validateVgpuProfileForVmMigration(vmProfile); final Type hostType = srcHost.getType(); - Pair, Integer> allHostsPair = null; - List allHosts = null; + Pair, Integer> allHostsPair; + List allHosts; List filteredHosts = null; final Map 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, @@ -1776,7 +1748,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe // Volume must be attached to an instance for live migration. List allPools = new ArrayList<>(); - List suitablePools = new ArrayList<>(); + List suitablePools = new ArrayList<>(); // Volume must be in Ready state to be migrated. if (!Volume.State.Ready.equals(volume.getState())) { @@ -1839,7 +1811,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe suitablePools = findAllSuitableStoragePoolsForDetachedVolume(volume, diskOfferingId, allPools); } removeDataStoreClusterParents((List) allPools); - removeDataStoreClusterParents((List) suitablePools); + removeDataStoreClusterParents(suitablePools); return new Pair<>(allPools, suitablePools); } @@ -1922,7 +1894,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe * Looks for all suitable storage pools to allocate the given volume. * We take into account the service offering of the VM and volume to find suitable storage pools. It is also excluded from the search the current storage pool used by the volume. * We use {@link StoragePoolAllocator} to look for possible storage pools to allocate the given volume. We will look for possible local storage poosl even if the volume is using a shared storage disk offering. - * + *

* Side note: the idea behind this method is to provide power for administrators of manually overriding deployments defined by CloudStack. */ private List findAllSuitableStoragePoolsForVm(final VolumeVO volume, Long diskOfferingId, Long newSize, Long newMinIops, Long newMaxIops, VMInstanceVO vm, Host vmHost, ExcludeList avoid, Cluster srcCluster, HypervisorType hypervisorType, boolean bypassStorageTypeCheck, String keyword) { @@ -2004,7 +1976,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe sb.and("hypervisorVersion", sb.entity().getHypervisorVersion(), SearchCriteria.Op.GTEQ); final String haTag = _haMgr.getHaTag(); - SearchBuilder hostTagSearch = null; + SearchBuilder hostTagSearch; if (haHosts != null && StringUtils.isNotEmpty(haTag)) { hostTagSearch = _hostTagsDao.createSearchBuilder(); if ((Boolean)haHosts) { @@ -2468,7 +2440,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } boolean isAllocatedTemp = isAllocated; - VlanType vlanType = null; + VlanType vlanType; if (forVirtualNetwork != null) { vlanType = forVirtualNetwork ? VlanType.VirtualNetwork : VlanType.DirectAttached; } else { @@ -2541,7 +2513,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe Boolean isRecursive = cmd.isRecursive(); final List permittedAccounts = new ArrayList<>(); ListProjectResourcesCriteria listProjectResourcesCriteria = null; - Boolean isAllocatedOrReserved = false; + boolean isAllocatedOrReserved = false; if (isAllocated || IpAddress.State.Reserved.name().equalsIgnoreCase(state)) { isAllocatedOrReserved = true; } @@ -2590,8 +2562,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (zoneId == null) { zoneId = guestNetwork.getDataCenterId(); } else if (zoneId != guestNetwork.getDataCenterId()) { - InvalidParameterValueException ex = new InvalidParameterValueException("Please specify a valid associated network id in the specified zone."); - throw ex; + throw new InvalidParameterValueException("Please specify a valid associated network id in the specified zone."); } owner = _accountDao.findById(guestNetwork.getAccountId()); } @@ -2633,7 +2604,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } } - if (freeAddrIds.size() > 0) { + if (!freeAddrIds.isEmpty()) { final SearchBuilder sb2 = _publicIpAddressDao.createSearchBuilder(); buildParameters(sb2, cmd, false); sb2.and("ids", sb2.entity().getId(), SearchCriteria.Op.IN); @@ -2687,8 +2658,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (tags != null && !tags.isEmpty()) { final SearchBuilder 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); @@ -2717,7 +2688,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe final Object keyword = cmd.getKeyword(); final Long physicalNetworkId = cmd.getPhysicalNetworkId(); final Long sourceNetworkId = cmd.getNetworkId(); - final Long vpcId = cmd.getVpcId(); Long zone = cmd.getZoneId(); final String address = cmd.getIpAddress(); final Long vlan = cmd.getVlanId(); @@ -2736,8 +2706,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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++; } } @@ -3018,7 +2988,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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())) { @@ -3110,7 +3080,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe logger.debug("GuestOSDetails"); final GuestOSVO guestOsVo = new GuestOSVO(); - guestOsVo.setCategoryId(categoryId.longValue()); + guestOsVo.setCategoryId(categoryId); guestOsVo.setDisplayName(displayName); guestOsVo.setName(name); guestOsVo.setIsUserDefined(true); @@ -3123,8 +3093,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } private void persistGuestOsDetails(Map 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); } } @@ -3369,7 +3339,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 { @@ -3543,9 +3513,9 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe }); - 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"); } @@ -4324,7 +4294,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 oldEvents = _eventDao.listOlderEvents(purgeTime); logger.debug("Found " + oldEvents.size() + " events to be purged"); for (final EventVO event : oldEvents) { @@ -4358,7 +4328,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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 oldAlerts = _alertDao.listOlderAlerts(purgeTime); logger.debug("Found " + oldAlerts.size() + " events to be purged"); for (final AlertVO alert : oldAlerts) { @@ -4677,8 +4647,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe // get the user obj to get their secret key user = _accountMgr.getActiveUser(userId); final String secretKey = user.getSecretKey(); - final String input = cloudIdentifier; - signature = signRequest(input, secretKey); + signature = signRequest(cloudIdentifier, secretKey); } catch (final Exception e) { logger.warn("Exception whilst creating a signature:" + e); } @@ -4697,8 +4666,8 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe final Account caller = getCaller(); final boolean isCallerAdmin = _accountService.isAdmin(caller.getId()); boolean securityGroupsEnabled = false; - boolean elasticLoadBalancerEnabled = false; - boolean KVMSnapshotEnabled = false; + boolean elasticLoadBalancerEnabled; + boolean KVMSnapshotEnabled; String supportELB = "false"; final List networks = networkDao.listSecurityGroupEnabledNetworks(); if (networks != null && !networks.isEmpty()) { @@ -4737,7 +4706,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe // check if region-wide secondary storage is used boolean regionSecondaryEnabled = false; final List imgStores = _imgStoreDao.findRegionImageStores(); - if (imgStores != null && imgStores.size() > 0) { + if (imgStores != null && !imgStores.isEmpty()) { regionSecondaryEnabled = true; } @@ -4780,6 +4749,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe if (isCallerAdmin) { capabilities.put(ApiConstants.EXTENSIONS_PATH, extensionsManager.getExtensionsPath()); } + capabilities.put(ApiConstants.ADDITONAL_CONFIG_ENABLED, UserVmManager.EnableAdditionalVmConfig.valueIn(caller.getId())); return capabilities; } @@ -4801,7 +4771,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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"); @@ -4828,7 +4798,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe public String getVersion() { final Class c = ManagementServer.class; final String fullVersion = c.getPackage().getImplementationVersion(); - if (fullVersion != null && fullVersion.length() > 0) { + if (fullVersion != null && !fullVersion.isEmpty()) { return fullVersion; } @@ -4884,7 +4854,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } } else { try { - logger.debug(String.format("Trying to validate the root certificate format")); + logger.debug("Trying to validate the root certificate format"); CertificateHelper.buildCertificate(certificate); } catch (CertificateException e) { String errorMsg = String.format("Failed to pass certificate validation check with error: Certificate validation failed due to exception: %s", e.getMessage()); @@ -4901,7 +4871,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe final String[] hypervisors = hypers.split(","); if (zoneId != null) { - if (zoneId.longValue() == -1L) { + if (zoneId == -1L) { final List zones = _dcDao.listAll(); for (final String hypervisor : hypervisors) { @@ -5235,8 +5205,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe * @return */ private String getFingerprint(final String publicKey) { - final String fingerprint = SSHKeysHelper.getPublicKeyFingerprint(publicKey); - return fingerprint; + return SSHKeysHelper.getPublicKeyFingerprint(publicKey); } /** @@ -5260,8 +5229,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe protected Account getOwner(final RegisterSSHKeyPairCmd cmd) { final Account caller = getCaller(); - final Account owner = _accountMgr.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId()); - return owner; + return _accountMgr.finalizeOwner(caller, cmd.getAccountName(), cmd.getDomainId(), cmd.getProjectId()); } /** @@ -5277,8 +5245,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe * @return */ protected Account getCaller() { - final Account caller = CallContext.current().getCallingAccount(); - return caller; + return CallContext.current().getCallingAccount(); } private SSHKeyPair createAndSaveSSHKeyPair(final String name, final String fingerprint, final String publicKey, final String privateKey, final Account owner) { @@ -5448,9 +5415,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe eventTypes[i++] = field.get(eventObj).toString(); } return eventTypes; - } catch (final IllegalArgumentException e) { - logger.error("Error while listing Event Types", e); - } catch (final IllegalAccessException e) { + } catch (final IllegalArgumentException | IllegalAccessException e) { logger.error("Error while listing Event Types", e); } return null; @@ -5585,8 +5550,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe } final boolean result = _userVmMgr.upgradeVirtualMachine(cmd.getId(), cmd.getServiceOfferingId(), cmd.getDetails()); if (result) { - final VirtualMachine vm = _vmInstanceDao.findById(cmd.getId()); - return vm; + return _vmInstanceDao.findById(cmd.getId()); } else { throw new CloudRuntimeException("Failed to upgrade System VM"); } @@ -5800,7 +5764,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe 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())); + throw new InvalidParameterValueException(String.format("Unable to find a Management Server with ID equal to [%s].", id)); } if (!ManagementServerHost.State.Down.equals(managementServer.getState())) { diff --git a/server/src/main/java/com/cloud/user/AccountManagerImpl.java b/server/src/main/java/com/cloud/user/AccountManagerImpl.java index b5da605f328..bbfc8fd3682 100644 --- a/server/src/main/java/com/cloud/user/AccountManagerImpl.java +++ b/server/src/main/java/com/cloud/user/AccountManagerImpl.java @@ -2754,7 +2754,10 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M logger.debug("Creating user: " + userName + ", accountId: " + accountId + " timezone:" + timezone); } - passwordPolicy.verifyIfPasswordCompliesWithPasswordPolicies(password, userName, getAccount(accountId).getDomainId()); + Account callingAccount = getCurrentCallingAccount(); + if (callingAccount.getId() != Account.ACCOUNT_ID_SYSTEM) { + passwordPolicy.verifyIfPasswordCompliesWithPasswordPolicies(password, userName, getAccount(accountId).getDomainId()); + } String encodedPassword = null; for (UserAuthenticator authenticator : _userPasswordEncoders) { diff --git a/server/src/main/java/com/cloud/vm/UserVmManager.java b/server/src/main/java/com/cloud/vm/UserVmManager.java index f2a8a672d42..21ac6e3eb36 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManager.java +++ b/server/src/main/java/com/cloud/vm/UserVmManager.java @@ -83,6 +83,15 @@ public interface UserVmManager extends UserVmService { "If set to true, tags specified in `resource.limit.host.tags` are also included in vm.strict.host.tags.", true); + ConfigKey EnableAdditionalVmConfig = new ConfigKey<>( + "Advanced", + Boolean.class, + "enable.additional.vm.configuration", + "false", + "allow additional arbitrary configuration to vm", + true, + ConfigKey.Scope.Account); + static final int MAX_USER_DATA_LENGTH_BYTES = 2048; public static final String CKS_NODE = "cksnode"; diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index a9877b97f84..f1bbfa07292 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -689,9 +689,6 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private static final ConfigKey AllowDeployVmIfGivenHostFails = new ConfigKey("Advanced", Boolean.class, "allow.deploy.vm.if.deploy.on.given.host.fails", "false", "allow vm to deploy on different host if vm fails to deploy on the given host ", true); - private static final ConfigKey EnableAdditionalVmConfig = new ConfigKey<>("Advanced", Boolean.class, - "enable.additional.vm.configuration", "false", "allow additional arbitrary configuration to vm", true, ConfigKey.Scope.Account); - private static final ConfigKey KvmAdditionalConfigAllowList = new ConfigKey<>(String.class, "allow.additional.vm.configuration.list.kvm", "Advanced", "", "Comma separated list of allowed additional configuration options.", true, ConfigKey.Scope.Account, null, null, EnableAdditionalVmConfig.key(), null, null, ConfigKey.Kind.CSV, null); @@ -775,7 +772,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir String networkCidr; String macAddress; - public VmIpAddrFetchThread(long vmId, long nicId, String instanceName, boolean windows, Long hostId, String networkCidr, String macAddress) { + public VmIpAddrFetchThread(long vmId, String vmUuid, long nicId, String instanceName, boolean windows, Long hostId, String networkCidr, String macAddress) { this.vmId = vmId; this.vmUuid = vmUuid; this.nicId = nicId; @@ -797,8 +794,13 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Answer answer = _agentMgr.send(hostId, cmd); if (answer.getResult()) { String vmIp = answer.getDetails(); - - if (NetUtils.isValidIp4(vmIp)) { + if (vmIp == null) { + // we got a valid response and the NIC does not have an IP assigned, as such we will update the database with null + if (nic.getIPv4Address() != null) { + nic.setIPv4Address(null); + _nicDao.update(nicId, nic); + } + } else if (NetUtils.isValidIp4(vmIp)) { // set this vm ip addr in vm nic. if (nic != null) { nic.setIPv4Address(vmIp); @@ -813,12 +815,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir } } } else { - //previously vm has ip and nic table has ip address. After vm restart or stop/start - //if vm doesnot get the ip then set the ip in nic table to null - if (nic.getIPv4Address() != null) { - nic.setIPv4Address(null); - _nicDao.update(nicId, nic); - } + // since no changes are being done, we should not decrement IP usage + decrementCount = false; if (answer.getDetails() != null) { logger.debug("Failed to get vm ip for Vm [id: {}, uuid: {}, name: {}], details: {}", vmId, vmUuid, vmName, answer.getDetails()); @@ -2726,7 +2724,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir VirtualMachineProfile vmProfile = new VirtualMachineProfileImpl(userVm); VirtualMachine vm = vmProfile.getVirtualMachine(); boolean isWindows = _guestOSCategoryDao.findById(_guestOSDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows"); - _vmIpFetchThreadExecutor.execute(new VmIpAddrFetchThread(vmId, nicId, vmInstance.getInstanceName(), + + _vmIpFetchThreadExecutor.execute(new VmIpAddrFetchThread(vmId, vmInstance.getUuid(), nicId, vmInstance.getInstanceName(), isWindows, vm.getHostId(), network.getCidr(), nicVo.getMacAddress())); } @@ -6604,7 +6603,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir protected void persistExtraConfigVmware(String decodedUrl, UserVm vm) { boolean isValidConfig = isValidKeyValuePair(decodedUrl); if (isValidConfig) { - String[] extraConfigs = decodedUrl.split("\\r?\\n"); + String[] extraConfigs = decodedUrl.split("\\r?\\n+"); for (String cfg : extraConfigs) { // Validate cfg against unsupported operations set by admin here String[] allowedKeyList = VmwareAdditionalConfigAllowList.value().split(","); @@ -6632,7 +6631,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir protected void persistExtraConfigXenServer(String decodedUrl, UserVm vm) { boolean isValidConfig = isValidKeyValuePair(decodedUrl); if (isValidConfig) { - String[] extraConfigs = decodedUrl.split("\\r?\\n"); + String[] extraConfigs = decodedUrl.split("\\r?\\n+"); int i = 1; String extraConfigKey = ApiConstants.EXTRA_CONFIG + "-"; for (String cfg : extraConfigs) { @@ -6712,8 +6711,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir // validate config against denied cfg commands validateKvmExtraConfig(decodedUrl, vm.getAccountId()); String[] extraConfigs = decodedUrl.split("\n\n"); + int i = 1; for (String cfg : extraConfigs) { - int i = 1; String[] cfgParts = cfg.split("\n"); String extraConfigKey = ApiConstants.EXTRA_CONFIG; String extraConfigValue; diff --git a/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManager.java b/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManager.java index c7ec7f0c488..377e57b31e9 100644 --- a/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManager.java +++ b/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManager.java @@ -55,6 +55,17 @@ public interface UserPasswordResetManager { "Use auth in the SMTP server for sending emails for resetting password for ACS users", false, ConfigKey.Scope.Global); + ConfigKey UserPasswordResetSMTPUseStartTLS = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, + Boolean.class, "user.password.reset.smtp.useStartTLS", "false", + "If set to true and if we enable security via user.password.reset.smtp.useAuth, this will enable StartTLS to secure the connection.", + true, + ConfigKey.Scope.Global); + + ConfigKey UserPasswordResetSMTPEnabledSecurityProtocols = new ConfigKey(ConfigKey.CATEGORY_ADVANCED, + String.class, "user.password.reset.smtp.enabledSecurityProtocols", "", + "White-space separated security protocols; ex: \"TLSv1 TLSv1.1\". Supported protocols: SSLv2Hello, SSLv3, TLSv1, TLSv1.1 and TLSv1.2", + true, ConfigKey.Kind.WhitespaceSeparatedListWithOptions, "SSLv2Hello,SSLv3,TLSv1,TLSv1.1,TLSv1.2"); + ConfigKey UserPasswordResetSMTPUsername = new ConfigKey<>(ConfigKey.CATEGORY_ADVANCED, String.class, "user.password.reset.smtp.username", null, "Username for SMTP server for sending emails for resetting password for ACS users", diff --git a/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManagerImpl.java b/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManagerImpl.java index 8a278f6e45d..618ad5c8657 100644 --- a/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManagerImpl.java +++ b/server/src/main/java/org/apache/cloudstack/user/UserPasswordResetManagerImpl.java @@ -92,6 +92,8 @@ public class UserPasswordResetManagerImpl extends ManagerBase implements UserPas UserPasswordResetSMTPHost, UserPasswordResetSMTPPort, UserPasswordResetSMTPUseAuth, + UserPasswordResetSMTPUseStartTLS, + UserPasswordResetSMTPEnabledSecurityProtocols, UserPasswordResetSMTPUsername, UserPasswordResetSMTPPassword, UserPasswordResetDomainURL, @@ -106,6 +108,8 @@ public class UserPasswordResetManagerImpl extends ManagerBase implements UserPas Boolean useAuth = UserPasswordResetSMTPUseAuth.value(); String username = UserPasswordResetSMTPUsername.value(); String password = UserPasswordResetSMTPPassword.value(); + Boolean useStartTLS = UserPasswordResetSMTPUseStartTLS.value(); + String enabledSecurityProtocols = UserPasswordResetSMTPEnabledSecurityProtocols.value(); if (!StringUtils.isEmpty(smtpHost) && smtpPort != null && smtpPort > 0) { String namespace = "password.reset.smtp"; @@ -117,6 +121,8 @@ public class UserPasswordResetManagerImpl extends ManagerBase implements UserPas configs.put(getKey(namespace, SMTPMailSender.CONFIG_USE_AUTH), useAuth.toString()); configs.put(getKey(namespace, SMTPMailSender.CONFIG_USERNAME), username); configs.put(getKey(namespace, SMTPMailSender.CONFIG_PASSWORD), password); + configs.put(getKey(namespace, SMTPMailSender.CONFIG_USE_STARTTLS), useStartTLS.toString()); + configs.put(getKey(namespace, SMTPMailSender.CONFIG_ENABLED_SECURITY_PROTOCOLS), enabledSecurityProtocols); mailSender = new SMTPMailSender(configs, namespace); } diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index d3e75d895ad..3cd8e8335ba 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -1051,6 +1051,8 @@ "label.externalid": "External Id", "label.externalloadbalanceripaddress": "External load balancer IP address.", "label.extra": "Extra arguments", +"label.extraconfig": "Additional Configuration", +"label.extraconfig.tooltip": "Additional configuration parameters (extraconfig) to pass to the instance in plain text", "label.f5": "F5", "label.f5.ip.loadbalancer": "F5 BIG-IP load balancer.", "label.failed": "Failed", diff --git a/ui/src/views/compute/DeployVM.vue b/ui/src/views/compute/DeployVM.vue index 854d082075f..423338c9264 100644 --- a/ui/src/views/compute/DeployVM.vue +++ b/ui/src/views/compute/DeployVM.vue @@ -762,6 +762,12 @@ + + + + 0) { deployVmData.userdata = this.$toBase64AndURIEncoded(values.userdata) } + if (values.extraconfig && values.extraconfig.length > 0) { + deployVmData.extraconfig = encodeURIComponent(values.extraconfig) + } // step 2: select template/iso if (this.imageType === 'templateid') { deployVmData.templateid = values.templateid diff --git a/ui/src/views/compute/EditVM.vue b/ui/src/views/compute/EditVM.vue index 7489674b966..bb66f2ccaf3 100644 --- a/ui/src/views/compute/EditVM.vue +++ b/ui/src/views/compute/EditVM.vue @@ -91,6 +91,12 @@ + + + + key.startsWith('extraconfig-')) + .map(key => this.resource.details[key] || '') + .filter(val => val.trim()) + return configs.join('\n\n') + } + }, beforeCreate () { this.apiParams = this.$getApiParams('updateVirtualMachine') }, @@ -225,6 +244,7 @@ export default { haenable: this.resource.haenable, leaseduration: this.resource.leaseduration, leaseexpiryaction: this.resource.leaseexpiryaction + extraconfig: this.combinedExtraConfig }) this.rules = reactive({ leaseduration: [this.naturalNumberRule] @@ -403,6 +423,9 @@ export default { params.leaseexpiryaction = values.leaseexpiryaction } } + if (values.extraconfig && values.extraconfig.length > 0) { + params.extraconfig = encodeURIComponent(values.extraconfig) + } this.loading = true postAPI('updateVirtualMachine', params).then(json => {