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")
|
@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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,11 +33,11 @@ import javax.persistence.Table;
|
|||||||
import javax.persistence.Transient;
|
import javax.persistence.Transient;
|
||||||
|
|
||||||
import org.apache.cloudstack.api.InternalIdentity;
|
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.Encrypt;
|
||||||
import com.cloud.utils.db.GenericDao;
|
import com.cloud.utils.db.GenericDao;
|
||||||
import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
|
||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = "user")
|
@Table(name = "user")
|
||||||
@ -131,12 +131,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 +373,10 @@ 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, "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
|
-- 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');
|
||||||
|
|||||||
@ -22,6 +22,7 @@
|
|||||||
-- Add column api_key_access to user and account tables
|
-- 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.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`.`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
|
-- Modify index for mshost_peer
|
||||||
DELETE FROM `cloud`.`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) {
|
protected List<StoragePool> reorderPoolsByCapacity(DeploymentPlan plan, List<StoragePool> pools) {
|
||||||
Long zoneId = plan.getDataCenterId();
|
Long zoneId = plan.getDataCenterId();
|
||||||
Long clusterId = plan.getClusterId();
|
Long clusterId = plan.getClusterId();
|
||||||
short capacityType;
|
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(pools)) {
|
if (CollectionUtils.isEmpty(pools)) {
|
||||||
return null;
|
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;
|
capacityType = Capacity.CAPACITY_TYPE_STORAGE_ALLOCATED;
|
||||||
} else {
|
storageType = "shared";
|
||||||
capacityType = Capacity.CAPACITY_TYPE_LOCAL_STORAGE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
List<Long> poolIdsByCapacity = capacityDao.orderHostsByFreeCapacity(zoneId, clusterId, capacityType);
|
||||||
|
|
||||||
logger.debug(String.format("List of pools in descending order of available capacity [%s].", poolIdsByCapacity));
|
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) {
|
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)) {
|
if (allocationAlgorithm.equals("random") || allocationAlgorithm.equals("userconcentratedpod_random") || (account == null)) {
|
||||||
reorderRandomPools(pools);
|
reorderRandomPools(pools);
|
||||||
} else if (StringUtils.equalsAny(allocationAlgorithm, "userdispersing", "firstfitleastconsumed")) {
|
} else if (StringUtils.equalsAny(allocationAlgorithm, "userdispersing", "firstfitleastconsumed")) {
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,7 +53,7 @@ echo "Downloading Kubernetes tools ${RELEASE}..."
|
|||||||
k8s_dir="${working_dir}/k8s"
|
k8s_dir="${working_dir}/k8s"
|
||||||
mkdir -p "${k8s_dir}"
|
mkdir -p "${k8s_dir}"
|
||||||
cd "${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`
|
kubeadm_file_permissions=`stat --format '%a' kubeadm`
|
||||||
chmod +x kubeadm
|
chmod +x kubeadm
|
||||||
|
|
||||||
|
|||||||
@ -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());
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1197,7 +1197,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;
|
||||||
@ -1242,6 +1242,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;
|
||||||
|
|||||||
@ -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.DeletePoolCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.storage.DeleteSecondaryStagingStoreCmd;
|
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.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.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.UpdateStoragePoolCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.storage.heuristics.CreateSecondaryStorageSelectorCmd;
|
import org.apache.cloudstack.api.command.admin.storage.heuristics.CreateSecondaryStorageSelectorCmd;
|
||||||
import org.apache.cloudstack.api.command.admin.storage.heuristics.RemoveSecondaryStorageSelectorCmd;
|
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.DateUtil;
|
||||||
import com.cloud.utils.NumbersUtil;
|
import com.cloud.utils.NumbersUtil;
|
||||||
import com.cloud.utils.Pair;
|
import com.cloud.utils.Pair;
|
||||||
import com.cloud.utils.UriUtils;
|
|
||||||
import com.cloud.utils.StringUtils;
|
import com.cloud.utils.StringUtils;
|
||||||
|
import com.cloud.utils.UriUtils;
|
||||||
import com.cloud.utils.component.ComponentContext;
|
import com.cloud.utils.component.ComponentContext;
|
||||||
import com.cloud.utils.component.ManagerBase;
|
import com.cloud.utils.component.ManagerBase;
|
||||||
import com.cloud.utils.concurrency.NamedThreadFactory;
|
import com.cloud.utils.concurrency.NamedThreadFactory;
|
||||||
@ -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,41 +2463,89 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (existingVolumeOfVm == null) {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
if (existingVolumeOfVm != null) {
|
logger.trace(String.format("No existing volume found for VM (%s/%s) to attach volume %s/%s",
|
||||||
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;
|
||||||
|
}
|
||||||
|
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();
|
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)) {
|
|
||||||
try {
|
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());
|
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());
|
||||||
boolean moveVolumeNeeded = needMoveVolume(existingVolumeOfVm, newVolumeOnPrimaryStorage);
|
boolean moveVolumeNeeded = needMoveVolume(existingVolumeOfVm, newVolumeOnPrimaryStorage);
|
||||||
@ -2515,19 +2563,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 +2758,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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -20,7 +20,6 @@ import java.net.InetAddress;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
|
||||||
import org.apache.cloudstack.acl.ControlledEntity;
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
import org.apache.cloudstack.api.command.admin.account.UpdateAccountCmd;
|
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.DeleteUserCmd;
|
||||||
@ -31,6 +30,7 @@ import org.apache.cloudstack.auth.UserTwoFactorAuthenticator;
|
|||||||
import org.apache.cloudstack.framework.config.ConfigKey;
|
import org.apache.cloudstack.framework.config.ConfigKey;
|
||||||
import org.apache.cloudstack.framework.config.Configurable;
|
import org.apache.cloudstack.framework.config.Configurable;
|
||||||
|
|
||||||
|
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||||
import com.cloud.api.query.vo.ControlledViewEntity;
|
import com.cloud.api.query.vo.ControlledViewEntity;
|
||||||
import com.cloud.exception.ConcurrentOperationException;
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
import com.cloud.exception.ResourceUnavailableException;
|
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 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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,6 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.naming.ConfigurationException;
|
import javax.naming.ConfigurationException;
|
||||||
|
|
||||||
import com.cloud.host.dao.HostDao;
|
|
||||||
import org.apache.cloudstack.acl.APIChecker;
|
import org.apache.cloudstack.acl.APIChecker;
|
||||||
import org.apache.cloudstack.acl.ControlledEntity;
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
import org.apache.cloudstack.acl.InfrastructureEntity;
|
import org.apache.cloudstack.acl.InfrastructureEntity;
|
||||||
@ -120,6 +119,7 @@ import com.cloud.exception.InvalidParameterValueException;
|
|||||||
import com.cloud.exception.OperationTimedoutException;
|
import com.cloud.exception.OperationTimedoutException;
|
||||||
import com.cloud.exception.PermissionDeniedException;
|
import com.cloud.exception.PermissionDeniedException;
|
||||||
import com.cloud.exception.ResourceUnavailableException;
|
import com.cloud.exception.ResourceUnavailableException;
|
||||||
|
import com.cloud.host.dao.HostDao;
|
||||||
import com.cloud.network.IpAddress;
|
import com.cloud.network.IpAddress;
|
||||||
import com.cloud.network.IpAddressManager;
|
import com.cloud.network.IpAddressManager;
|
||||||
import com.cloud.network.Network;
|
import com.cloud.network.Network;
|
||||||
@ -1378,7 +1378,7 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
*/
|
*/
|
||||||
private void checkRoleEscalation(Account caller, Account requested) {
|
private void checkRoleEscalation(Account caller, Account requested) {
|
||||||
if (logger.isDebugEnabled()) {
|
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.getAccountName(),
|
||||||
caller.getUuid(),
|
caller.getUuid(),
|
||||||
caller.getRoleId(),
|
caller.getRoleId(),
|
||||||
@ -1392,12 +1392,13 @@ public class AccountManagerImpl extends ManagerBase implements AccountManager, M
|
|||||||
checkApiAccess(apiCheckers, requested, command);
|
checkApiAccess(apiCheckers, requested, command);
|
||||||
} catch (PermissionDeniedException pde) {
|
} catch (PermissionDeniedException pde) {
|
||||||
if (logger.isTraceEnabled()) {
|
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,
|
command,
|
||||||
pde.getAccount().getAccountName(),
|
requested.getAccountName(),
|
||||||
pde.getAccount().getUuid(),
|
requested.getUuid()
|
||||||
pde.getEntitiesInViolation()
|
)
|
||||||
));
|
);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -3566,4 +3567,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;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,6 @@ import java.util.List;
|
|||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
|
|
||||||
import com.cloud.server.ManagementService;
|
|
||||||
import org.apache.cloudstack.acl.ControlledEntity;
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
|
||||||
import org.apache.cloudstack.api.command.user.volume.CheckAndRepairVolumeCmd;
|
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.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;
|
||||||
@ -108,6 +111,7 @@ 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;
|
||||||
import com.cloud.serializer.GsonHelper;
|
import com.cloud.serializer.GsonHelper;
|
||||||
|
import com.cloud.server.ManagementService;
|
||||||
import com.cloud.server.TaggedResourceService;
|
import com.cloud.server.TaggedResourceService;
|
||||||
import com.cloud.service.ServiceOfferingVO;
|
import com.cloud.service.ServiceOfferingVO;
|
||||||
import com.cloud.service.dao.ServiceOfferingDao;
|
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.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();
|
||||||
@ -250,9 +265,6 @@ public class VolumeApiServiceImplTest {
|
|||||||
@Mock
|
@Mock
|
||||||
private ConfigurationManager _configMgr;
|
private ConfigurationManager _configMgr;
|
||||||
|
|
||||||
@Mock
|
|
||||||
private VolumeOrchestrationService _volumeMgr;
|
|
||||||
|
|
||||||
@Mock
|
@Mock
|
||||||
private ManagementService managementService;
|
private ManagementService managementService;
|
||||||
|
|
||||||
@ -1925,4 +1937,236 @@ 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import com.cloud.event.ActionEventUtils;
|
|
||||||
|
|
||||||
import org.apache.cloudstack.acl.ControlledEntity;
|
import org.apache.cloudstack.acl.ControlledEntity;
|
||||||
import org.apache.cloudstack.acl.Role;
|
import org.apache.cloudstack.acl.Role;
|
||||||
import org.apache.cloudstack.acl.RoleService;
|
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.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.resourcedetail.UserDetailVO;
|
||||||
import org.apache.cloudstack.webhook.WebhookHelper;
|
import org.apache.cloudstack.webhook.WebhookHelper;
|
||||||
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;
|
||||||
import org.mockito.Mockito;
|
import org.mockito.Mockito;
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
import org.mockito.junit.MockitoJUnitRunner;
|
||||||
|
|
||||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||||
|
|
||||||
import com.cloud.acl.DomainChecker;
|
import com.cloud.acl.DomainChecker;
|
||||||
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
import com.cloud.api.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||||
import com.cloud.domain.Domain;
|
import com.cloud.domain.Domain;
|
||||||
import com.cloud.domain.DomainVO;
|
import com.cloud.domain.DomainVO;
|
||||||
|
import com.cloud.event.ActionEventUtils;
|
||||||
import com.cloud.exception.ConcurrentOperationException;
|
import com.cloud.exception.ConcurrentOperationException;
|
||||||
import com.cloud.exception.InvalidParameterValueException;
|
import com.cloud.exception.InvalidParameterValueException;
|
||||||
import com.cloud.exception.PermissionDeniedException;
|
import com.cloud.exception.PermissionDeniedException;
|
||||||
@ -1356,4 +1356,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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,27 @@
|
|||||||
// under the License.
|
// under the License.
|
||||||
package com.cloud.user;
|
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.auth.SetupUserTwoFactorAuthenticationCmd;
|
||||||
import com.cloud.api.query.vo.ControlledViewEntity;
|
import com.cloud.api.query.vo.ControlledViewEntity;
|
||||||
import com.cloud.dc.DataCenter;
|
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.component.ManagerBase;
|
||||||
import com.cloud.utils.db.SearchBuilder;
|
import com.cloud.utils.db.SearchBuilder;
|
||||||
import com.cloud.utils.db.SearchCriteria;
|
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
|
@Component
|
||||||
public class MockAccountManagerImpl extends ManagerBase implements Manager, AccountManager {
|
public class MockAccountManagerImpl extends ManagerBase implements Manager, AccountManager {
|
||||||
@ -496,4 +498,9 @@ 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) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserAccount clearUserTwoFactorAuthenticationInSetupStateOnLogin(UserAccount user) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,7 +33,7 @@
|
|||||||
<a-menu>
|
<a-menu>
|
||||||
<a-menu-item v-for="(column, idx) in columnKeys" :key="idx" @click="updateSelectedColumns(column)">
|
<a-menu-item v-for="(column, idx) in columnKeys" :key="idx" @click="updateSelectedColumns(column)">
|
||||||
<a-checkbox :id="idx.toString()" :checked="selectedColumns.includes(getColumnKey(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-item>
|
||||||
</a-menu>
|
</a-menu>
|
||||||
</div>
|
</div>
|
||||||
@ -1005,16 +1005,16 @@ export default {
|
|||||||
return host.state
|
return host.state
|
||||||
},
|
},
|
||||||
getColumnKey (name) {
|
getColumnKey (name) {
|
||||||
if (typeof name === 'object') {
|
if (typeof name !== 'object' || name === null) {
|
||||||
name = Object.keys(name).includes('field') ? name.field : name.customTitle
|
|
||||||
}
|
|
||||||
return name
|
return name
|
||||||
|
}
|
||||||
|
return name.field ?? name.customTitle ?? Object.keys(name)[0]
|
||||||
},
|
},
|
||||||
getColumTitle (name) {
|
getColumnTitle (name) {
|
||||||
if (typeof name === 'object') {
|
if (typeof name !== 'object' || name === null) {
|
||||||
name = Object.keys(name).includes('customTitle') ? name.customTitle : name.field
|
|
||||||
}
|
|
||||||
return name
|
return name
|
||||||
|
}
|
||||||
|
return name.customTitle ?? name.field ?? Object.keys(name)[0]
|
||||||
},
|
},
|
||||||
handleResizeColumn (w, col) {
|
handleResizeColumn (w, col) {
|
||||||
col.width = w
|
col.width = w
|
||||||
|
|||||||
@ -680,6 +680,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,10 @@ export default {
|
|||||||
icon: 'edit-outlined',
|
icon: 'edit-outlined',
|
||||||
label: 'label.update.network',
|
label: 'label.update.network',
|
||||||
dataView: true,
|
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,
|
popup: true,
|
||||||
component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateNetwork.vue')))
|
component: shallowRef(defineAsyncComponent(() => import('@/views/network/UpdateNetwork.vue')))
|
||||||
},
|
},
|
||||||
@ -150,7 +153,10 @@ export default {
|
|||||||
label: 'label.restart.network',
|
label: 'label.restart.network',
|
||||||
message: 'message.restart.network',
|
message: 'message.restart.network',
|
||||||
dataView: true,
|
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) => {
|
args: (record, store, isGroupAction) => {
|
||||||
var fields = []
|
var fields = []
|
||||||
if (isGroupAction || record.vpcid == null) {
|
if (isGroupAction || record.vpcid == null) {
|
||||||
@ -189,7 +195,10 @@ 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,
|
||||||
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,
|
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…
x
Reference in New Issue
Block a user