mirror of https://github.com/apache/cloudstack.git
Merge branch '4.19' into 4.20
This commit is contained in:
commit
9c9876094e
|
|
@ -196,6 +196,10 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement
|
||||||
@Param(description = "true network requires restart")
|
@Param(description = "true network requires restart")
|
||||||
private Boolean restartRequired;
|
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)
|
@SerializedName(ApiConstants.SPECIFY_IP_RANGES)
|
||||||
@Param(description = "true if network supports specifying ip ranges, false otherwise")
|
@Param(description = "true if network supports specifying ip ranges, false otherwise")
|
||||||
private Boolean specifyIpRanges;
|
private Boolean specifyIpRanges;
|
||||||
|
|
@ -516,6 +520,10 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement
|
||||||
this.restartRequired = restartRequired;
|
this.restartRequired = restartRequired;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setSpecifyVlan(Boolean specifyVlan) {
|
||||||
|
this.specifyVlan = specifyVlan;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSpecifyIpRanges(Boolean specifyIpRanges) {
|
public void setSpecifyIpRanges(Boolean specifyIpRanges) {
|
||||||
this.specifyIpRanges = specifyIpRanges;
|
this.specifyIpRanges = specifyIpRanges;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ import org.apache.cloudstack.api.InternalIdentity;
|
||||||
|
|
||||||
import com.cloud.utils.db.Encrypt;
|
import com.cloud.utils.db.Encrypt;
|
||||||
import com.cloud.utils.db.GenericDao;
|
import com.cloud.utils.db.GenericDao;
|
||||||
|
|
||||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
|
||||||
|
|
@ -131,12 +132,6 @@ public class UserAccountVO implements UserAccount, InternalIdentity {
|
||||||
public UserAccountVO() {
|
public UserAccountVO() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format("UserAccount %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields
|
|
||||||
(this, "id", "uuid", "username", "accountName"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getId() {
|
public long getId() {
|
||||||
return id;
|
return id;
|
||||||
|
|
@ -379,4 +374,9 @@ public class UserAccountVO implements UserAccount, InternalIdentity {
|
||||||
public void setDetails(Map<String, String> details) {
|
public void setDetails(Map<String, String> details) {
|
||||||
this.details = details;
|
this.details = details;
|
||||||
}
|
}
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("UserAccount %s.", ReflectionToStringBuilderUtils.reflectOnlySelectedFields
|
||||||
|
(this, "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
|
-- Add last_id to the volumes table
|
||||||
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'last_id', 'bigint(20) unsigned DEFAULT NULL');
|
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');
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ Requires: (openssh-clients or openssh)
|
||||||
Requires: (nfs-utils or nfs-client)
|
Requires: (nfs-utils or nfs-client)
|
||||||
Requires: iproute
|
Requires: iproute
|
||||||
Requires: wget
|
Requires: wget
|
||||||
Requires: mysql
|
Requires: (mysql or mariadb)
|
||||||
Requires: sudo
|
Requires: sudo
|
||||||
Requires: /sbin/service
|
Requires: /sbin/service
|
||||||
Requires: /sbin/chkconfig
|
Requires: /sbin/chkconfig
|
||||||
|
|
|
||||||
|
|
@ -203,7 +203,7 @@ public class KubernetesClusterScaleWorker extends KubernetesClusterResourceModif
|
||||||
retryCounter++;
|
retryCounter++;
|
||||||
try {
|
try {
|
||||||
Pair<Boolean, String> result = SshHelper.sshExecute(ipAddress, port, getControlNodeLoginUser(),
|
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);
|
10000, 10000, 60000);
|
||||||
if (!result.first()) {
|
if (!result.first()) {
|
||||||
logger.warn("Draining node: {} on VM: {} in Kubernetes cluster: {} unsuccessful", hostName, userVm, kubernetesCluster);
|
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
|
@Override
|
||||||
public void checkApiAccess(Account account, String command) throws PermissionDeniedException {
|
public void checkApiAccess(Account account, String command) throws PermissionDeniedException {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2565,6 +2565,7 @@ public class ApiResponseHelper implements ResponseGenerator {
|
||||||
response.setIsSystem(networkOffering.isSystemOnly());
|
response.setIsSystem(networkOffering.isSystemOnly());
|
||||||
response.setNetworkOfferingAvailability(networkOffering.getAvailability().toString());
|
response.setNetworkOfferingAvailability(networkOffering.getAvailability().toString());
|
||||||
response.setIsPersistent(networkOffering.isPersistent());
|
response.setIsPersistent(networkOffering.isPersistent());
|
||||||
|
response.setSpecifyVlan(networkOffering.isSpecifyVlan());
|
||||||
if (Network.GuestType.Isolated.equals(network.getGuestType()) && network.getVpcId() == null) {
|
if (Network.GuestType.Isolated.equals(network.getGuestType()) && network.getVpcId() == null) {
|
||||||
response.setEgressDefaultPolicy(networkOffering.isEgressDefaultPolicy());
|
response.setEgressDefaultPolicy(networkOffering.isEgressDefaultPolicy());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1190,7 +1190,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||||
domainId = userDomain.getId();
|
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) {
|
if (userAcct != null) {
|
||||||
final String timezone = userAcct.getTimezone();
|
final String timezone = userAcct.getTimezone();
|
||||||
float offsetInHrs = 0f;
|
float offsetInHrs = 0f;
|
||||||
|
|
@ -1235,6 +1235,7 @@ public class ApiServer extends ManagerBase implements HttpRequestHandler, ApiSer
|
||||||
session.setAttribute("timezoneoffset", Float.valueOf(offsetInHrs).toString());
|
session.setAttribute("timezoneoffset", Float.valueOf(offsetInHrs).toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
userAcct = accountMgr.clearUserTwoFactorAuthenticationInSetupStateOnLogin(userAcct);
|
||||||
boolean is2faEnabled = false;
|
boolean is2faEnabled = false;
|
||||||
if (userAcct.isUser2faEnabled() || (Boolean.TRUE.equals(AccountManagerImpl.enableUserTwoFactorAuthentication.valueIn(userAcct.getDomainId())) && Boolean.TRUE.equals(AccountManagerImpl.mandateUserTwoFactorAuthentication.valueIn(userAcct.getDomainId())))) {
|
if (userAcct.isUser2faEnabled() || (Boolean.TRUE.equals(AccountManagerImpl.enableUserTwoFactorAuthentication.valueIn(userAcct.getDomainId())) && Boolean.TRUE.equals(AccountManagerImpl.mandateUserTwoFactorAuthentication.valueIn(userAcct.getDomainId())))) {
|
||||||
is2faEnabled = true;
|
is2faEnabled = true;
|
||||||
|
|
|
||||||
|
|
@ -499,8 +499,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
|
||||||
public boolean isLocalStorageActiveOnHost(Long hostId) {
|
public boolean isLocalStorageActiveOnHost(Long hostId) {
|
||||||
List<StoragePoolHostVO> storagePoolHostRefs = _storagePoolHostDao.listByHostId(hostId);
|
List<StoragePoolHostVO> storagePoolHostRefs = _storagePoolHostDao.listByHostId(hostId);
|
||||||
for (StoragePoolHostVO storagePoolHostRef : storagePoolHostRefs) {
|
for (StoragePoolHostVO storagePoolHostRef : storagePoolHostRefs) {
|
||||||
StoragePoolVO PrimaryDataStoreVO = _storagePoolDao.findById(storagePoolHostRef.getPoolId());
|
StoragePoolVO primaryDataStoreVO = _storagePoolDao.findById(storagePoolHostRef.getPoolId());
|
||||||
if (PrimaryDataStoreVO.getPoolType() == StoragePoolType.LVM || PrimaryDataStoreVO.getPoolType() == StoragePoolType.EXT) {
|
if (primaryDataStoreVO != null && (primaryDataStoreVO.getPoolType() == StoragePoolType.LVM || primaryDataStoreVO.getPoolType() == StoragePoolType.EXT)) {
|
||||||
SearchBuilder<VolumeVO> volumeSB = volumeDao.createSearchBuilder();
|
SearchBuilder<VolumeVO> volumeSB = volumeDao.createSearchBuilder();
|
||||||
volumeSB.and("poolId", volumeSB.entity().getPoolId(), SearchCriteria.Op.EQ);
|
volumeSB.and("poolId", volumeSB.entity().getPoolId(), SearchCriteria.Op.EQ);
|
||||||
volumeSB.and("removed", volumeSB.entity().getRemoved(), SearchCriteria.Op.NULL);
|
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);
|
volumeSB.join("activeVmSB", activeVmSB, volumeSB.entity().getInstanceId(), activeVmSB.entity().getId(), JoinBuilder.JoinType.INNER);
|
||||||
|
|
||||||
SearchCriteria<VolumeVO> volumeSC = volumeSB.create();
|
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.setParameters("state", Volume.State.Expunging, Volume.State.Destroy);
|
||||||
volumeSC.setJoinParameters("activeVmSB", "state", State.Starting, State.Running, State.Stopping, State.Migrating);
|
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
|
// poolId is null only if volume is destroyed, which has been checked
|
||||||
// before.
|
// before.
|
||||||
assert poolId != null;
|
assert poolId != null;
|
||||||
StoragePoolVO PrimaryDataStoreVO = _storagePoolDao.findById(poolId);
|
StoragePoolVO primaryDataStoreVO = _storagePoolDao.findById(poolId);
|
||||||
assert PrimaryDataStoreVO != null;
|
assert primaryDataStoreVO != null;
|
||||||
return PrimaryDataStoreVO.getUuid();
|
return primaryDataStoreVO.getUuid();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
CapacityVO capacity = new CapacityVO(poolId, zoneId, podId, clusterId, 0, 0, Capacity.CAPACITY_TYPE_STORAGE);
|
||||||
for (StoragePoolVO pool : pools) {
|
for (StoragePoolVO primaryDataStoreVO : pools) {
|
||||||
StorageStats stats = ApiDBUtils.getStoragePoolStatistics(pool.getId());
|
StorageStats stats = ApiDBUtils.getStoragePoolStatistics(primaryDataStoreVO.getId());
|
||||||
if (stats == null) {
|
if (stats == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,9 @@ import com.cloud.dc.ClusterDetailsDao;
|
||||||
import com.cloud.dc.DataCenter;
|
import com.cloud.dc.DataCenter;
|
||||||
import com.cloud.dc.DataCenterVO;
|
import com.cloud.dc.DataCenterVO;
|
||||||
import com.cloud.dc.Pod;
|
import com.cloud.dc.Pod;
|
||||||
|
import com.cloud.dc.dao.ClusterDao;
|
||||||
import com.cloud.dc.dao.DataCenterDao;
|
import com.cloud.dc.dao.DataCenterDao;
|
||||||
|
import com.cloud.dc.dao.HostPodDao;
|
||||||
import com.cloud.domain.Domain;
|
import com.cloud.domain.Domain;
|
||||||
import com.cloud.domain.dao.DomainDao;
|
import com.cloud.domain.dao.DomainDao;
|
||||||
import com.cloud.event.ActionEvent;
|
import com.cloud.event.ActionEvent;
|
||||||
|
|
@ -153,6 +155,7 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType;
|
||||||
import com.cloud.hypervisor.HypervisorCapabilitiesVO;
|
import com.cloud.hypervisor.HypervisorCapabilitiesVO;
|
||||||
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
|
import com.cloud.hypervisor.dao.HypervisorCapabilitiesDao;
|
||||||
import com.cloud.offering.DiskOffering;
|
import com.cloud.offering.DiskOffering;
|
||||||
|
import com.cloud.org.Cluster;
|
||||||
import com.cloud.org.Grouping;
|
import com.cloud.org.Grouping;
|
||||||
import com.cloud.projects.Project;
|
import com.cloud.projects.Project;
|
||||||
import com.cloud.projects.ProjectManager;
|
import com.cloud.projects.ProjectManager;
|
||||||
|
|
@ -322,6 +325,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||||
@Inject
|
@Inject
|
||||||
private VmWorkJobDao _workJobDao;
|
private VmWorkJobDao _workJobDao;
|
||||||
@Inject
|
@Inject
|
||||||
|
ClusterDao clusterDao;
|
||||||
|
@Inject
|
||||||
private ClusterDetailsDao _clusterDetailsDao;
|
private ClusterDetailsDao _clusterDetailsDao;
|
||||||
@Inject
|
@Inject
|
||||||
private StorageManager storageMgr;
|
private StorageManager storageMgr;
|
||||||
|
|
@ -347,6 +352,8 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||||
protected StoragePoolDetailsDao storagePoolDetailsDao;
|
protected StoragePoolDetailsDao storagePoolDetailsDao;
|
||||||
@Inject
|
@Inject
|
||||||
private BackupDao backupDao;
|
private BackupDao backupDao;
|
||||||
|
@Inject
|
||||||
|
HostPodDao podDao;
|
||||||
|
|
||||||
|
|
||||||
protected Gson _gson;
|
protected Gson _gson;
|
||||||
|
|
@ -2436,17 +2443,10 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||||
return attachVolumeToVM(command.getVirtualMachineId(), command.getId(), command.getDeviceId(), false);
|
return attachVolumeToVM(command.getVirtualMachineId(), command.getId(), command.getDeviceId(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Volume orchestrateAttachVolumeToVM(Long vmId, Long volumeId, Long deviceId) {
|
protected VolumeVO getVmExistingVolumeForVolumeAttach(UserVmVO vm, VolumeInfo volumeToAttach) {
|
||||||
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 = null;
|
VolumeVO existingVolumeOfVm = null;
|
||||||
VMTemplateVO template = _templateDao.findById(vm.getTemplateId());
|
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()) {
|
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.");
|
throw new CloudRuntimeException("The VM " + vm.getHostName() + " has more than one ROOT volume and is in an invalid state.");
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2454,7 +2454,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||||
existingVolumeOfVm = rootVolumesOfVm.get(0);
|
existingVolumeOfVm = rootVolumesOfVm.get(0);
|
||||||
} else {
|
} else {
|
||||||
// locate data volume of the vm
|
// 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) {
|
for (VolumeVO diskVolume : diskVolumesOfVm) {
|
||||||
if (diskVolume.getState() != Volume.State.Allocated) {
|
if (diskVolume.getState() != Volume.State.Allocated) {
|
||||||
existingVolumeOfVm = diskVolume;
|
existingVolumeOfVm = diskVolume;
|
||||||
|
|
@ -2463,40 +2463,85 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (logger.isTraceEnabled()) {
|
if (existingVolumeOfVm == null) {
|
||||||
if (existingVolumeOfVm != null) {
|
logger.trace("No existing volume found for VM ({}/{}) to attach volume {}/{}",
|
||||||
logger.trace("attaching volume {} to a VM {} with an existing volume {} on primary storage {}",
|
vm.getName(), vm.getUuid(),
|
||||||
volumeToAttach, vm, existingVolumeOfVm, _storagePoolDao.findById(existingVolumeOfVm.getPoolId()));
|
volumeToAttach.getName(), volumeToAttach.getUuid());
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
logger.trace("attaching volume {}/{} to a VM ({}/{}) with an existing volume {}/{} on primary storage {}",
|
||||||
|
volumeToAttach.getName(), volumeToAttach.getUuid(),
|
||||||
|
vm.getName(), vm.getUuid(),
|
||||||
|
existingVolumeOfVm.getName(), existingVolumeOfVm.getUuid(),
|
||||||
|
existingVolumeOfVm.getPoolId());
|
||||||
|
return existingVolumeOfVm;
|
||||||
}
|
}
|
||||||
|
|
||||||
HypervisorType rootDiskHyperType = vm.getHypervisorType();
|
protected StoragePool getPoolForAllocatedOrUploadedVolumeForAttach(final VolumeInfo volumeToAttach, final UserVmVO vm) {
|
||||||
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
|
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;
|
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
|
//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)) {
|
if (existingVolumeOfVm != null && !existingVolumeOfVm.getState().equals(Volume.State.Allocated)) {
|
||||||
destPrimaryStorage = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
|
destPrimaryStorage = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
|
||||||
if (logger.isTraceEnabled() && destPrimaryStorage != null) {
|
if (logger.isTraceEnabled() && destPrimaryStorage != null) {
|
||||||
logger.trace("decided on target storage: {}", destPrimaryStorage);
|
logger.trace("decided on target storage: {}", destPrimaryStorage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (destPrimaryStorage == null) {
|
||||||
boolean volumeOnSecondary = volumeToAttach.getState() == Volume.State.Uploaded;
|
destPrimaryStorage = getPoolForAllocatedOrUploadedVolumeForAttach(volumeToAttach, vm);
|
||||||
|
}
|
||||||
if (destPrimaryStorage != null && (volumeToAttach.getState() == Volume.State.Allocated || volumeOnSecondary)) {
|
if (destPrimaryStorage != null && (volumeToAttach.getState() == Volume.State.Allocated || volumeOnSecondary)) {
|
||||||
try {
|
try {
|
||||||
if (volumeOnSecondary && destPrimaryStorage.getPoolType() == Storage.StoragePoolType.PowerFlex) {
|
if (volumeOnSecondary && destPrimaryStorage.getPoolType() == Storage.StoragePoolType.PowerFlex) {
|
||||||
throw new InvalidParameterValueException("Cannot attach uploaded volume, this operation is unsupported on storage pool type " + 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) {
|
} catch (NoTransitionException e) {
|
||||||
logger.debug("Failed to create volume on primary storage", e);
|
logger.debug("Failed to create volume on primary storage", e);
|
||||||
throw new CloudRuntimeException("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
|
// reload the volume from db
|
||||||
newVolumeOnPrimaryStorage = volFactory.getVolume(newVolumeOnPrimaryStorage.getId());
|
newVolumeOnPrimaryStorage = volFactory.getVolume(newVolumeOnPrimaryStorage.getId());
|
||||||
|
|
@ -2515,19 +2560,17 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||||
StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
|
StoragePoolVO vmRootVolumePool = _storagePoolDao.findById(existingVolumeOfVm.getPoolId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
HypervisorType volumeToAttachHyperType = _volsDao.getHypervisorType(volumeToAttach.getId());
|
||||||
newVolumeOnPrimaryStorage = _volumeMgr.moveVolume(newVolumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(),
|
newVolumeOnPrimaryStorage = _volumeMgr.moveVolume(newVolumeOnPrimaryStorage, vmRootVolumePool.getDataCenterId(), vmRootVolumePool.getPodId(), vmRootVolumePool.getClusterId(),
|
||||||
volumeToAttachHyperType);
|
volumeToAttachHyperType);
|
||||||
} catch (ConcurrentOperationException e) {
|
} catch (ConcurrentOperationException | StorageUnavailableException e) {
|
||||||
logger.debug("move volume failed", e);
|
|
||||||
throw new CloudRuntimeException("move volume failed", e);
|
|
||||||
} catch (StorageUnavailableException e) {
|
|
||||||
logger.debug("move volume failed", e);
|
logger.debug("move volume failed", e);
|
||||||
throw new CloudRuntimeException("move volume failed", e);
|
throw new CloudRuntimeException("move volume failed", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
VolumeVO newVol = _volsDao.findById(newVolumeOnPrimaryStorage.getId());
|
VolumeVO newVol = _volsDao.findById(newVolumeOnPrimaryStorage.getId());
|
||||||
// Getting the fresh vm object in case of volume migration to check the current state of VM
|
// 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);
|
vm = _userVmDao.findById(vmId);
|
||||||
if (vm == null) {
|
if (vm == null) {
|
||||||
throw new InvalidParameterValueException("VM not found.");
|
throw new InvalidParameterValueException("VM not found.");
|
||||||
|
|
@ -2712,9 +2755,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
||||||
if (!_volsDao.findByInstanceAndDeviceId(vm.getId(), 0).isEmpty()) {
|
if (!_volsDao.findByInstanceAndDeviceId(vm.getId(), 0).isEmpty()) {
|
||||||
throw new InvalidParameterValueException("Vm already has root volume attached to it");
|
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -125,9 +125,7 @@ public interface AccountManager extends AccountService, Configurable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables an account by accountName and domainId
|
* Disables an account by accountName and domainId
|
||||||
* @param disabled
|
* @return the disabled account
|
||||||
* account if success
|
|
||||||
* @return true if disable was successful, false otherwise
|
|
||||||
*/
|
*/
|
||||||
Account disableAccount(String accountName, Long domainId, Long accountId) throws ConcurrentOperationException, ResourceUnavailableException;
|
Account disableAccount(String accountName, Long domainId, Long accountId) throws ConcurrentOperationException, ResourceUnavailableException;
|
||||||
|
|
||||||
|
|
@ -187,7 +185,7 @@ public interface AccountManager extends AccountService, Configurable {
|
||||||
|
|
||||||
String MESSAGE_REMOVE_ACCOUNT_EVENT = "Message.RemoveAccount.Event";
|
String MESSAGE_REMOVE_ACCOUNT_EVENT = "Message.RemoveAccount.Event";
|
||||||
|
|
||||||
ConfigKey<Boolean> UseSecretKeyInResponse = new ConfigKey<Boolean>("Advanced", Boolean.class, "use.secret.key.in.response", "false",
|
ConfigKey<Boolean> UseSecretKeyInResponse = new ConfigKey<>("Advanced", Boolean.class, "use.secret.key.in.response", "false",
|
||||||
"This parameter allows the users to enable or disable of showing secret key as a part of response for various APIs. By default it is set to false.", true);
|
"This parameter allows the users to enable or disable of showing secret key as a part of response for various APIs. By default it is set to false.", true);
|
||||||
|
|
||||||
boolean moveUser(long id, Long domainId, Account newAccount);
|
boolean moveUser(long id, Long domainId, Account newAccount);
|
||||||
|
|
@ -203,4 +201,6 @@ public interface AccountManager extends AccountService, Configurable {
|
||||||
void validateUserPasswordAndUpdateIfNeeded(String newPassword, UserVO user, String currentPassword, boolean skipCurrentPassValidation);
|
void validateUserPasswordAndUpdateIfNeeded(String newPassword, UserVO user, String currentPassword, boolean skipCurrentPassValidation);
|
||||||
|
|
||||||
void checkApiAccess(Account caller, String command);
|
void checkApiAccess(Account caller, String command);
|
||||||
|
|
||||||
|
UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1377,28 +1377,23 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||||
* if there is any permission under the requested role that is not permitted for the caller, refuse
|
* if there is any permission under the requested role that is not permitted for the caller, refuse
|
||||||
*/
|
*/
|
||||||
private void checkRoleEscalation(Account caller, Account requested) {
|
private void checkRoleEscalation(Account caller, Account requested) {
|
||||||
if (logger.isDebugEnabled()) {
|
logger.debug("checking if user of account {} [{}] with role-id [{}] can create an account of type {} [{}] with role-id [{}]",
|
||||||
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.getAccountName(),
|
||||||
caller.getUuid(),
|
caller.getUuid(),
|
||||||
caller.getRoleId(),
|
caller.getRoleId(),
|
||||||
requested.getAccountName(),
|
requested.getAccountName(),
|
||||||
requested.getUuid(),
|
requested.getUuid(),
|
||||||
requested.getRoleId()));
|
requested.getRoleId());
|
||||||
}
|
|
||||||
List<APIChecker> apiCheckers = getEnabledApiCheckers();
|
List<APIChecker> apiCheckers = getEnabledApiCheckers();
|
||||||
for (String command : apiNameList) {
|
for (String command : apiNameList) {
|
||||||
try {
|
try {
|
||||||
checkApiAccess(apiCheckers, requested, command);
|
checkApiAccess(apiCheckers, requested, command);
|
||||||
} catch (PermissionDeniedException pde) {
|
} catch (PermissionDeniedException pde) {
|
||||||
if (logger.isTraceEnabled()) {
|
logger.trace("checking for permission to \"{}\" is irrelevant as it is not requested for {} [{}]",
|
||||||
logger.trace(String.format("checking for permission to \"%s\" is irrelevant as it is not requested for %s [%s]",
|
|
||||||
command,
|
command,
|
||||||
pde.getAccount().getAccountName(),
|
requested.getAccountName(),
|
||||||
pde.getAccount().getUuid(),
|
requested.getUuid()
|
||||||
pde.getEntitiesInViolation()
|
);
|
||||||
));
|
|
||||||
}
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// so requested can, now make sure caller can as well
|
// so requested can, now make sure caller can as well
|
||||||
|
|
@ -3566,4 +3561,26 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
||||||
return userTwoFactorAuthenticationProvidersMap.get(name.toLowerCase());
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -94,8 +94,12 @@ import com.cloud.api.query.dao.ServiceOfferingJoinDao;
|
||||||
import com.cloud.configuration.ConfigurationManager;
|
import com.cloud.configuration.ConfigurationManager;
|
||||||
import com.cloud.configuration.Resource;
|
import com.cloud.configuration.Resource;
|
||||||
import com.cloud.configuration.Resource.ResourceType;
|
import com.cloud.configuration.Resource.ResourceType;
|
||||||
|
import com.cloud.dc.ClusterVO;
|
||||||
import com.cloud.dc.DataCenterVO;
|
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.DataCenterDao;
|
||||||
|
import com.cloud.dc.dao.HostPodDao;
|
||||||
import com.cloud.event.EventTypes;
|
import com.cloud.event.EventTypes;
|
||||||
import com.cloud.event.UsageEventUtils;
|
import com.cloud.event.UsageEventUtils;
|
||||||
import com.cloud.exception.InvalidParameterValueException;
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
|
|
@ -130,10 +134,12 @@ import com.cloud.utils.Pair;
|
||||||
import com.cloud.utils.db.TransactionLegacy;
|
import com.cloud.utils.db.TransactionLegacy;
|
||||||
import com.cloud.utils.exception.CloudRuntimeException;
|
import com.cloud.utils.exception.CloudRuntimeException;
|
||||||
import com.cloud.utils.fsm.NoTransitionException;
|
import com.cloud.utils.fsm.NoTransitionException;
|
||||||
|
import com.cloud.vm.DiskProfile;
|
||||||
import com.cloud.vm.UserVmManager;
|
import com.cloud.vm.UserVmManager;
|
||||||
import com.cloud.vm.UserVmVO;
|
import com.cloud.vm.UserVmVO;
|
||||||
import com.cloud.vm.VirtualMachine;
|
import com.cloud.vm.VirtualMachine;
|
||||||
import com.cloud.vm.VirtualMachine.State;
|
import com.cloud.vm.VirtualMachine.State;
|
||||||
|
import com.cloud.vm.VirtualMachineManager;
|
||||||
import com.cloud.vm.dao.UserVmDao;
|
import com.cloud.vm.dao.UserVmDao;
|
||||||
import com.cloud.vm.dao.VMInstanceDao;
|
import com.cloud.vm.dao.VMInstanceDao;
|
||||||
import com.cloud.vm.snapshot.VMSnapshotVO;
|
import com.cloud.vm.snapshot.VMSnapshotVO;
|
||||||
|
|
@ -209,6 +215,15 @@ public class VolumeApiServiceImplTest {
|
||||||
private DataStoreManager dataStoreMgr;
|
private DataStoreManager dataStoreMgr;
|
||||||
@Mock
|
@Mock
|
||||||
private SnapshotHelper snapshotHelper;
|
private SnapshotHelper snapshotHelper;
|
||||||
|
@Mock
|
||||||
|
VirtualMachineManager virtualMachineManager;
|
||||||
|
@Mock
|
||||||
|
HostPodDao podDao;
|
||||||
|
@Mock
|
||||||
|
ClusterDao clusterDao;
|
||||||
|
@Mock
|
||||||
|
VolumeOrchestrationService volumeOrchestrationService;
|
||||||
|
|
||||||
|
|
||||||
private DetachVolumeCmd detachCmd = new DetachVolumeCmd();
|
private DetachVolumeCmd detachCmd = new DetachVolumeCmd();
|
||||||
private Class<?> _detachCmdClass = detachCmd.getClass();
|
private Class<?> _detachCmdClass = detachCmd.getClass();
|
||||||
|
|
@ -1925,4 +1940,237 @@ public class VolumeApiServiceImplTest {
|
||||||
verify(volumeServiceMock, times(0)).resize(any(VolumeInfo.class));
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// disabled @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disabled @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disabled @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disabled @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);
|
||||||
|
}
|
||||||
|
|
||||||
|
// disabled @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"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,10 +44,13 @@ import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
||||||
import org.apache.cloudstack.context.CallContext;
|
import org.apache.cloudstack.context.CallContext;
|
||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
import org.apache.cloudstack.webhook.WebhookHelper;
|
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||||
|
import org.apache.cloudstack.resourcedetail.UserDetailVO;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.mockito.InOrder;
|
import org.mockito.InOrder;
|
||||||
import org.mockito.Mock;
|
import org.mockito.Mock;
|
||||||
import org.mockito.MockedStatic;
|
import org.mockito.MockedStatic;
|
||||||
|
|
@ -1356,4 +1359,54 @@ public class AccountManagerImplTest extends AccountManagetImplTestBase {
|
||||||
Mockito.when(_projectAccountDao.listAdministratedProjectIds(accountId)).thenReturn(managedProjectIds);
|
Mockito.when(_projectAccountDao.listAdministratedProjectIds(accountId)).thenReturn(managedProjectIds);
|
||||||
accountManagerImpl.checkIfAccountManagesProjects(accountId);
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -496,4 +496,8 @@ public class MockAccountManagerImpl extends ManagerBase implements Manager, Acco
|
||||||
@Override
|
@Override
|
||||||
public void validateUserPasswordAndUpdateIfNeeded(String newPassword, UserVO user, String currentPassword, boolean skipCurrentPassValidation) {
|
public void validateUserPasswordAndUpdateIfNeeded(String newPassword, UserVO user, String currentPassword, boolean skipCurrentPassValidation) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -682,6 +682,9 @@ export default {
|
||||||
if (accountIndex > -1) {
|
if (accountIndex > -1) {
|
||||||
this.fields[accountIndex].loading = false
|
this.fields[accountIndex].loading = false
|
||||||
}
|
}
|
||||||
|
if (hypervisorIndex > -1) {
|
||||||
|
this.fields[hypervisorIndex].loading = false
|
||||||
|
}
|
||||||
if (imageStoreIndex > -1) {
|
if (imageStoreIndex > -1) {
|
||||||
this.fields[imageStoreIndex].loading = false
|
this.fields[imageStoreIndex].loading = false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -140,7 +140,14 @@ export default {
|
||||||
icon: 'edit-outlined',
|
icon: 'edit-outlined',
|
||||||
label: 'label.update.network',
|
label: 'label.update.network',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
|
<<<<<<< HEAD
|
||||||
disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
|
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))
|
||||||
|
},
|
||||||
|
>>>>>>> 4.19
|
||||||
popup: true,
|
popup: true,
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateNetwork.vue')))
|
component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateNetwork.vue')))
|
||||||
},
|
},
|
||||||
|
|
@ -150,7 +157,14 @@ export default {
|
||||||
label: 'label.restart.network',
|
label: 'label.restart.network',
|
||||||
message: 'message.restart.network',
|
message: 'message.restart.network',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
|
<<<<<<< HEAD
|
||||||
disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
|
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))
|
||||||
|
},
|
||||||
|
>>>>>>> 4.19
|
||||||
args: (record, store, isGroupAction) => {
|
args: (record, store, isGroupAction) => {
|
||||||
var fields = []
|
var fields = []
|
||||||
if (isGroupAction || record.vpcid == null) {
|
if (isGroupAction || record.vpcid == null) {
|
||||||
|
|
@ -189,7 +203,14 @@ export default {
|
||||||
label: 'label.action.delete.network',
|
label: 'label.action.delete.network',
|
||||||
message: 'message.action.delete.network',
|
message: 'message.action.delete.network',
|
||||||
dataView: true,
|
dataView: true,
|
||||||
|
<<<<<<< HEAD
|
||||||
disabled: (record, user) => { return (record.account !== user.userInfo.account && !['Admin', 'DomainAdmin'].includes(user.userInfo.roletype)) },
|
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))
|
||||||
|
},
|
||||||
|
>>>>>>> 4.19
|
||||||
groupAction: true,
|
groupAction: true,
|
||||||
popup: true,
|
popup: true,
|
||||||
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
groupMap: (selection) => { return selection.map(x => { return { id: x } }) }
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:tabs="$route.meta.tabs" />
|
:tabs="$route.meta.tabs" />
|
||||||
<tree-view
|
<tree-view
|
||||||
|
v-else
|
||||||
:key="treeViewKey"
|
:key="treeViewKey"
|
||||||
:treeData="treeData"
|
:treeData="treeData"
|
||||||
:treeSelected="treeSelected"
|
: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 () {
|
provide () {
|
||||||
return {
|
return {
|
||||||
parentCloseAction: this.closeAction,
|
parentCloseAction: this.closeAction,
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@
|
||||||
@keydown.esc="editableValueKey = null"
|
@keydown.esc="editableValueKey = null"
|
||||||
@pressEnter="updateConfigurationValue(configrecord)"
|
@pressEnter="updateConfigurationValue(configrecord)"
|
||||||
@change="value => setConfigurationEditable(configrecord, value)"
|
@change="value => setConfigurationEditable(configrecord, value)"
|
||||||
|
@keydown="e => handleInputNumberKeyDown(e, false)"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -52,6 +53,7 @@
|
||||||
@keydown.esc="editableValueKey = null"
|
@keydown.esc="editableValueKey = null"
|
||||||
@pressEnter="updateConfigurationValue(configrecord)"
|
@pressEnter="updateConfigurationValue(configrecord)"
|
||||||
@change="value => setConfigurationEditable(configrecord, value)"
|
@change="value => setConfigurationEditable(configrecord, value)"
|
||||||
|
@keydown="e => handleInputNumberKeyDown(e, true)"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</span>
|
</span>
|
||||||
|
|
@ -87,6 +89,7 @@
|
||||||
@keydown.esc="editableValueKey = null"
|
@keydown.esc="editableValueKey = null"
|
||||||
@pressEnter="updateConfigurationValue(configrecord)"
|
@pressEnter="updateConfigurationValue(configrecord)"
|
||||||
@change="value => setConfigurationEditable(configrecord, value)"
|
@change="value => setConfigurationEditable(configrecord, value)"
|
||||||
|
@keydown="e => handleInputNumberKeyDown(e, true)"
|
||||||
/>
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
|
@ -365,6 +368,26 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.editableValueKey = null
|
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…
Reference in New Issue