mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Merge remote-tracking branch 'apache/4.20'
This commit is contained in:
commit
7abda3b963
@ -196,6 +196,10 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement
|
||||
@Param(description = "true network requires restart")
|
||||
private Boolean restartRequired;
|
||||
|
||||
@SerializedName(ApiConstants.SPECIFY_VLAN)
|
||||
@Param(description = "true if network supports specifying vlan, false otherwise")
|
||||
private Boolean specifyVlan;
|
||||
|
||||
@SerializedName(ApiConstants.SPECIFY_IP_RANGES)
|
||||
@Param(description = "true if network supports specifying ip ranges, false otherwise")
|
||||
private Boolean specifyIpRanges;
|
||||
@ -516,6 +520,10 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement
|
||||
this.restartRequired = restartRequired;
|
||||
}
|
||||
|
||||
public void setSpecifyVlan(Boolean specifyVlan) {
|
||||
this.specifyVlan = specifyVlan;
|
||||
}
|
||||
|
||||
public void setSpecifyIpRanges(Boolean specifyIpRanges) {
|
||||
this.specifyIpRanges = specifyIpRanges;
|
||||
}
|
||||
|
||||
@ -33,11 +33,11 @@ import javax.persistence.Table;
|
||||
import javax.persistence.Transient;
|
||||
|
||||
import org.apache.cloudstack.api.InternalIdentity;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.cloud.utils.db.Encrypt;
|
||||
import com.cloud.utils.db.GenericDao;
|
||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@Entity
|
||||
@Table(name = "user")
|
||||
@ -131,12 +131,6 @@ public class UserAccountVO implements UserAccount, InternalIdentity {
|
||||
public UserAccountVO() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("UserAccount %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields
|
||||
(this, "id", "uuid", "username", "accountName"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getId() {
|
||||
return id;
|
||||
@ -379,4 +373,10 @@ public class UserAccountVO implements UserAccount, InternalIdentity {
|
||||
public void setDetails(Map<String, String> details) {
|
||||
this.details = details;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("UserAccount %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields
|
||||
(this, "id", "uuid", "username", "accountName"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,52 @@
|
||||
-- Licensed to the Apache Software Foundation (ASF) under one
|
||||
-- or more contributor license agreements. See the NOTICE file
|
||||
-- distributed with this work for additional information
|
||||
-- regarding copyright ownership. The ASF licenses this file
|
||||
-- to you under the Apache License, Version 2.0 (the
|
||||
-- "License"); you may not use this file except in compliance
|
||||
-- with the License. You may obtain a copy of the License at
|
||||
--
|
||||
-- http://www.apache.org/licenses/LICENSE-2.0
|
||||
--
|
||||
-- Unless required by applicable law or agreed to in writing,
|
||||
-- software distributed under the License is distributed on an
|
||||
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
-- KIND, either express or implied. See the License for the
|
||||
-- specific language governing permissions and limitations
|
||||
-- under the License.
|
||||
|
||||
DROP PROCEDURE IF EXISTS `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`;
|
||||
|
||||
CREATE PROCEDURE `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION` (
|
||||
IN role VARCHAR(255),
|
||||
IN rule VARCHAR(255),
|
||||
IN permission VARCHAR(255)
|
||||
)
|
||||
BEGIN
|
||||
DECLARE role_id BIGINT(20) UNSIGNED
|
||||
; DECLARE max_sort_order BIGINT(20) UNSIGNED
|
||||
|
||||
; SELECT `r`.`id` INTO role_id
|
||||
FROM `cloud`.`roles` `r`
|
||||
WHERE `r`.`name` = role
|
||||
AND `r`.`is_default` = 1
|
||||
|
||||
; SELECT MAX(`rp`.`sort_order`) INTO max_sort_order
|
||||
FROM `cloud`.`role_permissions` `rp`
|
||||
WHERE `rp`.`role_id` = role_id
|
||||
|
||||
; IF NOT EXISTS (
|
||||
SELECT * FROM `cloud`.`role_permissions` `rp`
|
||||
WHERE `rp`.`role_id` = role_id
|
||||
AND `rp`.`rule` = rule
|
||||
) THEN
|
||||
UPDATE `cloud`.`role_permissions` `rp`
|
||||
SET `rp`.`sort_order` = max_sort_order + 1
|
||||
WHERE `rp`.`sort_order` = max_sort_order
|
||||
AND `rp`.`role_id` = role_id
|
||||
|
||||
; INSERT INTO `cloud`.`role_permissions`
|
||||
(uuid, role_id, rule, permission, sort_order)
|
||||
VALUES (uuid(), role_id, rule, permission, max_sort_order)
|
||||
; END IF
|
||||
;END;
|
||||
@ -21,3 +21,25 @@
|
||||
|
||||
-- Add last_id to the volumes table
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'last_id', 'bigint(20) unsigned DEFAULT NULL');
|
||||
|
||||
-- Grant access to 2FA APIs for the "Read-Only User - Default" role
|
||||
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'setupUserTwoFactorAuthentication', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only User - Default', 'listUserTwoFactorAuthenticatorProviders', 'ALLOW');
|
||||
|
||||
-- Grant access to 2FA APIs for the "Support User - Default" role
|
||||
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'setupUserTwoFactorAuthentication', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support User - Default', 'listUserTwoFactorAuthenticatorProviders', 'ALLOW');
|
||||
|
||||
-- Grant access to 2FA APIs for the "Read-Only Admin - Default" role
|
||||
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Read-Only Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW');
|
||||
|
||||
-- Grant access to 2FA APIs for the "Support Admin - Default" role
|
||||
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'setupUserTwoFactorAuthentication', 'ALLOW');
|
||||
CALL `cloud`.`IDEMPOTENT_UPDATE_API_PERMISSION`('Support Admin - Default', 'validateUserTwoFactorAuthenticationCode', 'ALLOW');
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
-- Add column api_key_access to user and account tables
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the user" AFTER `secret_key`');
|
||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.account', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the account" ');
|
||||
CALL `cloud_usage`.`IDEMPOTENT_ADD_COLUMN`('cloud_usage.account', 'api_key_access', 'boolean DEFAULT NULL COMMENT "is api key access allowed for the account" ');
|
||||
|
||||
-- Modify index for mshost_peer
|
||||
DELETE FROM `cloud`.`mshost_peer`;
|
||||
|
||||
@ -124,18 +124,24 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
|
||||
protected List<StoragePool> reorderPoolsByCapacity(DeploymentPlan plan, List<StoragePool> pools) {
|
||||
Long zoneId = plan.getDataCenterId();
|
||||
Long clusterId = plan.getClusterId();
|
||||
short capacityType;
|
||||
|
||||
if (CollectionUtils.isEmpty(pools)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (pools.get(0).getPoolType().isShared()) {
|
||||
short capacityType = Capacity.CAPACITY_TYPE_LOCAL_STORAGE;
|
||||
String storageType = "local";
|
||||
StoragePool storagePool = pools.get(0);
|
||||
if (storagePool.isShared()) {
|
||||
capacityType = Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED;
|
||||
} else {
|
||||
capacityType = Capacity.CAPACITY_TYPE_LOCAL_STORAGE;
|
||||
storageType = "shared";
|
||||
}
|
||||
|
||||
logger.debug(String.format(
|
||||
"Filtering storage pools by capacity type [%s] as the first storage pool of the list, with name [%s] and ID [%s], is a [%s] storage.",
|
||||
capacityType, storagePool.getName(), storagePool.getUuid(), storageType
|
||||
));
|
||||
|
||||
List<Long> poolIdsByCapacity = capacityDao.orderHostsByFreeCapacity(zoneId, clusterId, capacityType);
|
||||
|
||||
logger.debug(String.format("List of pools in descending order of available capacity [%s].", poolIdsByCapacity));
|
||||
@ -221,6 +227,8 @@ public abstract class AbstractStoragePoolAllocator extends AdapterBase implement
|
||||
}
|
||||
|
||||
List<StoragePool> reorderStoragePoolsBasedOnAlgorithm(List<StoragePool> pools, DeploymentPlan plan, Account account) {
|
||||
logger.debug(String.format("Using allocation algorithm [%s] to reorder pools.", allocationAlgorithm));
|
||||
|
||||
if (allocationAlgorithm.equals("random") || allocationAlgorithm.equals("userconcentratedpod_random") || (account == null)) {
|
||||
reorderRandomPools(pools);
|
||||
} else if (StringUtils.equalsAny(allocationAlgorithm, "userdispersing", "firstfitleastconsumed")) {
|
||||
|
||||
@ -71,7 +71,7 @@ Requires: (openssh-clients or openssh)
|
||||
Requires: (nfs-utils or nfs-client)
|
||||
Requires: iproute
|
||||
Requires: wget
|
||||
Requires: mysql
|
||||
Requires: (mysql or mariadb)
|
||||
Requires: sudo
|
||||
Requires: /sbin/service
|
||||
Requires: /sbin/chkconfig
|
||||
|
||||
@ -203,7 +203,7 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
||||
retryCounter++;
|
||||
try {
|
||||
Pair<Boolean, String> result = SshHelper.sshExecute(ipAddress, port, getControlNodeLoginUser(),
|
||||
pkFile, null, String.format("sudo /opt/bin/kubectl drain %s --ignore-daemonsets --delete-local-data", hostName),
|
||||
pkFile, null, String.format("sudo /opt/bin/kubectl drain %s --ignore-daemonsets --delete-emptydir-data", hostName),
|
||||
10000, 10000, 60000);
|
||||
if (!result.first()) {
|
||||
logger.warn("Draining node: {} on VM: {} in Kubernetes cluster: {} unsuccessful", hostName, userVm, kubernetesCluster);
|
||||
|
||||
@ -527,6 +527,10 @@ public class MockAccountManager extends ManagerBase implements AccountManager {
|
||||
|
||||
@Override
|
||||
public void checkApiAccess(Account account, String command) throws PermissionDeniedException {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +53,7 @@ echo "Downloading Kubernetes tools ${RELEASE}..."
|
||||
k8s_dir="${working_dir}/k8s"
|
||||
mkdir -p "${k8s_dir}"
|
||||
cd "${k8s_dir}"
|
||||
curl -L --remote-name-all https://storage.googleapis.com/kubernetes-release/release/${RELEASE}/bin/linux/amd64/{kubeadm,kubelet,kubectl}
|
||||
curl -L --remote-name-all https://dl.k8s.io/release/${RELEASE}/bin/linux/amd64/{kubeadm,kubelet,kubectl}
|
||||
kubeadm_file_permissions=`stat --format '%a' kubeadm`
|
||||
chmod +x kubeadm
|
||||
|
||||
|
||||
@ -2565,6 +2565,7 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||
response.setIsSystem(networkOffering.isSystemOnly());
|
||||
response.setNetworkOfferingAvailability(networkOffering.getAvailability().toString());
|
||||
response.setIsPersistent(networkOffering.isPersistent());
|
||||
response.setSpecifyVlan(networkOffering.isSpecifyVlan());
|
||||
if (Network.GuestType.Isolated.equals(network.getGuestType()) && network.getVpcId() == null) {
|
||||
response.setEgressDefaultPolicy(networkOffering.isEgressDefaultPolicy());
|
||||
}
|
||||
|
||||
@ -1197,7 +1197,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
domainId = userDomain.getId();
|
||||
}
|
||||
|
||||
final UserAccount userAcct = accountMgr.authenticateUser(username, password, domainId, loginIpAddress, requestParameters);
|
||||
UserAccount userAcct = accountMgr.authenticateUser(username, password, domainId, loginIpAddress, requestParameters);
|
||||
if (userAcct != null) {
|
||||
final String timezone = userAcct.getTimezone();
|
||||
float offsetInHrs = 0f;
|
||||
@ -1242,6 +1242,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||
session.setAttribute("timezoneoffset", Float.valueOf(offsetInHrs).toString());
|
||||
}
|
||||
|
||||
userAcct = accountMgr.clearUserTwoFactorAuthenticationInSetupStateOnLogin(userAcct);
|
||||
boolean is2faEnabled = false;
|
||||
if (userAcct.isUser2faEnabled() || (Boolean.TRUE.equals(AccountManagerImpl.enableUserTwoFactorAuthentication.valueIn(userAcct.getDomainId())) && Boolean.TRUE.equals(AccountManagerImpl.mandateUserTwoFactorAuthentication.valueIn(userAcct.getDomainId())))) {
|
||||
is2faEnabled = true;
|
||||
|
||||
@ -62,8 +62,8 @@ import org.apache.cloudstack.api.command.admin.storage.DeleteObjectStoragePoolCm
|
||||
import org.apache.cloudstack.api.command.admin.storage.DeletePoolCmd;
|
||||
import org.apache.cloudstack.api.command.admin.storage.DeleteSecondaryStagingStoreCmd;
|
||||
import org.apache.cloudstack.api.command.admin.storage.SyncStoragePoolCmd;
|
||||
import org.apache.cloudstack.api.command.admin.storage.UpdateObjectStoragePoolCmd;
|
||||
import org.apache.cloudstack.api.command.admin.storage.UpdateImageStoreCmd;
|
||||
import org.apache.cloudstack.api.command.admin.storage.UpdateObjectStoragePoolCmd;
|
||||
import org.apache.cloudstack.api.command.admin.storage.UpdateStoragePoolCmd;
|
||||
import org.apache.cloudstack.api.command.admin.storage.heuristics.CreateSecondaryStorageSelectorCmd;
|
||||
import org.apache.cloudstack.api.command.admin.storage.heuristics.RemoveSecondaryStorageSelectorCmd;
|
||||
@ -234,8 +234,8 @@ import com.cloud.user.dao.UserDao;
|
||||
import com.cloud.utils.DateUtil;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.UriUtils;
|
||||
import com.cloud.utils.StringUtils;
|
||||
import com.cloud.utils.UriUtils;
|
||||
import com.cloud.utils.component.ComponentContext;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||
@ -499,8 +499,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
|
||||
public boolean isLocalStorageActiveOnHost(Long hostId) {
|
||||
List<StoragePoolHostVO> storagePoolHostRefs = _storagePoolHostDao.listByHostId(hostId);
|
||||
for (StoragePoolHostVO storagePoolHostRef : storagePoolHostRefs) {
|
||||
StoragePoolVO PrimaryDataStoreVO = _storagePoolDao.findById(storagePoolHostRef.getPoolId());
|
||||
if (PrimaryDataStoreVO.getPoolType() == StoragePoolType.LVM || PrimaryDataStoreVO.getPoolType() == StoragePoolType.EXT) {
|
||||
StoragePoolVO primaryDataStoreVO = _storagePoolDao.findById(storagePoolHostRef.getPoolId());
|
||||
if (primaryDataStoreVO != null && (primaryDataStoreVO.getPoolType() == StoragePoolType.LVM || primaryDataStoreVO.getPoolType() == StoragePoolType.EXT)) {
|
||||
SearchBuilder<VolumeVO> volumeSB = volumeDao.createSearchBuilder();
|
||||
volumeSB.and("poolId", volumeSB.entity().getPoolId(), SearchCriteria.Op.EQ);
|
||||
volumeSB.and("removed", volumeSB.entity().getRemoved(), SearchCriteria.Op.NULL);
|
||||
@ -511,7 +511,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
|
||||
volumeSB.join("activeVmSB", activeVmSB, volumeSB.entity().getInstanceId(), activeVmSB.entity().getId(), JoinBuilder.JoinType.INNER);
|
||||
|
||||
SearchCriteria<VolumeVO> volumeSC = volumeSB.create();
|
||||
volumeSC.setParameters("poolId", PrimaryDataStoreVO.getId());
|
||||
volumeSC.setParameters("poolId", primaryDataStoreVO.getId());
|
||||
volumeSC.setParameters("state", Volume.State.Expunging, Volume.State.Destroy);
|
||||
volumeSC.setJoinParameters("activeVmSB", "state", State.Starting, State.Running, State.Stopping, State.Migrating);
|
||||
|
||||
@ -2171,9 +2171,9 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
|
||||
// poolId is null only if volume is destroyed, which has been checked
|
||||
// before.
|
||||
assert poolId != null;
|
||||
StoragePoolVO PrimaryDataStoreVO = _storagePoolDao.findById(poolId);
|
||||
assert PrimaryDataStoreVO != null;
|
||||
return PrimaryDataStoreVO.getUuid();
|
||||
StoragePoolVO primaryDataStoreVO = _storagePoolDao.findById(poolId);
|
||||
assert primaryDataStoreVO != null;
|
||||
return primaryDataStoreVO.getUuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2724,8 +2724,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
|
||||
}
|
||||
|
||||
CapacityVO capacity = new CapacityVO(poolId, zoneId, podId, clusterId, 0, 0, Capacity.CAPACITY_TYPE_STORAGE);
|
||||
for (StoragePoolVO pool : pools) {
|
||||
StorageStats stats = ApiDBUtils.getStoragePoolStatistics(pool.getId());
|
||||
for (StoragePoolVO primaryDataStoreVO : pools) {
|
||||
StorageStats stats = ApiDBUtils.getStoragePoolStatistics(primaryDataStoreVO.getId());
|
||||
if (stats == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -133,7 +133,9 @@ import com.cloud.dc.ClusterDetailsDao;
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.Pod;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.dc.dao.HostPodDao;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.domain.dao.DomainDao;
|
||||
import com.cloud.event.ActionEvent;
|
||||
@ -153,6 +155,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||
import com.cloud.hypervisor.HypervisorCapabilitiesVO;
|
||||
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
|
||||
import com.cloud.offering.DiskOffering;
|
||||
import com.cloud.org.Cluster;
|
||||
import com.cloud.org.Grouping;
|
||||
import com.cloud.projects.Project;
|
||||
import com.cloud.projects.ProjectManager;
|
||||
@ -322,6 +325,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||
@Inject
|
||||
private VmWorkJobDao _workJobDao;
|
||||
@Inject
|
||||
ClusterDao clusterDao;
|
||||
@Inject
|
||||
private ClusterDetailsDao _clusterDetailsDao;
|
||||
@Inject
|
||||
private StorageManager storageMgr;
|
||||
@ -347,6 +352,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||
protected StoragePoolDetailsDao storagePoolDetailsDao;
|
||||
@Inject
|
||||
private BackupDao backupDao;
|
||||
@Inject
|
||||
HostPodDao podDao;
|
||||
|
||||
|
||||
protected Gson _gson;
|
||||
@ -2436,17 +2443,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||
return attachVolumeToVM(command.getVirtualMachineId(), command.getId(), command.getDeviceId(), false);
|
||||
}
|
||||
|
||||
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
|
||||
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
|
||||
|
||||
if (volumeToAttach.isAttachedVM()) {
|
||||
throw new CloudRuntimeException("This volume is already attached to a VM.");
|
||||
}
|
||||
|
||||
UserVmVO vm = _userVmDao.findById(vmId);
|
||||
protected VolumeVO getVmExistingVolumeForVolumeAttach(UserVmVO vm, VolumeInfo volumeToAttach) {
|
||||
VolumeVO existingVolumeOfVm = null;
|
||||
VMTemplateVO template = _templateDao.findById(vm.getTemplateId());
|
||||
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.ROOT);
|
||||
List<VolumeVO> rootVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.ROOT);
|
||||
if (rootVolumesOfVm.size() > 1 && template != null && !template.isDeployAsIs()) {
|
||||
throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state.");
|
||||
} else {
|
||||
@ -2454,7 +2454,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||
existingVolumeOfVm = rootVolumesOfVm.get(0);
|
||||
} else {
|
||||
// locate data volume of the vm
|
||||
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vmId, Volume.Type.DATADISK);
|
||||
List<VolumeVO> diskVolumesOfVm = _volsDao.findByInstanceAndType(vm.getId(), Volume.Type.DATADISK);
|
||||
for (VolumeVO diskVolume : diskVolumesOfVm) {
|
||||
if (diskVolume.getState() != Volume.State.Allocated) {
|
||||
existingVolumeOfVm = diskVolume;
|
||||
@ -2463,41 +2463,89 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||
}
|
||||
}
|
||||
}
|
||||
if (existingVolumeOfVm == null) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
if (existingVolumeOfVm != null) {
|
||||
logger.trace("attaching volume {} to a VM {} with an existing volume {} on primary storage {}",
|
||||
volumeToAttach, vm, existingVolumeOfVm, _storagePoolDao.findById(existingVolumeOfVm.getPoolId()));
|
||||
logger.trace(String.format("No existing volume found for VM (%s/%s) to attach volume %s/%s",
|
||||
vm.getName(), vm.getUuid(),
|
||||
volumeToAttach.getName(), volumeToAttach.getUuid()));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (logger.isTraceEnabled()) {
|
||||
String msg = "attaching volume %s/%s to a VM (%s/%s) with an existing volume %s/%s on primary storage %s";
|
||||
logger.trace(String.format(msg,
|
||||
volumeToAttach.getName(), volumeToAttach.getUuid(),
|
||||
vm.getName(), vm.getUuid(),
|
||||
existingVolumeOfVm.getName(), existingVolumeOfVm.getUuid(),
|
||||
existingVolumeOfVm.getPoolId()));
|
||||
}
|
||||
return existingVolumeOfVm;
|
||||
}
|
||||
|
||||
HypervisorType rootDiskHyperType = vm.getHypervisorType();
|
||||
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
|
||||
protected StoragePool getPoolForAllocatedOrUploadedVolumeForAttach(final VolumeInfo volumeToAttach, final UserVmVO vm) {
|
||||
DataCenter zone = _dcDao.findById(vm.getDataCenterId());
|
||||
Pair<Long, Long> clusterHostId = virtualMachineManager.findClusterAndHostIdForVm(vm, false);
|
||||
long podId = vm.getPodIdToDeployIn();
|
||||
if (clusterHostId.first() != null) {
|
||||
Cluster cluster = clusterDao.findById(clusterHostId.first());
|
||||
podId = cluster.getPodId();
|
||||
}
|
||||
Pod pod = podDao.findById(podId);
|
||||
DiskOfferingVO offering = _diskOfferingDao.findById(volumeToAttach.getDiskOfferingId());
|
||||
DiskProfile diskProfile = new DiskProfile(volumeToAttach.getId(), volumeToAttach.getVolumeType(),
|
||||
volumeToAttach.getName(), volumeToAttach.getId(), volumeToAttach.getSize(), offering.getTagsArray(),
|
||||
offering.isUseLocalStorage(), offering.isRecreatable(),
|
||||
volumeToAttach.getTemplateId());
|
||||
diskProfile.setHyperType(vm.getHypervisorType());
|
||||
StoragePool pool = _volumeMgr.findStoragePool(diskProfile, zone, pod, clusterHostId.first(),
|
||||
clusterHostId.second(), vm, Collections.emptySet());
|
||||
if (pool == null) {
|
||||
throw new CloudRuntimeException(String.format("Failed to find a primary storage for volume in state: %s", volumeToAttach.getState()));
|
||||
}
|
||||
return pool;
|
||||
}
|
||||
|
||||
protected VolumeInfo createVolumeOnPrimaryForAttachIfNeeded(final VolumeInfo volumeToAttach, final UserVmVO vm, VolumeVO existingVolumeOfVm) {
|
||||
VolumeInfo newVolumeOnPrimaryStorage = volumeToAttach;
|
||||
|
||||
boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
|
||||
if (!Arrays.asList(Volume.State.Allocated, Volume.State.Uploaded).contains(volumeToAttach.getState())) {
|
||||
return newVolumeOnPrimaryStorage;
|
||||
}
|
||||
//don't create volume on primary storage if its being attached to the vm which Root's volume hasn't been created yet
|
||||
StoragePoolVO destPrimaryStorage = null;
|
||||
StoragePool destPrimaryStorage = null;
|
||||
if (existingVolumeOfVm != null && !existingVolumeOfVm.getState().equals(Volume.State.Allocated)) {
|
||||
destPrimaryStorage = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
|
||||
if (logger.isTraceEnabled() && destPrimaryStorage != null) {
|
||||
logger.trace("decided on target storage: {}", destPrimaryStorage);
|
||||
}
|
||||
}
|
||||
|
||||
boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
|
||||
|
||||
if (destPrimaryStorage != null && (volumeToAttach.getState() == Volume.State.Allocated || volumeOnSecondary)) {
|
||||
if (destPrimaryStorage == null) {
|
||||
destPrimaryStorage = getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
|
||||
}
|
||||
try {
|
||||
if (volumeOnSecondary && destPrimaryStorage.getPoolType() == Storage.StoragePoolType.PowerFlex) {
|
||||
if (volumeOnSecondary && Storage.StoragePoolType.PowerFlex.equals(destPrimaryStorage.getPoolType())) {
|
||||
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + destPrimaryStorage.getPoolType());
|
||||
}
|
||||
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach, rootDiskHyperType, destPrimaryStorage);
|
||||
newVolumeOnPrimaryStorage = _volumeMgr.createVolumeOnPrimaryStorage(vm, volumeToAttach,
|
||||
vm.getHypervisorType(), destPrimaryStorage);
|
||||
} catch (NoTransitionException e) {
|
||||
logger.debug("Failed to create volume on primary storage", e);
|
||||
throw new CloudRuntimeException("Failed to create volume on primary storage", e);
|
||||
}
|
||||
return newVolumeOnPrimaryStorage;
|
||||
}
|
||||
|
||||
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
|
||||
VolumeInfo volumeToAttach = volFactory.getVolume(volumeId);
|
||||
|
||||
if (volumeToAttach.isAttachedVM()) {
|
||||
throw new CloudRuntimeException("This volume is already attached to a VM.");
|
||||
}
|
||||
|
||||
UserVmVO vm = _userVmDao.findById(vmId);
|
||||
VolumeVO existingVolumeOfVm = getVmExistingVolumeForVolumeAttach(vm, volumeToAttach);
|
||||
VolumeInfo newVolumeOnPrimaryStorage = createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, existingVolumeOfVm);
|
||||
|
||||
// reload the volume from db
|
||||
newVolumeOnPrimaryStorage = volFactory.getVolume(newVolumeOnPrimaryStorage.getId());
|
||||
boolean moveVolumeNeeded = needMoveVolume(existingVolumeOfVm, newVolumeOnPrimaryStorage);
|
||||
@ -2515,19 +2563,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||
StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
|
||||
|
||||
try {
|
||||
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
|
||||
newVolumeOnPrimaryStorage = _volumeMgr.moveVolume(newVolumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(),
|
||||
volumeToAttachHyperType);
|
||||
} catch (ConcurrentOperationException e) {
|
||||
logger.debug("move volume failed", e);
|
||||
throw new CloudRuntimeException("move volume failed", e);
|
||||
} catch (StorageUnavailableException e) {
|
||||
} catch (ConcurrentOperationException | StorageUnavailableException e) {
|
||||
logger.debug("move volume failed", e);
|
||||
throw new CloudRuntimeException("move volume failed", e);
|
||||
}
|
||||
}
|
||||
VolumeVO newVol = _volsDao.findById(newVolumeOnPrimaryStorage.getId());
|
||||
// Getting the fresh vm object in case of volume migration to check the current state of VM
|
||||
if (moveVolumeNeeded || volumeOnSecondary) {
|
||||
if (moveVolumeNeeded) {
|
||||
vm = _userVmDao.findById(vmId);
|
||||
if (vm == null) {
|
||||
throw new InvalidParameterValueException("VM not found.");
|
||||
@ -2712,9 +2758,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||
if (!_volsDao.findByInstanceAndDeviceId(vm.getId(), 0).isEmpty()) {
|
||||
throw new InvalidParameterValueException("Vm already has root volume attached to it");
|
||||
}
|
||||
if (volumeToAttach.getState() == Volume.State.Uploaded) {
|
||||
throw new InvalidParameterValueException("No support for Root volume attach in state " + Volume.State.Uploaded);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -20,7 +20,6 @@ import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
||||
@ -31,6 +30,7 @@ import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.framework.config.Configurable;
|
||||
|
||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||
import com.cloud.api.query.vo.ControlledViewEntity;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
@ -203,4 +203,7 @@ public interface AccountManager extends AccountService, Configurable {
|
||||
void validateUserPasswordAndUpdateIfNeeded(String newPassword, UserVO user, String currentPassword, boolean skipCurrentPassValidation);
|
||||
|
||||
void checkApiAccess(Account caller, String command);
|
||||
|
||||
UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user);
|
||||
|
||||
}
|
||||
|
||||
@ -41,7 +41,6 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import org.apache.cloudstack.acl.APIChecker;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.InfrastructureEntity;
|
||||
@ -120,6 +119,7 @@ import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.OperationTimedoutException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
import com.cloud.exception.ResourceUnavailableException;
|
||||
import com.cloud.host.dao.HostDao;
|
||||
import com.cloud.network.IpAddress;
|
||||
import com.cloud.network.IpAddressManager;
|
||||
import com.cloud.network.Network;
|
||||
@ -1378,7 +1378,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
*/
|
||||
private void checkRoleEscalation(Account caller, Account requested) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(String.format("checking if user of account %s [%s] with role-id [%d] can create an account of type %s [%s] with role-id [%d]",
|
||||
logger.debug(String.format("Checking if user of account %s [%s] with role-id [%d] can create an account of type %s [%s] with role-id [%d]",
|
||||
caller.getAccountName(),
|
||||
caller.getUuid(),
|
||||
caller.getRoleId(),
|
||||
@ -1392,12 +1392,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
checkApiAccess(apiCheckers, requested, command);
|
||||
} catch (PermissionDeniedException pde) {
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace(String.format("checking for permission to \"%s\" is irrelevant as it is not requested for %s [%s]",
|
||||
logger.trace(String.format(
|
||||
"Checking for permission to \"%s\" is irrelevant as it is not requested for %s [%s]",
|
||||
command,
|
||||
pde.getAccount().getAccountName(),
|
||||
pde.getAccount().getUuid(),
|
||||
pde.getEntitiesInViolation()
|
||||
));
|
||||
requested.getAccountName(),
|
||||
requested.getUuid()
|
||||
)
|
||||
);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@ -3566,4 +3567,26 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||
return userTwoFactorAuthenticationProvidersMap.get(name.toLowerCase());
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
|
||||
return Transaction.execute((TransactionCallback<UserAccount>) status -> {
|
||||
if (!user.isUser2faEnabled() && StringUtils.isBlank(user.getUser2faProvider())) {
|
||||
return user;
|
||||
}
|
||||
UserDetailVO userDetailVO = _userDetailsDao.findDetail(user.getId(), UserDetailVO.Setup2FADetail);
|
||||
if (userDetailVO != null && UserAccountVO.Setup2FAstatus.VERIFIED.name().equals(userDetailVO.getValue())) {
|
||||
return user;
|
||||
}
|
||||
logger.info("Clearing 2FA configurations for {} as it is still in setup on a new login request", user);
|
||||
if (userDetailVO != null) {
|
||||
_userDetailsDao.remove(userDetailVO.getId());
|
||||
}
|
||||
UserAccountVO userAccountVO = _userAccountDao.findById(user.getId());
|
||||
userAccountVO.setUser2faEnabled(false);
|
||||
userAccountVO.setUser2faProvider(null);
|
||||
userAccountVO.setKeyFor2fa(null);
|
||||
_userAccountDao.update(user.getId(), userAccountVO);
|
||||
return userAccountVO;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,6 @@ import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.cloud.server.ManagementService;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.api.command.user.volume.CheckAndRepairVolumeCmd;
|
||||
@ -94,8 +93,12 @@ import com.cloud.api.query.dao.ServiceOfferingJoinDao;
|
||||
import com.cloud.configuration.ConfigurationManager;
|
||||
import com.cloud.configuration.Resource;
|
||||
import com.cloud.configuration.Resource.ResourceType;
|
||||
import com.cloud.dc.ClusterVO;
|
||||
import com.cloud.dc.DataCenterVO;
|
||||
import com.cloud.dc.HostPodVO;
|
||||
import com.cloud.dc.dao.ClusterDao;
|
||||
import com.cloud.dc.dao.DataCenterDao;
|
||||
import com.cloud.dc.dao.HostPodDao;
|
||||
import com.cloud.event.EventTypes;
|
||||
import com.cloud.event.UsageEventUtils;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
@ -108,6 +111,7 @@ import com.cloud.org.Grouping;
|
||||
import com.cloud.projects.Project;
|
||||
import com.cloud.projects.ProjectManager;
|
||||
import com.cloud.serializer.GsonHelper;
|
||||
import com.cloud.server.ManagementService;
|
||||
import com.cloud.server.TaggedResourceService;
|
||||
import com.cloud.service.ServiceOfferingVO;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
@ -130,10 +134,12 @@ import com.cloud.utils.Pair;
|
||||
import com.cloud.utils.db.TransactionLegacy;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.cloud.utils.fsm.NoTransitionException;
|
||||
import com.cloud.vm.DiskProfile;
|
||||
import com.cloud.vm.UserVmManager;
|
||||
import com.cloud.vm.UserVmVO;
|
||||
import com.cloud.vm.VirtualMachine;
|
||||
import com.cloud.vm.VirtualMachine.State;
|
||||
import com.cloud.vm.VirtualMachineManager;
|
||||
import com.cloud.vm.dao.UserVmDao;
|
||||
import com.cloud.vm.dao.VMInstanceDao;
|
||||
import com.cloud.vm.snapshot.VMSnapshotVO;
|
||||
@ -209,6 +215,15 @@ public class VolumeApiServiceImplTest {
|
||||
private DataStoreManager dataStoreMgr;
|
||||
@Mock
|
||||
private SnapshotHelper snapshotHelper;
|
||||
@Mock
|
||||
VirtualMachineManager virtualMachineManager;
|
||||
@Mock
|
||||
HostPodDao podDao;
|
||||
@Mock
|
||||
ClusterDao clusterDao;
|
||||
@Mock
|
||||
VolumeOrchestrationService volumeOrchestrationService;
|
||||
|
||||
|
||||
private DetachVolumeCmd detachCmd = new DetachVolumeCmd();
|
||||
private Class<?> _detachCmdClass = detachCmd.getClass();
|
||||
@ -250,9 +265,6 @@ public class VolumeApiServiceImplTest {
|
||||
@Mock
|
||||
private ConfigurationManager _configMgr;
|
||||
|
||||
@Mock
|
||||
private VolumeOrchestrationService _volumeMgr;
|
||||
|
||||
@Mock
|
||||
private ManagementService managementService;
|
||||
|
||||
@ -1925,4 +1937,236 @@ public class VolumeApiServiceImplTest {
|
||||
verify(volumeServiceMock, times(0)).resize(any(VolumeInfo.class));
|
||||
}
|
||||
}
|
||||
private UserVmVO getMockedVm() {
|
||||
UserVmVO vm = Mockito.mock(UserVmVO.class);
|
||||
Mockito.when(vm.getId()).thenReturn(1L);
|
||||
Mockito.when(vm.getTemplateId()).thenReturn(10L);
|
||||
Mockito.when(vm.getHostName()).thenReturn("test-vm");
|
||||
return vm;
|
||||
}
|
||||
|
||||
private VMTemplateVO getMockedTemplate() {
|
||||
VMTemplateVO template = Mockito.mock(VMTemplateVO.class);
|
||||
Mockito.when(template.isDeployAsIs()).thenReturn(false);
|
||||
return template;
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testGetVmExistingVolumeForVolumeAttach_MultipleRootVolumes_ThrowsException() {
|
||||
UserVmVO vm = getMockedVm();
|
||||
VMTemplateVO template = getMockedTemplate();
|
||||
when(templateDao.findById(10L)).thenReturn(template);
|
||||
when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT))
|
||||
.thenReturn(Arrays.asList(Mockito.mock(VolumeVO.class), Mockito.mock(VolumeVO.class)));
|
||||
volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetVmExistingVolumeForVolumeAttach_SingleRootVolume() {
|
||||
UserVmVO vm = getMockedVm();
|
||||
VMTemplateVO template = getMockedTemplate();
|
||||
VolumeVO rootVolume = Mockito.mock(VolumeVO.class);
|
||||
Mockito.when(rootVolume.getId()).thenReturn(20L);
|
||||
Mockito.when(templateDao.findById(10L)).thenReturn(template);
|
||||
Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT))
|
||||
.thenReturn(Collections.singletonList(rootVolume));
|
||||
VolumeVO result = volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class));
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(20L, result.getId());
|
||||
}
|
||||
|
||||
private VolumeVO getMockedDataVolume() {
|
||||
VolumeVO volume = Mockito.mock(VolumeVO.class);
|
||||
Mockito.when(volume.getId()).thenReturn(30L);
|
||||
Mockito.when(volume.getState()).thenReturn(Volume.State.Ready);
|
||||
return volume;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetVmExistingVolumeForVolumeAttach_NoRootVolume_DataDiskAvailable() {
|
||||
UserVmVO vm = getMockedVm();
|
||||
VMTemplateVO template = getMockedTemplate();
|
||||
VolumeVO dataDisk = getMockedDataVolume();
|
||||
List<VolumeVO> rootVolumes = Collections.emptyList();
|
||||
List<VolumeVO> dataVolumes = Collections.singletonList(dataDisk);
|
||||
Mockito.when(templateDao.findById(10L)).thenReturn(template);
|
||||
Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT)).thenReturn(rootVolumes);
|
||||
Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.DATADISK)).thenReturn(dataVolumes);
|
||||
VolumeVO result = volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class));
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(30L, result.getId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetVmExistingVolumeForVolumeAttach_NoVolumesAtAll() {
|
||||
UserVmVO vm = getMockedVm();
|
||||
VMTemplateVO template = getMockedTemplate();
|
||||
Mockito.when(templateDao.findById(10L)).thenReturn(template);
|
||||
Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.ROOT)).thenReturn(Collections.emptyList());
|
||||
Mockito.when(volumeDaoMock.findByInstanceAndType(1L, Volume.Type.DATADISK)).thenReturn(Collections.emptyList());
|
||||
VolumeVO result = volumeApiServiceImpl.getVmExistingVolumeForVolumeAttach(vm, Mockito.mock(VolumeInfo.class));
|
||||
Assert.assertNull(result);
|
||||
}
|
||||
|
||||
private void mockDiskOffering() {
|
||||
DiskOfferingVO offering = Mockito.mock(DiskOfferingVO.class);
|
||||
Mockito.when(_diskOfferingDao.findById(1L)).thenReturn(offering);
|
||||
Mockito.when(offering.isUseLocalStorage()).thenReturn(true);
|
||||
Mockito.when(offering.isRecreatable()).thenReturn(false);
|
||||
}
|
||||
|
||||
private DataCenterVO mockZone() {
|
||||
DataCenterVO zone = Mockito.mock(DataCenterVO.class);
|
||||
Mockito.when(_dcDao.findById(1L)).thenReturn(zone);
|
||||
return zone;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPoolForAllocatedOrUploadedVolumeForAttach_Success() {
|
||||
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
|
||||
UserVmVO vm = Mockito.mock(UserVmVO.class);
|
||||
ClusterVO cluster = Mockito.mock(ClusterVO.class);
|
||||
HostPodVO pod = Mockito.mock(HostPodVO.class);
|
||||
DataCenterVO zone = mockZone();
|
||||
mockDiskOffering();
|
||||
StoragePool pool = Mockito.mock(StoragePool.class);
|
||||
when(vm.getDataCenterId()).thenReturn(1L);
|
||||
when(virtualMachineManager.findClusterAndHostIdForVm(vm, false)).thenReturn(new Pair<>(1L, 2L));
|
||||
when(clusterDao.findById(1L)).thenReturn(cluster);
|
||||
when(cluster.getPodId()).thenReturn(1L);
|
||||
when(podDao.findById(1L)).thenReturn(pod);
|
||||
when(volumeToAttach.getDiskOfferingId()).thenReturn(1L);
|
||||
when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(1L), eq(2L), eq(vm), eq(Collections.emptySet())))
|
||||
.thenReturn(pool);
|
||||
StoragePool result = volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(pool, result);
|
||||
}
|
||||
|
||||
@Test(expected = CloudRuntimeException.class)
|
||||
public void testGetPoolForAllocatedOrUploadedVolumeForAttach_NoPoolFound_ThrowsException() {
|
||||
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
|
||||
UserVmVO vm = Mockito.mock(UserVmVO.class);
|
||||
DataCenterVO zone = mockZone();
|
||||
Pair<Long, Long> clusterHostId = new Pair<>(1L, 2L);
|
||||
ClusterVO cluster = Mockito.mock(ClusterVO.class);
|
||||
HostPodVO pod = Mockito.mock(HostPodVO.class);
|
||||
mockDiskOffering();
|
||||
when(vm.getDataCenterId()).thenReturn(1L);
|
||||
when(clusterDao.findById(1L)).thenReturn(cluster);
|
||||
when(virtualMachineManager.findClusterAndHostIdForVm(vm, false)).thenReturn(clusterHostId);
|
||||
when(podDao.findById(anyLong())).thenReturn(pod);
|
||||
when(volumeToAttach.getDiskOfferingId()).thenReturn(1L);
|
||||
when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(1L), eq(2L), eq(vm), eq(Collections.emptySet())))
|
||||
.thenReturn(null);
|
||||
volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPoolForAllocatedOrUploadedVolumeForAttach_NoCluster() {
|
||||
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
|
||||
UserVmVO vm = Mockito.mock(UserVmVO.class);
|
||||
DataCenterVO zone = mockZone();
|
||||
HostPodVO pod = Mockito.mock(HostPodVO.class);
|
||||
mockDiskOffering();
|
||||
StoragePool pool = Mockito.mock(StoragePool.class);
|
||||
when(vm.getDataCenterId()).thenReturn(1L);
|
||||
when(vm.getPodIdToDeployIn()).thenReturn(2L);
|
||||
when(virtualMachineManager.findClusterAndHostIdForVm(vm, false)).thenReturn(new Pair<>(null, 2L));
|
||||
when(podDao.findById(2L)).thenReturn(pod);
|
||||
when(volumeToAttach.getDiskOfferingId()).thenReturn(1L);
|
||||
when(volumeOrchestrationService.findStoragePool(any(DiskProfile.class), eq(zone), eq(pod), eq(null), eq(2L), eq(vm), eq(Collections.emptySet())))
|
||||
.thenReturn(pool);
|
||||
StoragePool result = volumeApiServiceImpl.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertEquals(pool, result);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreateVolumeOnSecondaryForAttachIfNeeded_VolumeNotAllocatedOrUploaded() {
|
||||
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
|
||||
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Ready);
|
||||
VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(
|
||||
volumeToAttach, Mockito.mock(UserVmVO.class), null);
|
||||
Assert.assertSame(volumeToAttach, result);
|
||||
Mockito.verifyNoInteractions(primaryDataStoreDaoMock, volumeOrchestrationService);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateVolumeOnSecondaryForAttachIfNeeded_ExistingVolumeDeterminesStoragePool() {
|
||||
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
|
||||
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded);
|
||||
UserVmVO vm = Mockito.mock(UserVmVO.class);
|
||||
VolumeVO existingVolume = Mockito.mock(VolumeVO.class);
|
||||
Mockito.when(existingVolume.getState()).thenReturn(Volume.State.Ready);
|
||||
when(existingVolume.getPoolId()).thenReturn(1L);
|
||||
StoragePoolVO destPrimaryStorage = Mockito.mock(StoragePoolVO.class);
|
||||
Mockito.when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
Mockito.when(primaryDataStoreDaoMock.findById(1L)).thenReturn(destPrimaryStorage);
|
||||
VolumeInfo newVolumeOnPrimaryStorage = Mockito.mock(VolumeInfo.class);
|
||||
try {
|
||||
Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(vm, volumeToAttach, vm.getHypervisorType(), destPrimaryStorage))
|
||||
.thenReturn(newVolumeOnPrimaryStorage);
|
||||
} catch (NoTransitionException nte) {
|
||||
Assert.fail(nte.getMessage());
|
||||
}
|
||||
VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, existingVolume);
|
||||
Assert.assertSame(newVolumeOnPrimaryStorage, result);
|
||||
Mockito.verify(primaryDataStoreDaoMock).findById(1L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateVolumeOnPrimaryForAttachIfNeeded_UsesGetPoolForAttach() {
|
||||
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
|
||||
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Allocated);
|
||||
UserVmVO vm = Mockito.mock(UserVmVO.class);
|
||||
StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class);
|
||||
Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl)
|
||||
.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
|
||||
VolumeInfo newVolumeOnPrimaryStorage = Mockito.mock(VolumeInfo.class);
|
||||
try {
|
||||
Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(
|
||||
vm, volumeToAttach, vm.getHypervisorType(), destPrimaryStorage))
|
||||
.thenReturn(newVolumeOnPrimaryStorage);
|
||||
} catch (NoTransitionException nte) {
|
||||
Assert.fail(nte.getMessage());
|
||||
}
|
||||
VolumeInfo result = volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null);
|
||||
Assert.assertSame(newVolumeOnPrimaryStorage, result);
|
||||
verify(volumeApiServiceImpl).getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
|
||||
}
|
||||
|
||||
@Test(expected = InvalidParameterValueException.class)
|
||||
public void testCreateVolumeOnPrimaryForAttachIfNeeded_UnsupportedPoolType_ThrowsException() {
|
||||
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
|
||||
when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded);
|
||||
UserVmVO vm = Mockito.mock(UserVmVO.class);
|
||||
StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class);
|
||||
when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.PowerFlex);
|
||||
Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl)
|
||||
.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
|
||||
volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateVolumeOnSecondaryForAttachIfNeeded_CreateVolumeFails_ThrowsException() {
|
||||
VolumeInfo volumeToAttach = Mockito.mock(VolumeInfo.class);
|
||||
Mockito.when(volumeToAttach.getState()).thenReturn(Volume.State.Uploaded);
|
||||
UserVmVO vm = Mockito.mock(UserVmVO.class);
|
||||
StoragePool destPrimaryStorage = Mockito.mock(StoragePool.class);
|
||||
Mockito.when(destPrimaryStorage.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
|
||||
Mockito.doReturn(destPrimaryStorage).when(volumeApiServiceImpl)
|
||||
.getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
|
||||
try {
|
||||
Mockito.when(volumeOrchestrationService.createVolumeOnPrimaryStorage(vm, volumeToAttach, vm.getHypervisorType(), destPrimaryStorage))
|
||||
.thenThrow(new NoTransitionException("Mocked exception"));
|
||||
} catch (NoTransitionException nte) {
|
||||
Assert.fail(nte.getMessage());
|
||||
}
|
||||
CloudRuntimeException exception = Assert.assertThrows(CloudRuntimeException.class, () ->
|
||||
volumeApiServiceImpl.createVolumeOnPrimaryForAttachIfNeeded(volumeToAttach, vm, null)
|
||||
);
|
||||
Assert.assertTrue(exception.getMessage().contains("Failed to create volume on primary storage"));
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,8 +26,6 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.Role;
|
||||
import org.apache.cloudstack.acl.RoleService;
|
||||
@ -43,23 +41,25 @@ import org.apache.cloudstack.auth.UserAuthenticator.ActionOnFailedAuthentication
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.context.CallContext;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.apache.cloudstack.resourcedetail.UserDetailVO;
|
||||
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InOrder;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockedStatic;
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
|
||||
import com.cloud.acl.DomainChecker;
|
||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||
import com.cloud.domain.Domain;
|
||||
import com.cloud.domain.DomainVO;
|
||||
import com.cloud.event.ActionEventUtils;
|
||||
import com.cloud.exception.ConcurrentOperationException;
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.exception.PermissionDeniedException;
|
||||
@ -1356,4 +1356,54 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
||||
Mockito.when(_projectAccountDao.listAdministratedProjectIds(accountId)).thenReturn(managedProjectIds);
|
||||
accountManagerImpl.checkIfAccountManagesProjects(accountId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearUser2FA_When2FADisabled_NoChanges() {
|
||||
UserAccount user = Mockito.mock(UserAccount.class);
|
||||
Mockito.when(user.isUser2faEnabled()).thenReturn(false);
|
||||
Mockito.when(user.getUser2faProvider()).thenReturn(null);
|
||||
UserAccount result = accountManagerImpl.clearUserTwoFactorAuthenticationInSetupStateOnLogin(user);
|
||||
Assert.assertSame(user, result);
|
||||
Mockito.verifyNoInteractions(userDetailsDaoMock, userAccountDaoMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearUser2FA_When2FAInVerifiedState_NoChanges() {
|
||||
UserAccount user = Mockito.mock(UserAccount.class);
|
||||
Mockito.when(user.getId()).thenReturn(1L);
|
||||
Mockito.when(user.isUser2faEnabled()).thenReturn(true);
|
||||
UserDetailVO userDetail = new UserDetailVO();
|
||||
userDetail.setValue(UserAccountVO.Setup2FAstatus.VERIFIED.name());
|
||||
Mockito.when(userDetailsDaoMock.findDetail(1L, UserDetailVO.Setup2FADetail)).thenReturn(userDetail);
|
||||
UserAccount result = accountManagerImpl.clearUserTwoFactorAuthenticationInSetupStateOnLogin(user);
|
||||
Assert.assertSame(user, result);
|
||||
Mockito.verify(userDetailsDaoMock).findDetail(1L, UserDetailVO.Setup2FADetail);
|
||||
Mockito.verifyNoMoreInteractions(userDetailsDaoMock, userAccountDaoMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearUser2FA_When2FAInSetupState_Disable2FA() {
|
||||
UserAccount user = Mockito.mock(UserAccount.class);
|
||||
Mockito.when(user.getId()).thenReturn(1L);
|
||||
Mockito.when(user.isUser2faEnabled()).thenReturn(true);
|
||||
UserDetailVO userDetail = new UserDetailVO();
|
||||
userDetail.setValue(UserAccountVO.Setup2FAstatus.ENABLED.name());
|
||||
UserAccountVO userAccountVO = new UserAccountVO();
|
||||
userAccountVO.setId(1L);
|
||||
Mockito.when(userDetailsDaoMock.findDetail(1L, UserDetailVO.Setup2FADetail)).thenReturn(userDetail);
|
||||
Mockito.when(userAccountDaoMock.findById(1L)).thenReturn(userAccountVO);
|
||||
UserAccount result = accountManagerImpl.clearUserTwoFactorAuthenticationInSetupStateOnLogin(user);
|
||||
Assert.assertNotNull(result);
|
||||
Assert.assertFalse(result.isUser2faEnabled());
|
||||
Assert.assertNull(result.getUser2faProvider());
|
||||
Mockito.verify(userDetailsDaoMock).findDetail(1L, UserDetailVO.Setup2FADetail);
|
||||
Mockito.verify(userDetailsDaoMock).remove(Mockito.anyLong());
|
||||
Mockito.verify(userAccountDaoMock).findById(1L);
|
||||
ArgumentCaptor<UserAccountVO> captor = ArgumentCaptor.forClass(UserAccountVO.class);
|
||||
Mockito.verify(userAccountDaoMock).update(Mockito.eq(1L), captor.capture());
|
||||
UserAccountVO updatedUser = captor.getValue();
|
||||
Assert.assertFalse(updatedUser.isUser2faEnabled());
|
||||
Assert.assertNull(updatedUser.getUser2faProvider());
|
||||
Assert.assertNull(updatedUser.getKeyFor2fa());
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,27 @@
|
||||
// under the License.
|
||||
package com.cloud.user;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.RegisterCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||
import com.cloud.api.query.vo.ControlledViewEntity;
|
||||
import com.cloud.dc.DataCenter;
|
||||
@ -34,25 +55,6 @@ import com.cloud.utils.component.Manager;
|
||||
import com.cloud.utils.component.ManagerBase;
|
||||
import com.cloud.utils.db.SearchBuilder;
|
||||
import com.cloud.utils.db.SearchCriteria;
|
||||
import org.apache.cloudstack.acl.ControlledEntity;
|
||||
import org.apache.cloudstack.acl.RoleType;
|
||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||
import org.apache.cloudstack.api.command.admin.account.CreateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.DeleteUserCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.GetUserKeysCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.MoveUserCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.RegisterCmd;
|
||||
import org.apache.cloudstack.api.command.admin.user.UpdateUserCmd;
|
||||
import org.apache.cloudstack.api.response.UserTwoFactorAuthenticationSetupResponse;
|
||||
import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
import java.net.InetAddress;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Component
|
||||
public class MockAccountManagerImpl extends ManagerBase implements Manager, AccountManager {
|
||||
@ -496,4 +498,9 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco
|
||||
@Override
|
||||
public void validateUserPasswordAndUpdateIfNeeded(String newPassword, UserVO user, String currentPassword, boolean skipCurrentPassValidation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
<a-menu>
|
||||
<a-menu-item v-for="(column, idx) in columnKeys" :key="idx" @click="updateSelectedColumns(column)">
|
||||
<a-checkbox :id="idx.toString()" :checked="selectedColumns.includes(getColumnKey(column))"/>
|
||||
{{ $t('label.' + String(getColumTitle(column)).toLowerCase()) }}
|
||||
{{ $t('label.' + String(getColumnTitle(column)).toLowerCase()) }}
|
||||
</a-menu-item>
|
||||
</a-menu>
|
||||
</div>
|
||||
@ -1005,16 +1005,16 @@ export default {
|
||||
return host.state
|
||||
},
|
||||
getColumnKey (name) {
|
||||
if (typeof name === 'object') {
|
||||
name = Object.keys(name).includes('field') ? name.field : name.customTitle
|
||||
}
|
||||
if (typeof name !== 'object' || name === null) {
|
||||
return name
|
||||
}
|
||||
return name.field ?? name.customTitle ?? Object.keys(name)[0]
|
||||
},
|
||||
getColumTitle (name) {
|
||||
if (typeof name === 'object') {
|
||||
name = Object.keys(name).includes('customTitle') ? name.customTitle : name.field
|
||||
}
|
||||
getColumnTitle (name) {
|
||||
if (typeof name !== 'object' || name === null) {
|
||||
return name
|
||||
}
|
||||
return name.customTitle ?? name.field ?? Object.keys(name)[0]
|
||||
},
|
||||
handleResizeColumn (w, col) {
|
||||
col.width = w
|
||||
|
||||
@ -680,6 +680,9 @@ export default {
|
||||
if (accountIndex > -1) {
|
||||
this.fields[accountIndex].loading = false
|
||||
}
|
||||
if (hypervisorIndex > -1) {
|
||||
this.fields[hypervisorIndex].loading = false
|
||||
}
|
||||
if (imageStoreIndex > -1) {
|
||||
this.fields[imageStoreIndex].loading = false
|
||||
}
|
||||
|
||||
@ -140,7 +140,10 @@ export default {
|
||||
icon: 'edit-outlined',
|
||||
label: 'label.update.network',
|
||||
dataView: true,
|
||||
disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
|
||||
disabled: (record, user) => {
|
||||
return (!record.projectid && (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype))) ||
|
||||
(record.type === 'Shared' && record.specifyvlan && !['Admin'].includes(user.userInfo.roletype))
|
||||
},
|
||||
popup: true,
|
||||
component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateNetwork.vue')))
|
||||
},
|
||||
@ -150,7 +153,10 @@ export default {
|
||||
label: 'label.restart.network',
|
||||
message: 'message.restart.network',
|
||||
dataView: true,
|
||||
disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
|
||||
disabled: (record, user) => {
|
||||
return (!record.projectid && (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype))) ||
|
||||
(record.type === 'Shared' && record.specifyvlan && !['Admin'].includes(user.userInfo.roletype))
|
||||
},
|
||||
args: (record, store, isGroupAction) => {
|
||||
var fields = []
|
||||
if (isGroupAction || record.vpcid == null) {
|
||||
@ -189,7 +195,10 @@ export default {
|
||||
label: 'label.action.delete.network',
|
||||
message: 'message.action.delete.network',
|
||||
dataView: true,
|
||||
disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
|
||||
disabled: (record, user) => {
|
||||
return (!record.projectid && (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype))) ||
|
||||
(record.type === 'Shared' && record.specifyvlan && !['Admin'].includes(user.userInfo.roletype))
|
||||
},
|
||||
groupAction: true,
|
||||
popup: true,
|
||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||
|
||||
@ -56,6 +56,7 @@
|
||||
:loading="loading"
|
||||
:tabs="$route.meta.tabs" />
|
||||
<tree-view
|
||||
v-else
|
||||
:key="treeViewKey"
|
||||
:treeData="treeData"
|
||||
:treeSelected="treeSelected"
|
||||
@ -140,6 +141,15 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
watch: {
|
||||
'$route' (to, from) {
|
||||
// When the route changes from /domain/:id to /domain or vice versa, the component is not destroyed and created again
|
||||
// So, we need to watch the route params to fetch the data again to update the component
|
||||
if (to.path.startsWith('/domain') && from.params.id !== to.params.id) {
|
||||
this.fetchData()
|
||||
}
|
||||
}
|
||||
},
|
||||
provide () {
|
||||
return {
|
||||
parentCloseAction: this.closeAction,
|
||||
|
||||
@ -39,6 +39,7 @@
|
||||
@keydown.esc="editableValueKey = null"
|
||||
@pressEnter="updateConfigurationValue(configrecord)"
|
||||
@change="value => setConfigurationEditable(configrecord, value)"
|
||||
@keydown="e => handleInputNumberKeyDown(e, false)"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
@ -52,6 +53,7 @@
|
||||
@keydown.esc="editableValueKey = null"
|
||||
@pressEnter="updateConfigurationValue(configrecord)"
|
||||
@change="value => setConfigurationEditable(configrecord, value)"
|
||||
@keydown="e => handleInputNumberKeyDown(e, true)"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</span>
|
||||
@ -87,6 +89,7 @@
|
||||
@keydown.esc="editableValueKey = null"
|
||||
@pressEnter="updateConfigurationValue(configrecord)"
|
||||
@change="value => setConfigurationEditable(configrecord, value)"
|
||||
@keydown="e => handleInputNumberKeyDown(e, true)"
|
||||
/>
|
||||
</a-tooltip>
|
||||
</a-col>
|
||||
@ -365,6 +368,26 @@ export default {
|
||||
} else {
|
||||
this.editableValueKey = null
|
||||
}
|
||||
},
|
||||
handleInputNumberKeyDown (event, isDecimal) {
|
||||
const allowedCodes = ['Backspace', 'Delete', 'ArrowLeft', 'ArrowRight', 'Minus']
|
||||
|
||||
if (isDecimal) {
|
||||
allowedCodes.push('Period')
|
||||
}
|
||||
|
||||
if (
|
||||
event.getModifierState('Control') ||
|
||||
event.getModifierState('Meta') ||
|
||||
event.getModifierState('Alt')
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
const isValid = allowedCodes.includes(event.code) || !isNaN(event.key)
|
||||
if (!isValid) {
|
||||
event.preventDefault()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user