From a2d5aa997fc9335ea542f0e7fcdaa257e1ee0e1e Mon Sep 17 00:00:00 2001
From: Layon <144072822+LayonRibeiro@users.noreply.github.com>
Date: Thu, 28 Nov 2024 14:41:31 -0300
Subject: [PATCH 01/12] displaying info of the labels from form
CreateSSHKeyPair (#9970)
---
ui/src/views/compute/CreateSSHKeyPair.vue | 25 +++++++++--------------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/ui/src/views/compute/CreateSSHKeyPair.vue b/ui/src/views/compute/CreateSSHKeyPair.vue
index 0f879cb3846..3b1c5476856 100644
--- a/ui/src/views/compute/CreateSSHKeyPair.vue
+++ b/ui/src/views/compute/CreateSSHKeyPair.vue
@@ -27,11 +27,8 @@
@finish="handleSubmit"
layout="vertical">
-
- {{ $t('label.name') }}
-
-
-
+
+
-
- {{ $t('label.publickey') }}
-
-
-
+
+
-
- {{ $t('label.domainid') }}
-
-
-
+
+
Date: Thu, 28 Nov 2024 14:42:34 -0300
Subject: [PATCH 02/12] Remove info about the environment from exception when
revert VM snapshot (#9944)
Co-authored-by: Lucas Martins
---
.../storage/vmsnapshot/DefaultVMSnapshotStrategy.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java
index 1d3788a0301..09f569e6f19 100644
--- a/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java
+++ b/engine/storage/snapshot/src/main/java/org/apache/cloudstack/storage/vmsnapshot/DefaultVMSnapshotStrategy.java
@@ -419,7 +419,7 @@ public class DefaultVMSnapshotStrategy extends ManagerBase implements VMSnapshot
if (answer != null && answer.getDetails() != null)
errMsg = errMsg + " due to " + answer.getDetails();
logger.error(errMsg);
- throw new CloudRuntimeException(errMsg);
+ throw new CloudRuntimeException(String.format("Unable to revert VM %s to snapshot %s.", userVm.getInstanceName(), vmSnapshotVO.getName()));
}
} catch (OperationTimedoutException e) {
logger.debug("Failed to revert vm snapshot", e);
From 2c412f8947c60e7eb91e2e0ee9a98e60d182ee3c Mon Sep 17 00:00:00 2001
From: Fabricio Duarte
Date: Thu, 28 Nov 2024 14:43:26 -0300
Subject: [PATCH 03/12] Adjust misformatted logs (#9889)
* Adjust a Quota balance calculation log
* Fix another log
---
.../main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java | 2 +-
.../java/com/cloud/deploy/DeploymentPlanningManagerImpl.java | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java
index 226a47bb7df..c9254814f46 100644
--- a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java
+++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java
@@ -157,7 +157,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager {
.map(quotaUsageVO -> new Pair<>(quotaUsageVO.getStartDate(), quotaUsageVO.getEndDate()))
.collect(Collectors.toCollection(LinkedHashSet::new));
- logger.info(String.format("Processing quota balance for account[{}] between [{}] and [{}].", accountToString, startDate, lastQuotaUsageEndDate));
+ logger.info("Processing quota balance for account [{}] between [{}] and [{}].", accountToString, startDate, lastQuotaUsageEndDate);
long accountId = accountVo.getAccountId();
long domainId = accountVo.getDomainId();
diff --git a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
index 19760e6d025..cf202564a99 100644
--- a/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
+++ b/server/src/main/java/com/cloud/deploy/DeploymentPlanningManagerImpl.java
@@ -1795,7 +1795,7 @@ StateListener, Configurable {
useLocalStorage = diskOffering.isUseLocalStorage();
}
diskProfile.setUseLocalStorage(useLocalStorage);
- logger.debug(String.format("Calling StoragePoolAllocators to find suitable pools to allocate volume [{}] necessary to deploy VM [{}].", toBeCreated.getUuid(), vmProfile.getUuid()));
+ logger.debug("Calling StoragePoolAllocators to find suitable pools to allocate volume [{}] necessary to deploy VM [{}].", toBeCreated.getUuid(), vmProfile.getUuid());
boolean foundPotentialPools = tryToFindPotentialPoolsToAlocateVolume(vmProfile, plan, avoid, returnUpTo, suitableVolumeStoragePools, toBeCreated, diskProfile);
if (avoid.getPoolsToAvoid() != null) {
From 5886780240bbb9ee75e805cd750eb192a9126344 Mon Sep 17 00:00:00 2001
From: Lucas Martins <56271185+lucas-a-martins@users.noreply.github.com>
Date: Thu, 28 Nov 2024 14:49:05 -0300
Subject: [PATCH 04/12] Change vmsnapshot.max config to be dynamic (#9883)
Co-authored-by: Lucas Martins
---
.../java/com/cloud/vm/snapshot/VMSnapshotManager.java | 2 +-
.../src/main/java/com/cloud/configuration/Config.java | 1 -
.../com/cloud/vm/snapshot/VMSnapshotManagerImpl.java | 11 +++++------
.../com/cloud/vm/snapshot/VMSnapshotManagerTest.java | 3 ---
4 files changed, 6 insertions(+), 11 deletions(-)
diff --git a/engine/components-api/src/main/java/com/cloud/vm/snapshot/VMSnapshotManager.java b/engine/components-api/src/main/java/com/cloud/vm/snapshot/VMSnapshotManager.java
index 82456004cc3..a01d4ee5cae 100644
--- a/engine/components-api/src/main/java/com/cloud/vm/snapshot/VMSnapshotManager.java
+++ b/engine/components-api/src/main/java/com/cloud/vm/snapshot/VMSnapshotManager.java
@@ -31,7 +31,7 @@ public interface VMSnapshotManager extends VMSnapshotService, Manager {
static final ConfigKey VMSnapshotExpireInterval = new ConfigKey("Advanced", Integer.class, "vmsnapshot.expire.interval", "-1",
"VM Snapshot expire interval in hours", true, ConfigKey.Scope.Account);
- public static final int VMSNAPSHOTMAX = 10;
+ ConfigKey VMSnapshotMax = new ConfigKey("Advanced", Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a single vm", true, ConfigKey.Scope.Global);
/**
* Delete all VM snapshots belonging to one VM
diff --git a/server/src/main/java/com/cloud/configuration/Config.java b/server/src/main/java/com/cloud/configuration/Config.java
index ce3ac768468..b9de906ba46 100644
--- a/server/src/main/java/com/cloud/configuration/Config.java
+++ b/server/src/main/java/com/cloud/configuration/Config.java
@@ -1740,7 +1740,6 @@ DefaultMaxAccountProjects(
null),
// VMSnapshots
- VMSnapshotMax("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.max", "10", "Maximum vm snapshots for a vm", null),
VMSnapshotCreateWait("Advanced", VMSnapshotManager.class, Integer.class, "vmsnapshot.create.wait", "1800", "In second, timeout for create vm snapshot", null),
CloudDnsName("Advanced", ManagementServer.class, String.class, "cloud.dns.name", null, "DNS name of the cloud for the GSLB service", null),
diff --git a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java
index cd67c720b49..2061367cf4d 100644
--- a/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java
+++ b/server/src/main/java/com/cloud/vm/snapshot/VMSnapshotManagerImpl.java
@@ -174,7 +174,6 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this);
- int _vmSnapshotMax;
int _wait;
static final ConfigKey VmJobCheckInterval = new ConfigKey("Advanced",
@@ -188,8 +187,6 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
throw new ConfigurationException("Unable to get the configuration dao.");
}
- _vmSnapshotMax = NumbersUtil.parseInt(_configDao.getValue("vmsnapshot.max"), VMSNAPSHOTMAX);
-
String value = _configDao.getValue("vmsnapshot.create.wait");
_wait = NumbersUtil.parseInt(value, 1800);
@@ -398,8 +395,10 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
_accountMgr.checkAccess(caller, null, true, userVmVo);
// check max snapshot limit for per VM
- if (_vmSnapshotDao.findByVm(vmId).size() >= _vmSnapshotMax) {
- throw new CloudRuntimeException("Creating vm snapshot failed due to a VM can just have : " + _vmSnapshotMax + " VM snapshots. Please delete old ones");
+ int vmSnapshotMax = VMSnapshotManager.VMSnapshotMax.value();
+
+ if (_vmSnapshotDao.findByVm(vmId).size() >= vmSnapshotMax) {
+ throw new CloudRuntimeException("Creating vm snapshot failed due to a VM can just have : " + vmSnapshotMax + " VM snapshots. Please delete old ones");
}
// check if there are active volume snapshots tasks
@@ -1391,6 +1390,6 @@ public class VMSnapshotManagerImpl extends MutualExclusiveIdsManagerBase impleme
@Override
public ConfigKey>[] getConfigKeys() {
- return new ConfigKey>[] {VMSnapshotExpireInterval};
+ return new ConfigKey>[] {VMSnapshotExpireInterval, VMSnapshotMax};
}
}
diff --git a/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java b/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java
index 0ed17fcce76..440431086ee 100644
--- a/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java
+++ b/server/src/test/java/com/cloud/vm/snapshot/VMSnapshotManagerTest.java
@@ -136,7 +136,6 @@ public class VMSnapshotManagerTest {
VMSnapshotDetailsDao _vmSnapshotDetailsDao;
@Mock
UserVmManager _userVmManager;
- int _vmSnapshotMax = 10;
private static final long TEST_VM_ID = 3L;
private static final long SERVICE_OFFERING_ID = 1L;
@@ -194,8 +193,6 @@ public class VMSnapshotManagerTest {
doNothing().when(_accountMgr).checkAccess(any(Account.class), any(AccessType.class), any(Boolean.class), any(ControlledEntity.class));
- _vmSnapshotMgr._vmSnapshotMax = _vmSnapshotMax;
-
_vmSnapshotMgr._serviceOfferingDao = _serviceOfferingDao;
_vmSnapshotMgr._userVmDetailsDao = _userVmDetailsDao;
_vmSnapshotMgr._vmSnapshotDetailsDao = _vmSnapshotDetailsDao;
From 8f63660424a78f78f71c0ff5f38aad68aa3ab16f Mon Sep 17 00:00:00 2001
From: Wei Zhou
Date: Fri, 29 Nov 2024 09:08:45 +0100
Subject: [PATCH 05/12] pom.xml: revert systemvm template version to 4.20.0.0
(#10004)
---
pom.xml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index 3cde4831fc0..a2a81a82c3e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -50,7 +50,7 @@
UTF-8
UTF-8
https://download.cloudstack.org/systemvm
- 4.20.1.0-SNAPSHOT
+ 4.20.0.0
apache
https://sonarcloud.io
From 22a66044918ce37370ee4aa01ae1e20fbc41f581 Mon Sep 17 00:00:00 2001
From: Wei Zhou
Date: Sat, 30 Nov 2024 08:45:15 +0100
Subject: [PATCH 06/12] Routed mode: minor improvements and fixes (#9697)
* Routed: add global setting to enable/disable routed mode
* Routed: add unit tests
* Routed: add unit tests RoutedIpv4ManagerImplTest
* PR9697: fix end of files lint error
* PR9697: fix unit tests error with JDK17
* Routed: do not show ROUTED mode when create network/vpc offering if routed is disabled
* Routed: check if routed network is enabled when create ipv4 subnet/bgp peer/ASN range/network/vpc
* Routed: UI bug fix and improvements
* Routed: apply available BGP peers when VR is restarted
---
.../main/java/com/cloud/bgp/BGPService.java | 5 +
.../apache/cloudstack/api/ApiConstants.java | 1 +
.../cloudstack/api/response/ZoneResponse.java | 13 +
.../cloudstack/network/RoutedIpv4Manager.java | 9 +
.../api/query/dao/DataCenterJoinDaoImpl.java | 3 +
.../java/com/cloud/bgp/BGPServiceImpl.java | 59 +-
.../ConfigurationManagerImpl.java | 11 +
.../com/cloud/network/NetworkServiceImpl.java | 13 +-
.../VirtualNetworkApplianceManagerImpl.java | 13 +-
.../com/cloud/network/vpc/VpcManagerImpl.java | 21 +-
.../cloud/server/ManagementServerImpl.java | 11 -
.../network/RoutedIpv4ManagerImpl.java | 75 +-
.../com/cloud/bgp/BGPServiceImplTest.java | 133 +-
.../cloud/network/NetworkServiceImplTest.java | 92 +-
.../element/VirtualRouterElementTest.java | 56 +
.../router/CommandSetupHelperTest.java | 110 +-
...irtualNetworkApplianceManagerImplTest.java | 63 +
.../cloud/network/vpc/VpcManagerImplTest.java | 27 +
.../network/RoutedIpv4ManagerImplTest.java | 1217 +++++++++++++++++
ui/src/config/section/infra/zones.js | 9 +-
ui/src/config/section/network.js | 15 +-
.../network/CreateIpv4SubnetForNetwork.vue | 1 +
.../network/CreateIsolatedNetworkForm.vue | 5 +-
ui/src/views/network/CreateVpc.vue | 4 +
ui/src/views/offering/AddNetworkOffering.vue | 13 +
ui/src/views/offering/AddVpcOffering.vue | 13 +
26 files changed, 1902 insertions(+), 90 deletions(-)
create mode 100644 server/src/test/java/org/apache/cloudstack/network/RoutedIpv4ManagerImplTest.java
diff --git a/api/src/main/java/com/cloud/bgp/BGPService.java b/api/src/main/java/com/cloud/bgp/BGPService.java
index 935237092dd..61d149f2847 100644
--- a/api/src/main/java/com/cloud/bgp/BGPService.java
+++ b/api/src/main/java/com/cloud/bgp/BGPService.java
@@ -21,6 +21,7 @@ import com.cloud.network.Network;
import com.cloud.network.vpc.Vpc;
import com.cloud.utils.Pair;
import org.apache.cloudstack.api.command.user.bgp.ListASNumbersCmd;
+import org.apache.cloudstack.network.BgpPeer;
import java.util.List;
@@ -36,4 +37,8 @@ public interface BGPService {
boolean applyBgpPeers(Network network, boolean continueOnError) throws ResourceUnavailableException;
boolean applyBgpPeers(Vpc vpc, boolean continueOnError) throws ResourceUnavailableException;
+
+ List extends BgpPeer> getBgpPeersForNetwork(Network network);
+
+ List extends BgpPeer> getBgpPeersForVpc(Vpc vpc);
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index bb16b0ff90d..6df8d7f5f18 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -986,6 +986,7 @@ public class ApiConstants {
public static final String ACL_NAME = "aclname";
public static final String NUMBER = "number";
public static final String IS_DYNAMICALLY_SCALABLE = "isdynamicallyscalable";
+ public static final String ROUTED_MODE_ENABLED = "routedmodeenabled";
public static final String ROUTING = "isrouting";
public static final String ROUTING_MODE = "routingmode";
public static final String MAX_CONNECTIONS = "maxconnections";
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java
index 143dfad0eaf..4a5279753a1 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java
@@ -157,6 +157,11 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
@Param(description = "AS Number Range")
private String asnRange;
+ @SerializedName(ApiConstants.ROUTED_MODE_ENABLED)
+ @Param(description = "true, if routed network/vpc is enabled", since = "4.20.1")
+ private boolean routedModeEnabled = false;
+
+
public ZoneResponse() {
tags = new LinkedHashSet();
}
@@ -412,4 +417,12 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso
public String getAsnRange() {
return asnRange;
}
+
+ public boolean isRoutedModeEnabled() {
+ return routedModeEnabled;
+ }
+
+ public void setRoutedModeEnabled(boolean routedModeEnabled) {
+ this.routedModeEnabled = routedModeEnabled;
+ }
}
diff --git a/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java b/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java
index 2f704e9f47d..221a550ad63 100644
--- a/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java
+++ b/api/src/main/java/org/apache/cloudstack/network/RoutedIpv4Manager.java
@@ -57,6 +57,13 @@ import java.util.List;
public interface RoutedIpv4Manager extends PluggableService, Configurable {
+ ConfigKey RoutedNetworkVpcEnabled = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Boolean.class,
+ "routed.network.vpc.enabled",
+ "true",
+ "If true, the Routed network and VPC are enabled in the zone.",
+ true,
+ ConfigKey.Scope.Zone);
+
ConfigKey RoutedNetworkIPv4MaxCidrSize = new ConfigKey<>(ConfigKey.CATEGORY_NETWORK, Integer.class,
"routed.network.ipv4.max.cidr.size", "30", "The maximum value of the cidr size for isolated networks in ROUTED mode",
true, ConfigKey.Scope.Account);
@@ -196,4 +203,6 @@ public interface RoutedIpv4Manager extends PluggableService, Configurable {
void removeBgpPeersByAccountId(long accountId);
void removeBgpPeersByDomainId(long domainId);
+
+ Boolean isRoutedNetworkVpcEnabled(long zoneId);
}
diff --git a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java
index 2b3be728bd3..d457f8f7931 100644
--- a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java
+++ b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java
@@ -34,6 +34,7 @@ import org.apache.cloudstack.api.response.ResourceIconResponse;
import org.apache.cloudstack.api.response.ResourceTagResponse;
import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.stereotype.Component;
@@ -141,6 +142,8 @@ public class DataCenterJoinDaoImpl extends GenericDaoBase range.getStartASNumber() + "-" + range.getEndASNumber()).collect(Collectors.joining(", "));
zoneResponse.setAsnRange(asRange);
+ zoneResponse.setRoutedModeEnabled(RoutedIpv4Manager.RoutedNetworkVpcEnabled.valueIn(dataCenter.getId()));
+
zoneResponse.setResourceDetails(ApiDBUtils.getResourceDetails(dataCenter.getId(), ResourceObjectType.Zone));
zoneResponse.setHasAnnotation(annotationDao.hasAnnotations(dataCenter.getUuid(), AnnotationService.EntityType.ZONE.name(),
_accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId())));
diff --git a/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java b/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java
index 0e6ae7ade1a..672c6b7cd60 100644
--- a/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java
+++ b/server/src/main/java/com/cloud/bgp/BGPServiceImpl.java
@@ -24,6 +24,7 @@ import com.cloud.dc.dao.ASNumberRangeDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.domain.Domain;
import com.cloud.domain.dao.DomainDao;
+import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Network;
import com.cloud.network.NetworkModel;
@@ -54,6 +55,7 @@ import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import org.apache.cloudstack.api.command.user.bgp.ListASNumbersCmd;
import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.network.BgpPeer;
import org.apache.cloudstack.network.BgpPeerVO;
import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.apache.cloudstack.network.dao.BgpPeerDao;
@@ -116,6 +118,9 @@ public class BGPServiceImpl implements BGPService {
LOGGER.error(msg);
throw new InvalidParameterException(msg);
}
+ if (!routedIpv4Manager.isRoutedNetworkVpcEnabled(zoneId)) {
+ throw new InvalidParameterValueException("Cannot create ASN range as Routed networks and VPCs are not enabled for the zone.");
+ }
if (startASNumber > endASNumber) {
String msg = "Please specify a valid AS Number range";
LOGGER.error(msg);
@@ -391,19 +396,7 @@ public class BGPServiceImpl implements BGPService {
if (gatewayProviderStr != null) {
NetworkElement provider = networkModel.getElementImplementingProvider(gatewayProviderStr);
if (provider != null && provider instanceof BgpServiceProvider) {
- List bgpPeers;
- if (network.getVpcId() != null) {
- bgpPeers = bgpPeerDao.listNonRevokeByVpcId(network.getVpcId());
- } else {
- bgpPeers = bgpPeerDao.listNonRevokeByNetworkId(network.getId());
- }
- if (CollectionUtils.isEmpty(bgpPeers)) {
- Account owner = accountDao.findByIdIncludingRemoved(network.getAccountId());
- List bgpPeerIds = routedIpv4Manager.getBgpPeerIdsForAccount(owner, network.getDataCenterId());
- bgpPeers = bgpPeerIds.stream()
- .map(bgpPeerId -> bgpPeerDao.findById(bgpPeerId))
- .collect(Collectors.toList());
- }
+ List extends BgpPeer> bgpPeers = getBgpPeersForNetwork(network);
LOGGER.debug(String.format("Applying BPG Peers for network [%s]: [%s]", network, bgpPeers));
return ((BgpServiceProvider) provider).applyBgpPeers(null, network, bgpPeers);
}
@@ -420,14 +413,7 @@ public class BGPServiceImpl implements BGPService {
if (gatewayProviderStr != null) {
NetworkElement provider = networkModel.getElementImplementingProvider(gatewayProviderStr);
if (provider != null && provider instanceof BgpServiceProvider) {
- List bgpPeers = bgpPeerDao.listNonRevokeByVpcId(vpc.getId());
- if (CollectionUtils.isEmpty(bgpPeers)) {
- Account owner = accountDao.findByIdIncludingRemoved(vpc.getAccountId());
- List bgpPeerIds = routedIpv4Manager.getBgpPeerIdsForAccount(owner, vpc.getZoneId());
- bgpPeers = bgpPeerIds.stream()
- .map(bgpPeerId -> bgpPeerDao.findById(bgpPeerId))
- .collect(Collectors.toList());
- }
+ List extends BgpPeer> bgpPeers = getBgpPeersForVpc(vpc);
LOGGER.debug(String.format("Applying BPG Peers for VPC [%s]: [%s]", vpc, bgpPeers));
return ((BgpServiceProvider) provider).applyBgpPeers(vpc, null, bgpPeers);
@@ -435,4 +421,35 @@ public class BGPServiceImpl implements BGPService {
}
return true;
}
+
+ @Override
+ public List extends BgpPeer> getBgpPeersForNetwork(Network network) {
+ List bgpPeers;
+ if (network.getVpcId() != null) {
+ bgpPeers = bgpPeerDao.listNonRevokeByVpcId(network.getVpcId());
+ } else {
+ bgpPeers = bgpPeerDao.listNonRevokeByNetworkId(network.getId());
+ }
+ if (CollectionUtils.isEmpty(bgpPeers)) {
+ Account owner = accountDao.findByIdIncludingRemoved(network.getAccountId());
+ List bgpPeerIds = routedIpv4Manager.getBgpPeerIdsForAccount(owner, network.getDataCenterId());
+ bgpPeers = bgpPeerIds.stream()
+ .map(bgpPeerId -> bgpPeerDao.findById(bgpPeerId))
+ .collect(Collectors.toList());
+ }
+ return bgpPeers;
+ }
+
+ @Override
+ public List extends BgpPeer> getBgpPeersForVpc(Vpc vpc) {
+ List bgpPeers = bgpPeerDao.listNonRevokeByVpcId(vpc.getId());
+ if (CollectionUtils.isEmpty(bgpPeers)) {
+ Account owner = accountDao.findByIdIncludingRemoved(vpc.getAccountId());
+ List bgpPeerIds = routedIpv4Manager.getBgpPeerIdsForAccount(owner, vpc.getZoneId());
+ bgpPeers = bgpPeerIds.stream()
+ .map(bgpPeerId -> bgpPeerDao.findById(bgpPeerId))
+ .collect(Collectors.toList());
+ }
+ return bgpPeers;
+ }
}
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 25cbd10de0a..4a9e03ba67e 100644
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -106,6 +106,7 @@ import org.apache.cloudstack.framework.config.impl.ConfigurationVO;
import org.apache.cloudstack.framework.messagebus.MessageBus;
import org.apache.cloudstack.framework.messagebus.MessageSubscriber;
import org.apache.cloudstack.framework.messagebus.PublishScope;
+import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.apache.cloudstack.query.QueryService;
import org.apache.cloudstack.region.PortableIp;
import org.apache.cloudstack.region.PortableIpDao;
@@ -6673,6 +6674,16 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
throw new InvalidParameterValueException("networkMode should be set only for Isolated network offerings");
}
if (NetworkOffering.NetworkMode.ROUTED.equals(networkMode)) {
+ if (!RoutedIpv4Manager.RoutedNetworkVpcEnabled.value()) {
+ throw new InvalidParameterValueException(String.format("Configuration %s needs to be enabled for Routed networks", RoutedIpv4Manager.RoutedNetworkVpcEnabled.key()));
+ }
+ if (zoneIds != null) {
+ for (Long zoneId: zoneIds) {
+ if (!RoutedIpv4Manager.RoutedNetworkVpcEnabled.valueIn(zoneId)) {
+ throw new InvalidParameterValueException(String.format("Configuration %s needs to be enabled for Routed networks in zone (ID: %s)", RoutedIpv4Manager.RoutedNetworkVpcEnabled.key(), zoneId));
+ }
+ }
+ }
boolean useVirtualRouterOnly = true;
for (Service service : serviceProviderMap.keySet()) {
Set providers = serviceProviderMap.get(service);
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index 2ccd5fdd820..ffb0be83494 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -1384,7 +1384,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
}
- void validateNetworkCidrSize(Account caller, Integer cidrSize, String cidr, NetworkOffering networkOffering, long accountId) {
+ void validateNetworkCidrSize(Account caller, Integer cidrSize, String cidr, NetworkOffering networkOffering, long accountId, long zoneId) {
if (!GuestType.Isolated.equals(networkOffering.getGuestType())) {
if (cidrSize != null) {
throw new InvalidParameterValueException("network cidr size is only applicable on Isolated networks");
@@ -1405,11 +1405,11 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
if (cidrSize == null) {
throw new InvalidParameterValueException("network cidr or cidr size is required for Isolated networks with ROUTED mode");
}
- Integer maxCidrSize = routedIpv4Manager.RoutedNetworkIPv4MaxCidrSize.valueIn(accountId);
+ Integer maxCidrSize = RoutedIpv4Manager.RoutedNetworkIPv4MaxCidrSize.valueIn(accountId);
if (cidrSize > maxCidrSize) {
throw new InvalidParameterValueException("network cidr size cannot be bigger than maximum cidr size " + maxCidrSize);
}
- Integer minCidrSize = routedIpv4Manager.RoutedNetworkIPv4MinCidrSize.valueIn(accountId);
+ Integer minCidrSize = RoutedIpv4Manager.RoutedNetworkIPv4MinCidrSize.valueIn(accountId);
if (cidrSize < minCidrSize) {
throw new InvalidParameterValueException("network cidr size cannot be smaller than minimum cidr size " + minCidrSize);
}
@@ -1651,11 +1651,16 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
}
}
+ if (NetworkOffering.NetworkMode.ROUTED.equals(ntwkOff.getNetworkMode())
+ && !routedIpv4Manager.isRoutedNetworkVpcEnabled(zone.getId())) {
+ throw new InvalidParameterValueException("Routed network is not enabled in this zone");
+ }
+
if (isNonVpcNetworkSupportingDynamicRouting(ntwkOff) && ntwkOff.isSpecifyAsNumber() && asNumber == null) {
throw new InvalidParameterValueException("AS number is required for the network but not passed.");
}
- validateNetworkCidrSize(caller, networkCidrSize, cidr, ntwkOff, owner.getAccountId());
+ validateNetworkCidrSize(caller, networkCidrSize, cidr, ntwkOff, owner.getAccountId(), zone.getId());
validateSharedNetworkRouterIPs(gateway, startIP, endIP, netmask, routerIPv4, routerIPv6, startIPv6, endIPv6, ip6Cidr, ntwkOff);
diff --git a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
index 8f07fcfca3b..8a28eeabe21 100644
--- a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
@@ -65,10 +65,8 @@ import org.apache.cloudstack.framework.jobs.impl.AsyncJobVO;
import org.apache.cloudstack.lb.ApplicationLoadBalancerRuleVO;
import org.apache.cloudstack.lb.dao.ApplicationLoadBalancerRuleDao;
import org.apache.cloudstack.managed.context.ManagedContextRunnable;
-import org.apache.cloudstack.network.BgpPeerVO;
+import org.apache.cloudstack.network.BgpPeer;
import org.apache.cloudstack.network.RoutedIpv4Manager;
-import org.apache.cloudstack.network.dao.BgpPeerDao;
-import org.apache.cloudstack.network.dao.BgpPeerNetworkMapDao;
import org.apache.cloudstack.network.topology.NetworkTopology;
import org.apache.cloudstack.network.topology.NetworkTopologyContext;
import org.apache.cloudstack.utils.CloudStackVersion;
@@ -114,6 +112,7 @@ import com.cloud.api.query.dao.DomainRouterJoinDao;
import com.cloud.api.query.dao.UserVmJoinDao;
import com.cloud.api.query.vo.DomainRouterJoinVO;
import com.cloud.api.query.vo.UserVmJoinVO;
+import com.cloud.bgp.BGPService;
import com.cloud.cluster.ManagementServerHostVO;
import com.cloud.cluster.dao.ManagementServerHostDao;
import com.cloud.configuration.Config;
@@ -348,9 +347,7 @@ Configurable, StateListener bgpPeers = bgpPeerDao.listNonRevokeByVpcId(guestNetwork.getVpcId());
+ List extends BgpPeer> bgpPeers = bgpService.getBgpPeersForVpc(vpc);
_commandSetupHelper.createBgpPeersCommands(bgpPeers, router, cmds, guestNetwork);
}
} else {
if (routedIpv4Manager.isDynamicRoutedNetwork(guestNetwork)) {
- final List bgpPeers = bgpPeerDao.listNonRevokeByNetworkId(guestNetworkId);
+ List extends BgpPeer> bgpPeers = bgpService.getBgpPeersForNetwork(guestNetwork);
_commandSetupHelper.createBgpPeersCommands(bgpPeers, router, cmds, guestNetwork);
}
}
diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
index b956ccc16ed..6d24c0fe700 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -500,6 +500,18 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
}
networkMode = NetworkOffering.NetworkMode.valueOf(networkModeStr);
}
+ if (NetworkOffering.NetworkMode.ROUTED.equals(networkMode)) {
+ if (!RoutedIpv4Manager.RoutedNetworkVpcEnabled.value()) {
+ throw new InvalidParameterValueException(String.format("Configuration %s needs to be enabled for Routed VPCs", RoutedIpv4Manager.RoutedNetworkVpcEnabled.key()));
+ }
+ if (zoneIds != null) {
+ for (Long zoneId: zoneIds) {
+ if (!RoutedIpv4Manager.RoutedNetworkVpcEnabled.valueIn(zoneId)) {
+ throw new InvalidParameterValueException(String.format("Configuration %s needs to be enabled for Routed VPCs in zone (ID: %s)", RoutedIpv4Manager.RoutedNetworkVpcEnabled.key(), zoneId));
+ }
+ }
+ }
+ }
boolean specifyAsNumber = cmd.getSpecifyAsNumber();
String routingModeString = cmd.getRoutingMode();
@@ -1161,12 +1173,17 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
throw ex;
}
+ if (NetworkOffering.NetworkMode.ROUTED.equals(vpcOff.getNetworkMode())
+ && !routedIpv4Manager.RoutedNetworkVpcEnabled.valueIn(zoneId)) {
+ throw new InvalidParameterValueException("Routed VPC is not enabled in this zone");
+ }
+
if (NetworkOffering.RoutingMode.Dynamic.equals(vpcOff.getRoutingMode()) && vpcOff.isSpecifyAsNumber() && asNumber == null) {
throw new InvalidParameterValueException("AS number is required for the VPC but not passed.");
}
// Validate VPC cidr/cidrsize
- validateVpcCidrSize(caller, owner.getAccountId(), vpcOff, cidr, cidrSize);
+ validateVpcCidrSize(caller, owner.getAccountId(), vpcOff, cidr, cidrSize, zoneId);
// Validate BGP peers
if (CollectionUtils.isNotEmpty(bgpPeerIds)) {
@@ -1251,7 +1268,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
return newVpc;
}
- private void validateVpcCidrSize(Account caller, long accountId, VpcOffering vpcOffering, String cidr, Integer cidrSize) {
+ private void validateVpcCidrSize(Account caller, long accountId, VpcOffering vpcOffering, String cidr, Integer cidrSize, long zoneId) {
if (ObjectUtils.allNull(cidr, cidrSize)) {
throw new InvalidParameterValueException("VPC cidr or cidr size must be specified");
}
diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
index 87b3dd3cbd9..2062ee1e94d 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java
@@ -68,11 +68,6 @@ import org.apache.cloudstack.api.command.admin.affinitygroup.UpdateVMAffinityGro
import org.apache.cloudstack.api.command.admin.alert.GenerateAlertCmd;
import org.apache.cloudstack.api.command.admin.autoscale.CreateCounterCmd;
import org.apache.cloudstack.api.command.admin.autoscale.DeleteCounterCmd;
-import org.apache.cloudstack.api.command.admin.bgp.CreateASNRangeCmd;
-import org.apache.cloudstack.api.command.admin.bgp.DeleteASNRangeCmd;
-import org.apache.cloudstack.api.command.admin.bgp.ListASNRangesCmd;
-import org.apache.cloudstack.api.command.user.bgp.ListASNumbersCmd;
-import org.apache.cloudstack.api.command.admin.bgp.ReleaseASNumberCmd;
import org.apache.cloudstack.api.command.admin.cluster.AddClusterCmd;
import org.apache.cloudstack.api.command.admin.cluster.DeleteClusterCmd;
import org.apache.cloudstack.api.command.admin.cluster.ListClustersCmd;
@@ -4017,12 +4012,6 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
cmdList.add(RemoveSecondaryStorageSelectorCmd.class);
cmdList.add(ListAffectedVmsForStorageScopeChangeCmd.class);
- cmdList.add(CreateASNRangeCmd.class);
- cmdList.add(ListASNRangesCmd.class);
- cmdList.add(DeleteASNRangeCmd.class);
- cmdList.add(ListASNumbersCmd.class);
- cmdList.add(ReleaseASNumberCmd.class);
-
// Out-of-band management APIs for admins
cmdList.add(EnableOutOfBandManagementForHostCmd.class);
cmdList.add(DisableOutOfBandManagementForHostCmd.class);
diff --git a/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java b/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java
index bbad93737f1..ab05895b8d2 100644
--- a/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java
+++ b/server/src/main/java/org/apache/cloudstack/network/RoutedIpv4ManagerImpl.java
@@ -57,6 +57,10 @@ import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
+import org.apache.cloudstack.api.command.admin.bgp.CreateASNRangeCmd;
+import org.apache.cloudstack.api.command.admin.bgp.DeleteASNRangeCmd;
+import org.apache.cloudstack.api.command.admin.bgp.ListASNRangesCmd;
+import org.apache.cloudstack.api.command.admin.bgp.ReleaseASNumberCmd;
import org.apache.cloudstack.api.command.admin.network.CreateIpv4SubnetForZoneCmd;
import org.apache.cloudstack.api.command.admin.network.CreateIpv4SubnetForGuestNetworkCmd;
import org.apache.cloudstack.api.command.admin.network.DedicateIpv4SubnetForZoneCmd;
@@ -74,6 +78,7 @@ import org.apache.cloudstack.api.command.admin.network.bgp.DeleteBgpPeerCmd;
import org.apache.cloudstack.api.command.admin.network.bgp.ListBgpPeersCmd;
import org.apache.cloudstack.api.command.admin.network.bgp.ReleaseDedicatedBgpPeerCmd;
import org.apache.cloudstack.api.command.admin.network.bgp.UpdateBgpPeerCmd;
+import org.apache.cloudstack.api.command.user.bgp.ListASNumbersCmd;
import org.apache.cloudstack.api.command.user.network.routing.CreateRoutingFirewallRuleCmd;
import org.apache.cloudstack.api.command.user.network.routing.DeleteRoutingFirewallRuleCmd;
import org.apache.cloudstack.api.command.user.network.routing.ListRoutingFirewallRulesCmd;
@@ -154,6 +159,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
@Override
public ConfigKey>[] getConfigKeys() {
return new ConfigKey[] {
+ RoutedNetworkVpcEnabled,
RoutedNetworkIPv4MaxCidrSize, RoutedNetworkIPv4MinCidrSize, RoutedIPv4NetworkCidrAutoAllocationEnabled,
RoutedVpcIPv4MaxCidrSize, RoutedVpcIPv4MinCidrSize, UseSystemBgpPeers
};
@@ -162,19 +168,25 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
@Override
public List> getCommands() {
final List> cmdList = new ArrayList>();
+ if (!RoutedNetworkVpcEnabled.value()) {
+ return cmdList;
+ }
cmdList.add(CreateIpv4SubnetForZoneCmd.class);
cmdList.add(DeleteIpv4SubnetForZoneCmd.class);
cmdList.add(ListIpv4SubnetsForZoneCmd.class);
cmdList.add(UpdateIpv4SubnetForZoneCmd.class);
cmdList.add(DedicateIpv4SubnetForZoneCmd.class);
cmdList.add(ReleaseDedicatedIpv4SubnetForZoneCmd.class);
+
cmdList.add(CreateIpv4SubnetForGuestNetworkCmd.class);
cmdList.add(ListIpv4SubnetsForGuestNetworkCmd.class);
cmdList.add(DeleteIpv4SubnetForGuestNetworkCmd.class);
+
cmdList.add(CreateRoutingFirewallRuleCmd.class);
cmdList.add(ListRoutingFirewallRulesCmd.class);
cmdList.add(UpdateRoutingFirewallRuleCmd.class);
cmdList.add(DeleteRoutingFirewallRuleCmd.class);
+
cmdList.add(CreateBgpPeerCmd.class);
cmdList.add(DeleteBgpPeerCmd.class);
cmdList.add(ListBgpPeersCmd.class);
@@ -183,6 +195,13 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
cmdList.add(ReleaseDedicatedBgpPeerCmd.class);
cmdList.add(ChangeBgpPeersForNetworkCmd.class);
cmdList.add(ChangeBgpPeersForVpcCmd.class);
+
+ cmdList.add(CreateASNRangeCmd.class);
+ cmdList.add(ListASNRangesCmd.class);
+ cmdList.add(DeleteASNRangeCmd.class);
+ cmdList.add(ListASNumbersCmd.class);
+ cmdList.add(ReleaseASNumberCmd.class);
+
return cmdList;
}
@@ -198,6 +217,8 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
throw new InvalidParameterValueException("Invalid IPv4 subnet: " + subnet);
}
+ checkIfRoutedNetworkVpcEnabled(zoneId);
+
// check conflicts
List existingSubnets = dataCenterIpv4GuestSubnetDao.listByDataCenterId(zoneId);
checkConflicts(existingSubnets, subnet, null);
@@ -319,7 +340,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
return dataCenterIpv4GuestSubnetDao.findById(subnetId);
}
- private void checkConflicts(List existingSubnets, String newSubnet, Long ignoreSubnetId) {
+ protected void checkConflicts(List existingSubnets, String newSubnet, Long ignoreSubnetId) {
for (DataCenterIpv4GuestSubnetVO existing : existingSubnets) {
if ((ignoreSubnetId == null || existing.getId() != ignoreSubnetId) && NetUtils.isNetworksOverlap(existing.getSubnet(), newSubnet)) {
throw new InvalidParameterValueException(String.format("Existing zone subnet %s has overlap with: %s", existing.getSubnet(), newSubnet));
@@ -553,11 +574,6 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
response.setParentSubnet(parent.getSubnet());
zoneId = parent.getDataCenterId();
}
- } else if (subnet.getNetworkId() != null) {
- Network network = ApiDBUtils.findNetworkById(subnet.getNetworkId());
- if (network != null) {
- zoneId = network.getDataCenterId();
- }
}
if (zoneId != null) {
DataCenter zone = ApiDBUtils.findZoneById(zoneId);
@@ -590,7 +606,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
return getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(vpcCidrSize, vpc.getDomainId(), vpc.getAccountId(), vpc.getZoneId());
}
- private Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, Long ownerDomainId, Long ownerAccountId, Long zoneId) {
+ protected Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, Long ownerDomainId, Long ownerAccountId, Long zoneId) {
validateNetworkCidrSize(ownerAccountId, cidrSize);
List subnets = getZoneSubnetsForAccount(ownerDomainId, ownerAccountId, zoneId);
for (DataCenterIpv4GuestSubnetVO subnet : subnets) {
@@ -602,7 +618,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
return null;
}
- private Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, DataCenterIpv4GuestSubnetVO subnet) {
+ protected Ipv4GuestSubnetNetworkMap getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(Integer cidrSize, DataCenterIpv4GuestSubnetVO subnet) {
Ipv4GuestSubnetNetworkMap map = ipv4GuestSubnetNetworkMapDao.findFirstAvailable(subnet.getId(), cidrSize);
if (map != null) {
return map;
@@ -615,7 +631,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
return null;
}
- private void getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(String networkCidr, Long ownerDomainId, Long ownerAccountId, Long zoneId) {
+ protected void getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(String networkCidr, Long ownerDomainId, Long ownerAccountId, Long zoneId) {
Ipv4GuestSubnetNetworkMapVO subnetMap = ipv4GuestSubnetNetworkMapDao.findBySubnet(networkCidr);
if (subnetMap != null) {
// check if the subnet is in use
@@ -650,7 +666,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
}
}
- private DataCenterIpv4GuestSubnet getParentOfNetworkCidr(Long zoneId, String networkCidr) {
+ protected DataCenterIpv4GuestSubnet getParentOfNetworkCidr(Long zoneId, String networkCidr) {
List existingSubnets = dataCenterIpv4GuestSubnetDao.listByDataCenterId(zoneId);
for (DataCenterIpv4GuestSubnetVO existing : existingSubnets) {
if (NetUtils.isNetworkAWithinNetworkB(networkCidr, existing.getSubnet())) {
@@ -689,14 +705,22 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
private List getZoneSubnetsForAccount(long domainId, long accountId, long zoneId) {
// Get dedicated guest subnets for the account
- List subnets = dataCenterIpv4GuestSubnetDao.listByDataCenterIdAndAccountId(zoneId, accountId);
+ List subnets = new ArrayList<>();
+ subnets.addAll(dataCenterIpv4GuestSubnetDao.listByDataCenterIdAndAccountId(zoneId, accountId));
subnets.addAll(dataCenterIpv4GuestSubnetDao.listByDataCenterIdAndDomainId(zoneId, domainId));
// Get non-dedicated zone guest subnets for the account
subnets.addAll(dataCenterIpv4GuestSubnetDao.listNonDedicatedByDataCenterId(zoneId));
return subnets;
}
- private Ipv4GuestSubnetNetworkMap createIpv4SubnetFromParentSubnet(DataCenterIpv4GuestSubnet parent, Integer networkCidrSize) {
+ protected Ipv4GuestSubnetNetworkMap createIpv4SubnetFromParentSubnet(DataCenterIpv4GuestSubnet parent, Integer networkCidrSize) {
+ String networkCidr = createIpv4SubnetStringFromParentSubnet(parent, networkCidrSize);
+ // create DB record
+ Ipv4GuestSubnetNetworkMapVO subnetMap = new Ipv4GuestSubnetNetworkMapVO(parent.getId(), NetUtils.transformCidr(networkCidr), null, State.Free);
+ return ipv4GuestSubnetNetworkMapDao.persist(subnetMap);
+ }
+
+ protected String createIpv4SubnetStringFromParentSubnet(DataCenterIpv4GuestSubnet parent, Integer networkCidrSize) {
DataCenterIpv4GuestSubnetVO subnetVO = dataCenterIpv4GuestSubnetDao.findById(parent.getId());
if (subnetVO == null) {
throw new InvalidParameterValueException(String.format("Invalid subnet ID: %s", parent.getId()));
@@ -733,9 +757,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
if (networkCidr == null) {
throw new CloudRuntimeException("Failed to automatically allocate a subnet with specified cidrsize");
}
- // create DB record
- Ipv4GuestSubnetNetworkMapVO subnetMap = new Ipv4GuestSubnetNetworkMapVO(parent.getId(), NetUtils.transformCidr(networkCidr), null, State.Free);
- return ipv4GuestSubnetNetworkMapDao.persist(subnetMap);
+ return networkCidr;
}
private String getFreeNetworkCidr(List> subnetsInFreeIpRanges, int networkCidrSize) {
@@ -752,7 +774,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
return null;
}
- private Ipv4GuestSubnetNetworkMap createIpv4SubnetFromParentSubnet(DataCenterIpv4GuestSubnet parent, String networkCidr) {
+ protected Ipv4GuestSubnetNetworkMap createIpv4SubnetFromParentSubnet(DataCenterIpv4GuestSubnet parent, String networkCidr) {
// Validate the network cidr
if (!NetUtils.isNetworkAWithinNetworkB(networkCidr, parent.getSubnet())) {
throw new InvalidParameterValueException(String.format("networkCidr %s is not within parent cidr: %s", networkCidr, parent.getSubnet()));
@@ -941,9 +963,11 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
}
if (!FirewallRule.Purpose.Firewall.equals(rule.getPurpose())) {
logger.error(String.format("Cannot apply routing firewall rule with ID: %d as purpose %s is not %s", id, rule.getPurpose(), FirewallRule.Purpose.Firewall));
+ return false;
}
logger.debug(String.format("Applying routing firewall rules for rule with ID: %s", rule.getUuid()));
- List rules = firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), rule.getPurpose(), FirewallRule.TrafficType.Egress);
+ List rules = new ArrayList<>();
+ rules.addAll(firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), rule.getPurpose(), FirewallRule.TrafficType.Egress));
rules.addAll(firewallDao.listByNetworkPurposeTrafficType(rule.getNetworkId(), rule.getPurpose(), FirewallRule.TrafficType.Ingress));
return firewallManager.applyFirewallRules(rules, false, CallContext.current().getCallingAccount());
}
@@ -1014,6 +1038,8 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
String password = createBgpPeerCmd.getPassword();
Map detailsStr = createBgpPeerCmd.getDetails();
+ checkIfRoutedNetworkVpcEnabled(zoneId);
+
if (ObjectUtils.allNull(ip4Address, ip6Address)) {
throw new InvalidParameterValueException("At least one of IPv4 and IPv6 address must be specified.");
}
@@ -1398,7 +1424,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
return changeBgpPeersForNetworkInternal(network, null);
}
- private Network changeBgpPeersForNetworkInternal(Network network, List bgpPeerIds) {
+ protected Network changeBgpPeersForNetworkInternal(Network network, List bgpPeerIds) {
final List bgpPeerIdsToBeAdded;
if (CollectionUtils.isNotEmpty(bgpPeerIds)) {
bgpPeerIdsToBeAdded = new ArrayList<>(bgpPeerIds);
@@ -1529,7 +1555,7 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
return bgpPeerDao.listAvailableBgpPeerIdsForAccount(zoneId, owner.getDomainId(), owner.getId(), UseSystemBgpPeers.valueIn(owner.getId()));
}
- private Vpc changeBgpPeersForVpcInternal(Vpc vpc, List bgpPeerIds) {
+ protected Vpc changeBgpPeersForVpcInternal(Vpc vpc, List bgpPeerIds) {
final List bgpPeerIdsToBeAdded;
if (CollectionUtils.isNotEmpty(bgpPeerIds)) {
bgpPeerIdsToBeAdded = new ArrayList<>(bgpPeerIds);
@@ -1618,4 +1644,15 @@ public class RoutedIpv4ManagerImpl extends ComponentLifecycleBase implements Rou
public void removeBgpPeersByDomainId(long domainId) {
bgpPeerDao.removeByDomainId(domainId);
}
+
+ @Override
+ public Boolean isRoutedNetworkVpcEnabled(long zoneId) {
+ return RoutedNetworkVpcEnabled.valueIn(zoneId);
+ }
+
+ private void checkIfRoutedNetworkVpcEnabled(long zoneId) {
+ if (!isRoutedNetworkVpcEnabled(zoneId)) {
+ throw new InvalidParameterValueException("Routed networks and VPCs are not enabled for the zone.");
+ }
+ }
}
diff --git a/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java b/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java
index ace7bc85691..be31a1c6502 100644
--- a/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java
+++ b/server/src/test/java/com/cloud/bgp/BGPServiceImplTest.java
@@ -18,14 +18,39 @@
package com.cloud.bgp;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.Network;
+import com.cloud.network.NetworkModel;
+import com.cloud.network.dao.NetworkServiceMapDao;
+import com.cloud.network.element.VirtualRouterElement;
+import com.cloud.network.element.VpcVirtualRouterElement;
+import com.cloud.network.vpc.Vpc;
+import com.cloud.network.vpc.dao.VpcServiceMapDao;
+import com.cloud.user.AccountVO;
+import com.cloud.user.dao.AccountDao;
+import org.apache.cloudstack.network.BgpPeerVO;
+import org.apache.cloudstack.network.RoutedIpv4Manager;
+import org.apache.cloudstack.network.dao.BgpPeerDao;
+
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
-
+import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnitRunner;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
@RunWith(MockitoJUnitRunner.class)
public class BGPServiceImplTest {
@@ -33,6 +58,24 @@ public class BGPServiceImplTest {
@InjectMocks
BGPServiceImpl bGPServiceImplSpy = new BGPServiceImpl();
+ @Mock
+ RoutedIpv4Manager routedIpv4Manager;
+
+ @Mock
+ NetworkServiceMapDao ntwkSrvcDao;
+
+ @Mock
+ NetworkModel networkModel;
+
+ @Mock
+ BgpPeerDao bgpPeerDao;
+
+ @Mock
+ AccountDao accountDao;
+
+ @Mock
+ VpcServiceMapDao vpcServiceMapDao;
+
@Test
public void testASNumbersOverlap() {
Assert.assertEquals(bGPServiceImplSpy.isASNumbersOverlap(1,2,3,4), false);
@@ -42,4 +85,92 @@ public class BGPServiceImplTest {
Assert.assertEquals(bGPServiceImplSpy.isASNumbersOverlap(1,4,2,3), true);
Assert.assertEquals(bGPServiceImplSpy.isASNumbersOverlap(3,4,1,2), false);
}
+
+ @Test
+ public void testApplyBgpPeersForIsolatedNetwork() throws ResourceUnavailableException {
+ Long networkId = 11L;
+ Network network = Mockito.mock(Network.class);
+ when(network.getId()).thenReturn(networkId);
+ when(network.getVpcId()).thenReturn(null);
+
+ when(routedIpv4Manager.isDynamicRoutedNetwork(network)).thenReturn(true);
+ when(ntwkSrvcDao.getProviderForServiceInNetwork(networkId, Network.Service.Gateway)).thenReturn("VirtualRouter");
+ VirtualRouterElement virtualRouterElement = Mockito.mock(VirtualRouterElement.class);
+ when(networkModel.getElementImplementingProvider("VirtualRouter")).thenReturn(virtualRouterElement);
+
+ BgpPeerVO bgpPeer1 = Mockito.mock(BgpPeerVO.class);
+ List bgpPeers = Arrays.asList(bgpPeer1);
+ when(bgpPeerDao.listNonRevokeByNetworkId(networkId)).thenReturn(bgpPeers);
+ doReturn(true).when(virtualRouterElement).applyBgpPeers(null, network, bgpPeers);
+
+ bGPServiceImplSpy.applyBgpPeers(network, true);
+
+ verify(virtualRouterElement).applyBgpPeers(null, network, bgpPeers);
+ }
+
+ @Test
+ public void testApplyBgpPeersForVpcTier() throws ResourceUnavailableException {
+ Long networkId = 11L;
+ Long accountId = 12L;
+ Long vpcId = 13L;
+ Long zoneId = 1L;
+ Network network = Mockito.mock(Network.class);
+ when(network.getId()).thenReturn(networkId);
+ when(network.getVpcId()).thenReturn(vpcId);
+ when(network.getAccountId()).thenReturn(accountId);
+ when(network.getDataCenterId()).thenReturn(zoneId);
+
+ when(routedIpv4Manager.isDynamicRoutedNetwork(network)).thenReturn(true);
+ when(ntwkSrvcDao.getProviderForServiceInNetwork(networkId, Network.Service.Gateway)).thenReturn("VirtualRouter");
+ VirtualRouterElement virtualRouterElement = Mockito.mock(VirtualRouterElement.class);
+ when(networkModel.getElementImplementingProvider("VirtualRouter")).thenReturn(virtualRouterElement);
+
+ when(bgpPeerDao.listNonRevokeByVpcId(vpcId)).thenReturn(new ArrayList<>());
+
+ AccountVO owner = Mockito.mock(AccountVO.class);
+ when(accountDao.findByIdIncludingRemoved(accountId)).thenReturn(owner);
+
+ Long bgpPeerId1 = 14L;
+ BgpPeerVO bgpPeer1 = Mockito.mock(BgpPeerVO.class);
+ when(bgpPeerDao.findById(bgpPeerId1)).thenReturn(bgpPeer1);
+ when(routedIpv4Manager.getBgpPeerIdsForAccount(owner, zoneId)).thenReturn(Arrays.asList(bgpPeerId1));
+
+ doReturn(true).when(virtualRouterElement).applyBgpPeers(eq(null), eq(network), any());
+
+ bGPServiceImplSpy.applyBgpPeers(network, true);
+
+ verify(virtualRouterElement).applyBgpPeers(eq(null), eq(network), any());
+ }
+
+ @Test
+ public void testApplyBgpPeersForVpcWithBgpPeers() throws ResourceUnavailableException {
+ Long accountId = 12L;
+ Long vpcId = 13L;
+ Long zoneId = 1L;
+ Vpc vpc = Mockito.mock(Vpc.class);
+ when(vpc.getId()).thenReturn(vpcId);
+ when(vpc.getAccountId()).thenReturn(accountId);
+ when(vpc.getZoneId()).thenReturn(zoneId);
+
+ when(routedIpv4Manager.isDynamicRoutedVpc(vpc)).thenReturn(true);
+ when(vpcServiceMapDao.getProviderForServiceInVpc(vpcId, Network.Service.Gateway)).thenReturn("VPCVirtualRouter");
+ VpcVirtualRouterElement vpcVirtualRouterElement = Mockito.mock(VpcVirtualRouterElement.class);
+ when(networkModel.getElementImplementingProvider("VPCVirtualRouter")).thenReturn(vpcVirtualRouterElement);
+
+ when(bgpPeerDao.listNonRevokeByVpcId(vpcId)).thenReturn(new ArrayList<>());
+
+ AccountVO owner = Mockito.mock(AccountVO.class);
+ when(accountDao.findByIdIncludingRemoved(accountId)).thenReturn(owner);
+
+ Long bgpPeerId1 = 14L;
+ BgpPeerVO bgpPeer1 = Mockito.mock(BgpPeerVO.class);
+ when(bgpPeerDao.findById(bgpPeerId1)).thenReturn(bgpPeer1);
+ when(routedIpv4Manager.getBgpPeerIdsForAccount(owner, zoneId)).thenReturn(Arrays.asList(bgpPeerId1));
+
+ doReturn(true).when(vpcVirtualRouterElement).applyBgpPeers(eq(vpc), eq(null), any());
+
+ bGPServiceImplSpy.applyBgpPeers(vpc, true);
+
+ verify(vpcVirtualRouterElement).applyBgpPeers(eq(vpc), eq(null), any());
+ }
}
diff --git a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java
index 64d813c9ba8..51d5a61cbca 100644
--- a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java
+++ b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
@@ -29,6 +30,7 @@ import static org.mockito.Mockito.doReturn;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
@@ -46,6 +48,7 @@ import com.cloud.user.dao.AccountDao;
import com.cloud.utils.net.Ip;
import com.cloud.exception.InsufficientAddressCapacityException;
import org.apache.cloudstack.alert.AlertService;
+import org.apache.cloudstack.api.command.admin.network.CreateNetworkCmdByAdmin;
import org.apache.cloudstack.api.command.user.address.UpdateQuarantinedIpCmd;
import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd;
import org.apache.cloudstack.api.command.user.network.UpdateNetworkCmd;
@@ -753,13 +756,14 @@ public class NetworkServiceImplTest {
}
@Test
- public void testCreateIpv4RoutedNetwork() throws InsufficientCapacityException, ResourceAllocationException {
+ public void testCreateIpv4RoutedNetwork() {
registerCallContext();
CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class);
Mockito.when(cmd.getCidrSize()).thenReturn(24);
prepareCreateNetworkDnsMocks(cmd, Network.GuestType.Isolated, false, false, true);
when(networkOfferingVO.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED);
when(networkOfferingVO.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Static);
+ when(routedIpv4Manager.isRoutedNetworkVpcEnabled(nullable(Long.class))).thenReturn(true);
when(routedIpv4Manager.isVirtualRouterGateway(networkOfferingVO)).thenReturn(true);
doNothing().when(routedIpv4Manager).assignIpv4SubnetToNetwork(nullable(Network.class));
@@ -777,6 +781,92 @@ public class NetworkServiceImplTest {
Mockito.verify(routedIpv4Manager).assignIpv4SubnetToNetwork(nullable(Network.class));
}
+ @Test
+ public void testCreateIpv4RoutedNetworkWithBgpPeersFailure1() {
+ registerCallContext();
+ CreateNetworkCmdByAdmin cmd = Mockito.mock(CreateNetworkCmdByAdmin.class);
+ Mockito.when(cmd.getCidrSize()).thenReturn(24);
+ List bgpPeerIds = Arrays.asList(11L, 12L);
+ Mockito.when(cmd.getBgpPeerIds()).thenReturn(bgpPeerIds);
+
+ prepareCreateNetworkDnsMocks(cmd, Network.GuestType.Isolated, false, true, true);
+ when(networkOfferingVO.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED);
+ when(networkOfferingVO.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Static);
+ when(routedIpv4Manager.isRoutedNetworkVpcEnabled(nullable(Long.class))).thenReturn(true);
+ when(routedIpv4Manager.isVirtualRouterGateway(networkOfferingVO)).thenReturn(true);
+
+ DataCenterVO zone = Mockito.mock(DataCenterVO.class);
+ when(cmd.getZoneId()).thenReturn(zoneId);
+ when(dcDao.findById(zoneId)).thenReturn(zone);
+ when(zone.getId()).thenReturn(zoneId);
+
+ try {
+ service.createGuestNetwork(cmd);
+ } catch (InsufficientCapacityException | ResourceAllocationException e) {
+ Assert.fail(String.format("failure with exception: %s", e.getMessage()));
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals("The BGP peers of VPC tiers will inherit from the VPC, do not add separately.", ex.getMessage());
+ }
+ }
+
+ @Test
+ public void testCreateIpv4RoutedNetworkWithBgpPeersFailure2() {
+ registerCallContext();
+ CreateNetworkCmdByAdmin cmd = Mockito.mock(CreateNetworkCmdByAdmin.class);
+ Mockito.when(cmd.getCidrSize()).thenReturn(24);
+ List bgpPeerIds = Arrays.asList(11L, 12L);
+ Mockito.when(cmd.getBgpPeerIds()).thenReturn(bgpPeerIds);
+
+ prepareCreateNetworkDnsMocks(cmd, Network.GuestType.Isolated, false, false, true);
+ when(networkOfferingVO.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED);
+ when(networkOfferingVO.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Static);
+ when(routedIpv4Manager.isRoutedNetworkVpcEnabled(nullable(Long.class))).thenReturn(true);
+ when(routedIpv4Manager.isVirtualRouterGateway(networkOfferingVO)).thenReturn(true);
+
+ DataCenterVO zone = Mockito.mock(DataCenterVO.class);
+ when(cmd.getZoneId()).thenReturn(zoneId);
+ when(dcDao.findById(zoneId)).thenReturn(zone);
+ when(zone.getId()).thenReturn(zoneId);
+
+ try {
+ service.createGuestNetwork(cmd);
+ } catch (InsufficientCapacityException | ResourceAllocationException e) {
+ Assert.fail(String.format("failure with exception: %s", e.getMessage()));
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals("The network offering does not support Dynamic routing", ex.getMessage());
+ }
+ }
+
+ @Test
+ public void testCreateIpv4RoutedNetworkWithBgpPeersFailure3() {
+ registerCallContext();
+ CreateNetworkCmdByAdmin cmd = Mockito.mock(CreateNetworkCmdByAdmin.class);
+ Mockito.when(cmd.getCidrSize()).thenReturn(24);
+ List bgpPeerIds = Arrays.asList(11L, 12L);
+ Mockito.when(cmd.getBgpPeerIds()).thenReturn(bgpPeerIds);
+
+ prepareCreateNetworkDnsMocks(cmd, Network.GuestType.Isolated, false, false, true);
+ when(networkOfferingVO.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED);
+ when(networkOfferingVO.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Static);
+ when(routedIpv4Manager.isRoutedNetworkVpcEnabled(nullable(Long.class))).thenReturn(true);
+ when(routedIpv4Manager.isVirtualRouterGateway(networkOfferingVO)).thenReturn(true);
+ when(routedIpv4Manager.isDynamicRoutedNetwork(networkOfferingVO)).thenReturn(true);
+ doThrow(new InvalidParameterValueException("validation error")).when(routedIpv4Manager).validateBgpPeers(nullable(Account.class), nullable(Long.class), any(List.class));
+
+ DataCenterVO zone = Mockito.mock(DataCenterVO.class);
+ when(cmd.getZoneId()).thenReturn(zoneId);
+ when(dcDao.findById(zoneId)).thenReturn(zone);
+ when(zone.getId()).thenReturn(zoneId);
+
+ try {
+ service.createGuestNetwork(cmd);
+ } catch (InsufficientCapacityException | ResourceAllocationException e) {
+ Assert.fail(String.format("failure with exception: %s", e.getMessage()));
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals("validation error", ex.getMessage());
+ }
+ }
+
@Test
public void testCheckAndUpdateNetworkResetSuccess() {
NetworkVO networkVO = new NetworkVO();
diff --git a/server/src/test/java/com/cloud/network/element/VirtualRouterElementTest.java b/server/src/test/java/com/cloud/network/element/VirtualRouterElementTest.java
index b0d9cdc9dc4..de9858d2f07 100644
--- a/server/src/test/java/com/cloud/network/element/VirtualRouterElementTest.java
+++ b/server/src/test/java/com/cloud/network/element/VirtualRouterElementTest.java
@@ -65,6 +65,7 @@ import com.cloud.network.router.VirtualRouter;
import com.cloud.network.router.VirtualRouter.RedundantState;
import com.cloud.network.router.VpcVirtualNetworkApplianceManagerImpl;
import com.cloud.network.rules.dao.PortForwardingRulesDao;
+import com.cloud.network.vpc.VpcVO;
import com.cloud.offering.NetworkOffering;
import com.cloud.offerings.NetworkOfferingVO;
import com.cloud.offerings.dao.NetworkOfferingDao;
@@ -98,7 +99,11 @@ import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.network.BgpPeerVO;
import org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinitionBuilder;
+import org.apache.cloudstack.network.topology.NetworkTopology;
+import org.apache.cloudstack.network.topology.NetworkTopologyContext;
+import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -111,6 +116,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@@ -122,6 +128,7 @@ import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.lenient;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
@@ -178,6 +185,7 @@ public class VirtualRouterElementTest {
@Mock private ResourceManager _resourceMgr;
@Mock private UserVmManager _userVmMgr;
@Mock private VirtualMachineManager _itMgr;
+ @Mock private NetworkTopologyContext networkTopologyContext;
@InjectMocks
private RouterDeploymentDefinitionBuilder routerDeploymentDefinitionBuilder;
@@ -517,4 +525,52 @@ public class VirtualRouterElementTest {
assertTrue(counterNames.contains(AutoScaleCounterType.Memory.getName()));
assertTrue(counterNames.contains(AutoScaleCounterType.VirtualRouter.getName()));
}
+
+ @Test
+ public void testApplyBgpPeersForVpc() throws ResourceUnavailableException {
+ List bgpPeers = Mockito.mock(List.class);
+ VpcVO vpc = Mockito.mock(VpcVO.class);
+
+ DomainRouterVO router = Mockito.mock(DomainRouterVO.class);
+ when(router.getState()).thenReturn(VirtualMachine.State.Running);
+ long zoneId = 10L;
+ long vpcId = 11L;
+ when(vpc.getId()).thenReturn(vpcId);
+ when(vpc.getZoneId()).thenReturn(zoneId);
+ when(_routerDao.listByVpcId(vpcId)).thenReturn(Arrays.asList(router));
+ DataCenterVO dc = Mockito.mock(DataCenterVO.class);
+ when(_dcDao.findById(zoneId)).thenReturn(dc);
+ NetworkTopology networkTopology = Mockito.mock(NetworkTopology.class);
+ when(networkTopologyContext.retrieveNetworkTopology(dc)).thenReturn(networkTopology);
+ doReturn(true).when(networkTopology).applyBgpPeers(any(), any(), any());
+
+ boolean result = virtualRouterElement.applyBgpPeers(vpc, null, bgpPeers);
+
+ Assert.assertTrue(result);
+ verify(networkTopology).applyBgpPeers(any(), any(), any());
+ }
+
+ @Test
+ public void testApplyBgpPeersForNetwork() throws ResourceUnavailableException {
+ List bgpPeers = Mockito.mock(List.class);
+ NetworkVO network = Mockito.mock(NetworkVO.class);
+
+ DomainRouterVO router = Mockito.mock(DomainRouterVO.class);
+ when(router.getState()).thenReturn(VirtualMachine.State.Running);
+ long zoneId = 10L;
+ long networkId = 11L;
+ when(network.getId()).thenReturn(networkId);
+ when(network.getDataCenterId()).thenReturn(zoneId);
+ when(_routerDao.listByNetworkAndRole(networkId, VirtualRouter.Role.VIRTUAL_ROUTER)).thenReturn(Arrays.asList(router));
+ DataCenterVO dc = Mockito.mock(DataCenterVO.class);
+ when(_dcDao.findById(zoneId)).thenReturn(dc);
+ NetworkTopology networkTopology = Mockito.mock(NetworkTopology.class);
+ when(networkTopologyContext.retrieveNetworkTopology(dc)).thenReturn(networkTopology);
+ doReturn(true).when(networkTopology).applyBgpPeers(any(), any(), any());
+
+ boolean result = virtualRouterElement.applyBgpPeers(null, network, bgpPeers);
+
+ Assert.assertTrue(result);
+ verify(networkTopology).applyBgpPeers(any(), any(), any());
+ }
}
diff --git a/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java b/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java
index 37fa13baf98..bdd905841f9 100644
--- a/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java
+++ b/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java
@@ -17,12 +17,15 @@
package com.cloud.network.router;
import com.cloud.agent.api.Command;
+import com.cloud.agent.api.routing.SetBgpPeersCommand;
import com.cloud.agent.api.routing.VmDataCommand;
import com.cloud.agent.manager.Commands;
import com.cloud.configuration.ConfigurationManager;
+import com.cloud.dc.ASNumberVO;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.VlanVO;
+import com.cloud.dc.dao.ASNumberDao;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.VlanDao;
import com.cloud.network.NetworkModel;
@@ -43,6 +46,8 @@ import com.cloud.utils.net.Ip;
import com.cloud.vm.NicVO;
import com.cloud.vm.VirtualMachine;
import com.cloud.vm.dao.NicDao;
+import org.apache.cloudstack.network.BgpPeerVO;
+import org.apache.cloudstack.network.dao.BgpPeerDetailsDao;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -55,10 +60,13 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import static org.mockito.Mockito.when;
+
@RunWith(MockitoJUnitRunner.class)
public class CommandSetupHelperTest {
@@ -88,6 +96,10 @@ public class CommandSetupHelperTest {
RouterControlHelper routerControlHelper;
@Mock
DataCenterDao dcDao;
+ @Mock
+ ASNumberDao asNumberDao;
+ @Mock
+ BgpPeerDetailsDao bgpPeerDetailsDao;
@Before
public void setUp() {
@@ -173,24 +185,90 @@ public class CommandSetupHelperTest {
VpcVO vpc = new VpcVO();
DataCenterVO dc = new DataCenterVO(1L, null, null, null, null, null, null, null, null, null, DataCenter.NetworkType.Advanced, null, null);
- Mockito.when(router.getId()).thenReturn(14L);
- Mockito.when(router.getDataCenterId()).thenReturn(4L);
- Mockito.when(nicDao.listByVmId(ArgumentMatchers.anyLong())).thenReturn(List.of(nicVO));
- Mockito.when(networkDao.findById(ArgumentMatchers.anyLong())).thenReturn(networkVO);
- Mockito.when(ipAddressDao.listByAssociatedVpc(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Boolean.class))).thenReturn(userIps);
- Mockito.when(vlanDao.findById(ArgumentMatchers.anyLong())).thenReturn(vlanVO);
- Mockito.when(networkModel.getNetworkRate(ArgumentMatchers.anyLong(), ArgumentMatchers.anyLong())).thenReturn(1200);
- Mockito.when(networkModel.getNetwork(ArgumentMatchers.anyLong())).thenReturn(networkVO);
- Mockito.when(networkOfferingDao.findById(ArgumentMatchers.anyLong())).thenReturn(networkOfferingVO);
- Mockito.when(configurationManager.getNetworkOfferingNetworkRate(ArgumentMatchers.anyLong(), ArgumentMatchers.anyLong())).thenReturn(1200);
- Mockito.when(networkModel.isSecurityGroupSupportedInNetwork(networkVO)).thenReturn(false);
- Mockito.when(networkOfferingDetailsDao.getNtwkOffDetails(ArgumentMatchers.anyLong())).thenReturn(details);
- Mockito.when(networkDetailsDao.findDetail(ArgumentMatchers.anyLong(), ArgumentMatchers.anyString())).thenReturn(null);
- Mockito.when(vpcDao.findById(ArgumentMatchers.anyLong())).thenReturn(vpc);
- Mockito.when(routerControlHelper.getRouterControlIp(ArgumentMatchers.anyLong())).thenReturn("10.1.11.101");
- Mockito.when(dcDao.findById(ArgumentMatchers.anyLong())).thenReturn(dc);
+ when(router.getId()).thenReturn(14L);
+ when(router.getDataCenterId()).thenReturn(4L);
+ when(nicDao.listByVmId(ArgumentMatchers.anyLong())).thenReturn(List.of(nicVO));
+ when(networkDao.findById(ArgumentMatchers.anyLong())).thenReturn(networkVO);
+ when(ipAddressDao.listByAssociatedVpc(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Boolean.class))).thenReturn(userIps);
+ when(vlanDao.findById(ArgumentMatchers.anyLong())).thenReturn(vlanVO);
+ when(networkModel.getNetworkRate(ArgumentMatchers.anyLong(), ArgumentMatchers.anyLong())).thenReturn(1200);
+ when(networkModel.getNetwork(ArgumentMatchers.anyLong())).thenReturn(networkVO);
+ when(networkOfferingDao.findById(ArgumentMatchers.anyLong())).thenReturn(networkOfferingVO);
+ when(configurationManager.getNetworkOfferingNetworkRate(ArgumentMatchers.anyLong(), ArgumentMatchers.anyLong())).thenReturn(1200);
+ when(networkModel.isSecurityGroupSupportedInNetwork(networkVO)).thenReturn(false);
+ when(networkOfferingDetailsDao.getNtwkOffDetails(ArgumentMatchers.anyLong())).thenReturn(details);
+ when(networkDetailsDao.findDetail(ArgumentMatchers.anyLong(), ArgumentMatchers.anyString())).thenReturn(null);
+ when(vpcDao.findById(ArgumentMatchers.anyLong())).thenReturn(vpc);
+ when(routerControlHelper.getRouterControlIp(ArgumentMatchers.anyLong())).thenReturn("10.1.11.101");
+ when(dcDao.findById(ArgumentMatchers.anyLong())).thenReturn(dc);
commandSetupHelper.createVpcAssociatePublicIPCommands(router, pubIpList, commands, vlanMacAddress);
Assert.assertEquals(2, commands.size());
}
+
+ @Test
+ public void testCreateBgpPeersCommandsForNetwork() {
+ BgpPeerVO bgpPeer1 = Mockito.mock(BgpPeerVO.class);
+ BgpPeerVO bgpPeer2 = Mockito.mock(BgpPeerVO.class);
+ List bgpPeers = Arrays.asList(bgpPeer1, bgpPeer2);
+ Commands cmds = new Commands(Command.OnError.Stop);
+ VirtualRouter router = Mockito.mock(VirtualRouter.class);
+ NetworkVO network = Mockito.mock(NetworkVO.class);
+
+ long zoneId = 10L;
+ long networkId = 11L;
+ when(router.getDataCenterId()).thenReturn(zoneId);
+ when(router.getVpcId()).thenReturn(null);
+ when(network.getId()).thenReturn(networkId);
+ ASNumberVO asNumberVO = Mockito.mock(ASNumberVO.class);
+ when(asNumberDao.findByZoneAndNetworkId(zoneId, networkId)).thenReturn(asNumberVO);
+ DataCenterVO dc = Mockito.mock(DataCenterVO.class);
+ when(dcDao.findById(zoneId)).thenReturn(dc);
+ when(dc.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced);
+
+ commandSetupHelper.createBgpPeersCommands(bgpPeers, router, cmds, network);
+
+ Assert.assertEquals(1, cmds.size());
+ Command cmd = cmds.toCommands()[0];
+ Assert.assertTrue(cmd instanceof SetBgpPeersCommand);
+ Assert.assertEquals(2, ((SetBgpPeersCommand) cmd).getBpgPeers().length);
+ }
+
+ @Test
+ public void testCreateBgpPeersCommandsForVpc() {
+ BgpPeerVO bgpPeer1 = Mockito.mock(BgpPeerVO.class);
+ BgpPeerVO bgpPeer2 = Mockito.mock(BgpPeerVO.class);
+ List bgpPeers = Arrays.asList(bgpPeer1, bgpPeer2);
+ Commands cmds = new Commands(Command.OnError.Stop);
+ VirtualRouter router = Mockito.mock(VirtualRouter.class);
+ NetworkVO network = Mockito.mock(NetworkVO.class);
+
+ long zoneId = 10L;
+ long vpcId = 11L;
+ when(router.getDataCenterId()).thenReturn(zoneId);
+ when(router.getVpcId()).thenReturn(vpcId);
+ ASNumberVO asNumberVO = Mockito.mock(ASNumberVO.class);
+ when(asNumberDao.findByZoneAndVpcId(zoneId, vpcId)).thenReturn(asNumberVO);
+
+ long networkOfferingId = 12L;
+ NetworkOfferingVO offering = Mockito.mock(NetworkOfferingVO.class);
+ when(networkOfferingDao.findByIdIncludingRemoved(networkOfferingId)).thenReturn(offering);
+ when(offering.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Dynamic);
+ NetworkVO network1 = Mockito.mock(NetworkVO.class);
+ when(network1.getNetworkOfferingId()).thenReturn(networkOfferingId);
+ NetworkVO network2 = Mockito.mock(NetworkVO.class);
+ when(network2.getNetworkOfferingId()).thenReturn(networkOfferingId);
+ when(networkDao.listByVpc(vpcId)).thenReturn(Arrays.asList(network1, network2));
+
+ DataCenterVO dc = Mockito.mock(DataCenterVO.class);
+ when(dcDao.findById(zoneId)).thenReturn(dc);
+ when(dc.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced);
+
+ commandSetupHelper.createBgpPeersCommands(bgpPeers, router, cmds, network);
+
+ Assert.assertEquals(1, cmds.size());
+ Command cmd = cmds.toCommands()[0];
+ Assert.assertTrue(cmd instanceof SetBgpPeersCommand);
+ Assert.assertEquals(4, ((SetBgpPeersCommand) cmd).getBpgPeers().length);
+ }
}
diff --git a/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java b/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java
index ec5fdc0730f..1e55f55fbd6 100644
--- a/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java
+++ b/server/src/test/java/com/cloud/network/router/VirtualNetworkApplianceManagerImplTest.java
@@ -19,7 +19,10 @@ package com.cloud.network.router;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.CheckS2SVpnConnectionsAnswer;
import com.cloud.agent.api.CheckS2SVpnConnectionsCommand;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.manager.Commands;
import com.cloud.alert.AlertManager;
+import com.cloud.bgp.BGPService;
import com.cloud.cluster.dao.ManagementServerHostDao;
import com.cloud.dc.dao.ClusterDao;
import com.cloud.dc.dao.DataCenterDao;
@@ -30,6 +33,8 @@ import com.cloud.host.HostVO;
import com.cloud.host.Status;
import com.cloud.host.dao.HostDao;
import com.cloud.hypervisor.Hypervisor;
+import com.cloud.network.Network;
+import com.cloud.network.NetworkModel;
import com.cloud.network.Site2SiteVpnConnection;
import com.cloud.network.dao.FirewallRulesDao;
import com.cloud.network.dao.IPAddressDao;
@@ -37,6 +42,7 @@ import com.cloud.network.dao.LoadBalancerDao;
import com.cloud.network.dao.LoadBalancerVMMapDao;
import com.cloud.network.dao.MonitoringServiceDao;
import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.NetworkVO;
import com.cloud.network.dao.OpRouterMonitorServiceDao;
import com.cloud.network.dao.PhysicalNetworkServiceProviderDao;
import com.cloud.network.dao.RemoteAccessVpnDao;
@@ -49,6 +55,8 @@ import com.cloud.network.dao.UserIpv6AddressDao;
import com.cloud.network.dao.VirtualRouterProviderDao;
import com.cloud.network.dao.VpnUserDao;
import com.cloud.network.rules.dao.PortForwardingRulesDao;
+import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.network.vpn.Site2SiteVpnManager;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.service.dao.ServiceOfferingDao;
@@ -69,12 +77,15 @@ import com.cloud.vm.dao.UserVmDao;
import com.cloud.vm.dao.UserVmDetailsDao;
import com.cloud.vm.dao.VMInstanceDao;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
+import org.apache.cloudstack.network.BgpPeer;
+import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.apache.cloudstack.utils.identity.ManagementServerNode;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
@@ -233,6 +244,20 @@ public class VirtualNetworkApplianceManagerImplTest {
@InjectMocks
private VirtualNetworkApplianceManagerImpl virtualNetworkApplianceManagerImpl;
+ @Mock
+ private NetworkModel _networkModel;
+
+ @Mock
+ private RoutedIpv4Manager routedIpv4Manager;
+
+ @Mock
+ private CommandSetupHelper _commandSetupHelper;
+
+ @Mock
+ private VpcDao _vpcDao;
+
+ @Mock
+ private BGPService bgpService;
// @InjectMocks
// private VirtualNetworkApplianceManagerImpl virtualNetworkApplianceManagerImpl;
@@ -328,4 +353,42 @@ public class VirtualNetworkApplianceManagerImplTest {
result = virtualNetworkApplianceManagerImpl.checkLogrotateTimerPattern(foo);
Assert.assertTrue(result);
}
+
+ @Test
+ public void testFinalizeNetworkRulesForNetwork() {
+ Long guestNetworkId = 10L;
+ Commands cmds = new Commands(Command.OnError.Stop);
+ DomainRouterVO router = Mockito.mock(DomainRouterVO.class);
+
+ NetworkVO network = Mockito.mock(NetworkVO.class);
+ when(_networkDao.findById(guestNetworkId)).thenReturn(network);
+ when(network.getVpcId()).thenReturn(null);
+ when(routedIpv4Manager.isDynamicRoutedNetwork(network)).thenReturn(true);
+ List extends BgpPeer> bgpPeers = Mockito.mock(List.class);
+ doReturn(bgpPeers).when(bgpService).getBgpPeersForNetwork(network);
+ virtualNetworkApplianceManagerImpl.finalizeNetworkRulesForNetwork(cmds, router, Network.Provider.VirtualRouter, guestNetworkId);
+
+ Mockito.verify(_commandSetupHelper).createBgpPeersCommands(bgpPeers, router, cmds, network);
+ }
+
+ @Test
+ public void testFinalizeNetworkRulesForVpcNetwork() {
+ Long guestNetworkId = 10L;
+ Long vpcId = 11L;
+ Commands cmds = new Commands(Command.OnError.Stop);
+ DomainRouterVO router = Mockito.mock(DomainRouterVO.class);
+
+ NetworkVO network = Mockito.mock(NetworkVO.class);
+ when(_networkDao.findById(guestNetworkId)).thenReturn(network);
+ when(network.getVpcId()).thenReturn(vpcId);
+ VpcVO vpc = Mockito.mock(VpcVO.class);
+ when(_vpcDao.findById(vpcId)).thenReturn(vpc);
+ when(routedIpv4Manager.isDynamicRoutedVpc(vpc)).thenReturn(true);
+ List extends BgpPeer> bgpPeers = Mockito.mock(List.class);
+ doReturn(bgpPeers).when(bgpService).getBgpPeersForVpc(vpc);
+
+ virtualNetworkApplianceManagerImpl.finalizeNetworkRulesForNetwork(cmds, router, Network.Provider.VirtualRouter, guestNetworkId);
+
+ Mockito.verify(_commandSetupHelper).createBgpPeersCommands(bgpPeers, router, cmds, network);
+ }
}
diff --git a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java
index 54acaa58acc..ee56a092dd1 100644
--- a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java
+++ b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java
@@ -73,6 +73,7 @@ import org.apache.cloudstack.api.command.user.vpc.UpdateVPCCmd;
import org.apache.cloudstack.context.CallContext;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.network.Ipv4GuestSubnetNetworkMap;
import org.apache.cloudstack.network.RoutedIpv4Manager;
import org.junit.After;
import org.junit.Assert;
@@ -87,6 +88,7 @@ import org.springframework.test.util.ReflectionTestUtils;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -98,6 +100,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.anyString;
@@ -541,6 +544,30 @@ public class VpcManagerImplTest {
verify(routedIpv4Manager).getOrCreateIpv4SubnetForVpc(any(), anyString());
}
+ @Test
+ public void testCreateRoutedVpcWithDynamicRouting() {
+ mockVpcDnsResources(true, false);
+ VpcVO vpc = Mockito.mock(VpcVO.class);
+ Mockito.when(vpcDao.persist(any(), anyMap())).thenReturn(vpc);
+ Mockito.when(vpc.getUuid()).thenReturn("uuid");
+ doReturn(true).when(routedIpv4Manager).isRoutedVpc(any());
+ doReturn(true).when(routedIpv4Manager).isVpcVirtualRouterGateway(vpcOfferingVO);
+ doReturn(true).when(routedIpv4Manager).isDynamicRoutedVpc(vpcOfferingVO);
+ Ipv4GuestSubnetNetworkMap ipv4GuestSubnetNetworkMap = Mockito.mock(Ipv4GuestSubnetNetworkMap.class);
+ doReturn(ipv4GuestSubnetNetworkMap).when(routedIpv4Manager).getOrCreateIpv4SubnetForVpc(any(), anyInt());
+ List bgpPeerIds = Arrays.asList(11L, 12L);
+ try {
+ doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc);
+ manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, null, vpcDomain,
+ ip4Dns[0], ip4Dns[1], null, null, true, 1500, 24, null, bgpPeerIds);
+ } catch (ResourceAllocationException e) {
+ Assert.fail(String.format("failure with exception: %s", e.getMessage()));
+ }
+
+ verify(routedIpv4Manager).getOrCreateIpv4SubnetForVpc(any(), anyInt());
+ verify(routedIpv4Manager).validateBgpPeers(any(), any(), any());
+ }
+
@Test
public void validateVpcPrivateGatewayAclIdTestNullAclVoThrowsInvalidParameterValueException() {
Mockito.doReturn(null).when(networkACLDaoMock).findById(aclId);
diff --git a/server/src/test/java/org/apache/cloudstack/network/RoutedIpv4ManagerImplTest.java b/server/src/test/java/org/apache/cloudstack/network/RoutedIpv4ManagerImplTest.java
new file mode 100644
index 00000000000..5351abe2e2d
--- /dev/null
+++ b/server/src/test/java/org/apache/cloudstack/network/RoutedIpv4ManagerImplTest.java
@@ -0,0 +1,1217 @@
+// 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.
+//
+
+package org.apache.cloudstack.network;
+
+import com.cloud.api.ApiDBUtils;
+import com.cloud.bgp.BGPService;
+import com.cloud.dc.DataCenterVO;
+import com.cloud.domain.DomainVO;
+import com.cloud.exception.InvalidParameterValueException;
+import com.cloud.exception.NetworkRuleConflictException;
+import com.cloud.exception.ResourceUnavailableException;
+import com.cloud.network.Network;
+import com.cloud.network.NetworkModel;
+import com.cloud.network.dao.FirewallRulesDao;
+import com.cloud.network.dao.NetworkDao;
+import com.cloud.network.dao.NetworkServiceMapDao;
+import com.cloud.network.dao.NetworkVO;
+import com.cloud.network.element.VirtualRouterElement;
+import com.cloud.network.firewall.FirewallService;
+import com.cloud.network.rules.FirewallManager;
+import com.cloud.network.rules.FirewallRule;
+import com.cloud.network.rules.FirewallRuleVO;
+import com.cloud.network.vpc.Vpc;
+import com.cloud.network.vpc.VpcOfferingVO;
+import com.cloud.network.vpc.VpcVO;
+import com.cloud.network.vpc.dao.VpcDao;
+import com.cloud.network.vpc.dao.VpcOfferingDao;
+import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
+import com.cloud.offering.NetworkOffering;
+import com.cloud.offerings.NetworkOfferingVO;
+import com.cloud.offerings.dao.NetworkOfferingDao;
+import com.cloud.offerings.dao.NetworkOfferingServiceMapDao;
+import com.cloud.projects.ProjectVO;
+import com.cloud.user.Account;
+import com.cloud.user.AccountManager;
+import com.cloud.user.AccountVO;
+import com.cloud.user.User;
+import com.cloud.user.UserVO;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.net.NetUtils;
+
+import org.apache.cloudstack.api.command.admin.network.CreateIpv4SubnetForGuestNetworkCmd;
+import org.apache.cloudstack.api.command.admin.network.CreateIpv4SubnetForZoneCmd;
+import org.apache.cloudstack.api.command.admin.network.DedicateIpv4SubnetForZoneCmd;
+import org.apache.cloudstack.api.command.admin.network.DeleteIpv4SubnetForGuestNetworkCmd;
+import org.apache.cloudstack.api.command.admin.network.DeleteIpv4SubnetForZoneCmd;
+import org.apache.cloudstack.api.command.admin.network.ReleaseDedicatedIpv4SubnetForZoneCmd;
+import org.apache.cloudstack.api.command.admin.network.UpdateIpv4SubnetForZoneCmd;
+import org.apache.cloudstack.api.command.admin.network.bgp.ChangeBgpPeersForNetworkCmd;
+import org.apache.cloudstack.api.command.admin.network.bgp.ChangeBgpPeersForVpcCmd;
+import org.apache.cloudstack.api.command.admin.network.bgp.CreateBgpPeerCmd;
+import org.apache.cloudstack.api.command.admin.network.bgp.DedicateBgpPeerCmd;
+import org.apache.cloudstack.api.command.admin.network.bgp.DeleteBgpPeerCmd;
+import org.apache.cloudstack.api.command.admin.network.bgp.ReleaseDedicatedBgpPeerCmd;
+import org.apache.cloudstack.api.command.admin.network.bgp.UpdateBgpPeerCmd;
+import org.apache.cloudstack.api.command.user.network.routing.CreateRoutingFirewallRuleCmd;
+import org.apache.cloudstack.api.command.user.network.routing.UpdateRoutingFirewallRuleCmd;
+import org.apache.cloudstack.api.response.BgpPeerResponse;
+import org.apache.cloudstack.api.response.DataCenterIpv4SubnetResponse;
+import org.apache.cloudstack.api.response.Ipv4SubnetForGuestNetworkResponse;
+import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnet;
+import org.apache.cloudstack.datacenter.DataCenterIpv4GuestSubnetVO;
+import org.apache.cloudstack.datacenter.dao.DataCenterIpv4GuestSubnetDao;
+import org.apache.cloudstack.framework.config.ConfigKey;
+import org.apache.cloudstack.network.dao.BgpPeerDao;
+import org.apache.cloudstack.network.dao.BgpPeerDetailsDao;
+import org.apache.cloudstack.network.dao.BgpPeerNetworkMapDao;
+import org.apache.cloudstack.network.dao.Ipv4GuestSubnetNetworkMapDao;
+import org.apache.commons.collections.CollectionUtils;
+import org.junit.AfterClass;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockedStatic;
+import org.mockito.Spy;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class RoutedIpv4ManagerImplTest {
+
+ @Spy
+ @InjectMocks
+ RoutedIpv4ManagerImpl routedIpv4Manager = new RoutedIpv4ManagerImpl();
+
+ @Mock
+ DataCenterIpv4GuestSubnetDao dataCenterIpv4GuestSubnetDao;
+ @Mock
+ Ipv4GuestSubnetNetworkMapDao ipv4GuestSubnetNetworkMapDao;
+ @Mock
+ FirewallService firewallService;
+ @Mock
+ FirewallManager firewallManager;
+ @Mock
+ FirewallRulesDao firewallDao;
+ @Mock
+ NetworkServiceMapDao networkServiceMapDao;
+ @Mock
+ NetworkOfferingServiceMapDao networkOfferingServiceMapDao;
+ @Mock
+ NetworkOfferingDao networkOfferingDao;
+ @Mock
+ NetworkModel networkModel;
+ @Mock
+ AccountManager accountManager;
+ @Mock
+ VpcOfferingDao vpcOfferingDao;
+ @Mock
+ VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
+ @Mock
+ VpcDao vpcDao;
+ @Mock
+ BgpPeerDao bgpPeerDao;
+ @Mock
+ BgpPeerDetailsDao bgpPeerDetailsDao;
+ @Mock
+ BgpPeerNetworkMapDao bgpPeerNetworkMapDao;
+ @Mock
+ NetworkDao networkDao;
+ @Mock
+ BGPService bgpService;
+
+ static MockedStatic apiDBUtilsMocked;
+
+ @Mock
+ DataCenterIpv4GuestSubnetVO subnetVO;
+ @Mock
+ DomainVO domain;
+ @Mock
+ AccountVO account;
+ @Mock
+ ProjectVO project;
+ @Mock
+ Ipv4GuestSubnetNetworkMapVO ipv4GuestSubnetNetworkMap;
+ @Mock
+ NetworkVO network;
+ @Mock
+ VpcVO vpc;
+ @Mock
+ DataCenterVO zone;
+ @Mock
+ FirewallRuleVO rule;
+ @Mock
+ BgpPeerVO bgpPeer;
+ @Mock
+ NetworkOfferingVO networkOffering;
+ @Mock
+ VpcOfferingVO vpcOffering;
+
+ Long zoneId = 1L;
+ String zoneUuid = "zone-uuid";
+ String zoneName = "zone-name";
+ Long zoneSubnetId = 2L;
+ String zoneSubnetUuid = "zone-subnet-uuid";
+ Long domainId = 3L;
+ Long accountId = 4L;
+ Long ipv4GuestSubnetNetworkMapId = 5L;
+ Long networkId = 6L;
+ String networkUuid = "network-uuid";
+ String networkName = "network-name";
+ Long vpcId = 7L;
+ String vpcUuid = "vpc-uuid";
+ String vpcName = "vpc-name";
+ String domainName = "domain";
+ String domainUuid = "domain-uuid";
+ String accountName = "user";
+ String subnet = "172.16.1.0/24";
+ String newSubnet = "172.16.2.0/24";
+ String subnetForNetwork = "172.16.1.0/28";
+ String newSubnetForNetwork = "172.16.1.64/28";
+ String newSubnetForNetworkTooSmall = "172.16.1.0/30";
+ String newSubnetForNetworkTooBig = "172.16.1.0/23";
+ Integer cidrSize = 28;
+ Ipv4GuestSubnetNetworkMap.State ipv4GuestSubnetNetworkMapState = Ipv4GuestSubnetNetworkMap.State.Free;
+ Date created = new Date();
+ String uuid = "xxx-yyy-zzz";
+ Long ruleId = 8L;
+ String ip4Address = "10.10.11.11";
+ String ip6Address = "fd00:10:10:11:11::1";
+ Long asNumber = 9999L;
+ String password = "password-text";
+ Long bgpPeerId = 9L;
+ String bgpPeerUuid = "bgp-peer-uuid";
+ Long projectId = 10L;
+ String projectName = "project";
+ String projectUuid = "project-uuid";
+ Long networkOfferingId = 11L;
+ Long vpcOfferingId = 12L;
+
+ @BeforeClass
+ public static void setup() {
+ apiDBUtilsMocked = Mockito.mockStatic(ApiDBUtils.class);
+ apiDBUtilsMocked.when(() -> ApiDBUtils.findZoneById(Mockito.anyLong())).thenReturn(Mockito.mock(DataCenterVO.class));
+
+ CallContext.unregister();
+ AccountVO account = new AccountVO("admin", 1L, "", Account.Type.ADMIN, "uuid");
+ account.setId(2L);
+ UserVO user = new UserVO(1, "admin", "password", "firstname", "lastName", "email", "timezone", UUID.randomUUID().toString(), User.Source.UNKNOWN);
+ CallContext.register(user, account);
+ }
+
+ @AfterClass
+ public static void close() {
+ apiDBUtilsMocked.close();
+
+ CallContext.unregister();
+ }
+
+ @Test
+ public void testGetCommands() throws NoSuchFieldException, IllegalAccessException {
+ Assert.assertTrue(CollectionUtils.isNotEmpty(routedIpv4Manager.getCommands()));
+ Assert.assertEquals(26, routedIpv4Manager.getCommands().size());
+
+ overrideDefaultConfigValue(RoutedIpv4Manager.RoutedNetworkVpcEnabled, "_defaultValue", "false");
+ Assert.assertTrue(CollectionUtils.isEmpty(routedIpv4Manager.getCommands()));
+ }
+
+ private static void overrideDefaultConfigValue(final ConfigKey configKey, final String name, final Object o) throws IllegalAccessException, NoSuchFieldException {
+ Field f = ConfigKey.class.getDeclaredField(name);
+ f.setAccessible(true);
+ f.set(configKey, o);
+ }
+
+ @Test
+ public void testCreateDataCenterIpv4GuestSubnet() {
+ CreateIpv4SubnetForZoneCmd cmd = new CreateIpv4SubnetForZoneCmd();
+ ReflectionTestUtils.setField(cmd,"zoneId", zoneId);
+ ReflectionTestUtils.setField(cmd,"subnet", subnet);
+
+ routedIpv4Manager.createDataCenterIpv4GuestSubnet(cmd);
+
+ verify(routedIpv4Manager).checkConflicts(new ArrayList<>(), subnet, null);
+ verify(dataCenterIpv4GuestSubnetDao).persist(any());
+ }
+
+ @Test
+ public void testCreateDataCenterIpv4SubnetResponse() {
+ DataCenterIpv4GuestSubnetVO subnetVO = new DataCenterIpv4GuestSubnetVO(zoneId, NetUtils.transformCidr(subnet));
+ DataCenterIpv4SubnetResponse response = routedIpv4Manager.createDataCenterIpv4SubnetResponse(subnetVO);
+
+ Assert.assertEquals(subnet, response.getSubnet());
+ Assert.assertEquals(subnetVO.getUuid(), response.getId());
+ Assert.assertEquals("zoneipv4subnet", response.getObjectName());
+ }
+
+ @Test
+ public void testDeleteDataCenterIpv4GuestSubnet() {
+ DeleteIpv4SubnetForZoneCmd cmd = new DeleteIpv4SubnetForZoneCmd();
+ ReflectionTestUtils.setField(cmd,"id", zoneSubnetId);
+
+ routedIpv4Manager.deleteDataCenterIpv4GuestSubnet(cmd);
+
+ verify(ipv4GuestSubnetNetworkMapDao).deleteByParentId(zoneSubnetId);
+ verify(dataCenterIpv4GuestSubnetDao).remove(zoneSubnetId);
+ }
+
+ @Test
+ public void testUpdateDataCenterIpv4GuestSubnet() {
+ UpdateIpv4SubnetForZoneCmd cmd = new UpdateIpv4SubnetForZoneCmd();
+ ReflectionTestUtils.setField(cmd,"id", zoneSubnetId);
+ ReflectionTestUtils.setField(cmd,"subnet", newSubnet);
+
+ when(dataCenterIpv4GuestSubnetDao.findById(zoneSubnetId)).thenReturn(subnetVO);
+
+ routedIpv4Manager.updateDataCenterIpv4GuestSubnet(cmd);
+
+ verify(routedIpv4Manager).checkConflicts(new ArrayList<>(), newSubnet, zoneSubnetId);
+ verify(ipv4GuestSubnetNetworkMapDao).listByParent(zoneSubnetId);
+ verify(dataCenterIpv4GuestSubnetDao).update(eq(zoneSubnetId), any(DataCenterIpv4GuestSubnetVO.class));
+ }
+
+ @Test
+ public void testCheckConflicts1() {
+ DataCenterIpv4GuestSubnetVO existingSubnet = Mockito.mock(DataCenterIpv4GuestSubnetVO.class);
+ when(existingSubnet.getSubnet()).thenReturn(subnet);
+ List existingSubnets = Arrays.asList(existingSubnet);
+ routedIpv4Manager.checkConflicts(existingSubnets, newSubnet, null);
+ }
+
+ @Test(expected = InvalidParameterValueException.class)
+ public void testCheckConflicts2() {
+ DataCenterIpv4GuestSubnetVO existingSubnet = Mockito.mock(DataCenterIpv4GuestSubnetVO.class);
+ when(existingSubnet.getSubnet()).thenReturn(subnet);
+ List existingSubnets = Arrays.asList(existingSubnet);
+ routedIpv4Manager.checkConflicts(existingSubnets, subnet, null);
+ }
+
+ @Test
+ public void testCheckConflicts3() {
+ DataCenterIpv4GuestSubnetVO existingSubnet = Mockito.mock(DataCenterIpv4GuestSubnetVO.class);
+ when(existingSubnet.getId()).thenReturn(zoneSubnetId);
+ List existingSubnets = Arrays.asList(existingSubnet);
+ routedIpv4Manager.checkConflicts(existingSubnets, subnet, zoneSubnetId);
+ }
+
+ @Test
+ public void testDedicateDataCenterIpv4GuestSubnet() {
+ DedicateIpv4SubnetForZoneCmd cmd = new DedicateIpv4SubnetForZoneCmd();
+ ReflectionTestUtils.setField(cmd,"id", zoneSubnetId);
+ ReflectionTestUtils.setField(cmd,"domainId", domainId);
+ ReflectionTestUtils.setField(cmd,"accountName", accountName);
+ ReflectionTestUtils.setField(cmd,"projectId", null);
+
+ when(dataCenterIpv4GuestSubnetDao.findById(zoneSubnetId)).thenReturn(subnetVO);
+
+ when(accountManager.finalyzeAccountId(accountName, domainId, null, false)).thenReturn(accountId);
+ when(accountManager.getAccount(accountId)).thenReturn(account);
+ when(account.getDomainId()).thenReturn(domainId);
+ when(dataCenterIpv4GuestSubnetDao.findById(zoneSubnetId)).thenReturn(subnetVO);
+
+ routedIpv4Manager.dedicateDataCenterIpv4GuestSubnet(cmd);
+
+ verify(subnetVO).setDomainId(domainId);
+ verify(subnetVO).setAccountId(accountId);
+ verify(dataCenterIpv4GuestSubnetDao).update(zoneSubnetId, subnetVO);
+ }
+
+ @Test
+ public void testReleaseDedicatedDataCenterIpv4GuestSubnet() {
+ ReleaseDedicatedIpv4SubnetForZoneCmd cmd = new ReleaseDedicatedIpv4SubnetForZoneCmd();
+ ReflectionTestUtils.setField(cmd,"id", zoneSubnetId);
+ when(dataCenterIpv4GuestSubnetDao.findById(zoneSubnetId)).thenReturn(subnetVO);
+
+ routedIpv4Manager.releaseDedicatedDataCenterIpv4GuestSubnet(cmd);
+
+ verify(subnetVO).setDomainId(null);
+ verify(subnetVO).setAccountId(null);
+ verify(dataCenterIpv4GuestSubnetDao).update(zoneSubnetId, subnetVO);
+ }
+
+ @Test
+ public void testCreateIpv4SubnetForGuestNetwork1() {
+ CreateIpv4SubnetForGuestNetworkCmd cmd = new CreateIpv4SubnetForGuestNetworkCmd();
+
+ try {
+ routedIpv4Manager.createIpv4SubnetForGuestNetwork(cmd);
+ Assert.fail("creating IPv4 subnet for guest network should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals("One of subnet and cidrsize must be specified", ex.getMessage());
+ }
+ }
+
+ @Test
+ public void testCreateIpv4SubnetForGuestNetwork2() {
+ CreateIpv4SubnetForGuestNetworkCmd cmd = new CreateIpv4SubnetForGuestNetworkCmd();
+ ReflectionTestUtils.setField(cmd,"subnet", subnet);
+ ReflectionTestUtils.setField(cmd,"cidrSize", cidrSize);
+ try {
+ routedIpv4Manager.createIpv4SubnetForGuestNetwork(cmd);
+ Assert.fail("creating IPv4 subnet for guest network should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals("subnet and cidrsize are mutually exclusive", ex.getMessage());
+ }
+ }
+
+ @Test
+ public void testCreateIpv4SubnetForGuestNetwork3() {
+ CreateIpv4SubnetForGuestNetworkCmd cmd = new CreateIpv4SubnetForGuestNetworkCmd();
+ ReflectionTestUtils.setField(cmd,"subnet", subnet);
+ ReflectionTestUtils.setField(cmd,"parentId", zoneSubnetId);
+ when(dataCenterIpv4GuestSubnetDao.findById(zoneSubnetId)).thenReturn(subnetVO);
+ doReturn(ipv4GuestSubnetNetworkMap).when(routedIpv4Manager).createIpv4SubnetFromParentSubnet(subnetVO, subnet);
+
+ Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.createIpv4SubnetForGuestNetwork(cmd);
+
+ Assert.assertEquals(ipv4GuestSubnetNetworkMap, result);
+ }
+
+ @Test
+ public void testCreateIpv4SubnetForGuestNetwork4() {
+ CreateIpv4SubnetForGuestNetworkCmd cmd = new CreateIpv4SubnetForGuestNetworkCmd();
+ ReflectionTestUtils.setField(cmd,"cidrSize", cidrSize);
+ ReflectionTestUtils.setField(cmd,"parentId", zoneSubnetId);
+ when(dataCenterIpv4GuestSubnetDao.findById(zoneSubnetId)).thenReturn(subnetVO);
+ doReturn(ipv4GuestSubnetNetworkMap).when(routedIpv4Manager).createIpv4SubnetFromParentSubnet(subnetVO, cidrSize);
+
+ Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.createIpv4SubnetForGuestNetwork(cmd);
+
+ Assert.assertEquals(ipv4GuestSubnetNetworkMap, result);
+ }
+
+ @Test
+ public void testDeleteIpv4SubnetForGuestNetwork() {
+ DeleteIpv4SubnetForGuestNetworkCmd cmd = new DeleteIpv4SubnetForGuestNetworkCmd();
+ ReflectionTestUtils.setField(cmd,"id", ipv4GuestSubnetNetworkMapId);
+
+ when(ipv4GuestSubnetNetworkMapDao.findById(ipv4GuestSubnetNetworkMapId)).thenReturn(ipv4GuestSubnetNetworkMap);
+ when(ipv4GuestSubnetNetworkMap.getState()).thenReturn(ipv4GuestSubnetNetworkMapState);
+ when(ipv4GuestSubnetNetworkMap.getNetworkId()).thenReturn(null);
+
+ routedIpv4Manager.deleteIpv4SubnetForGuestNetwork(cmd);
+
+ verify(ipv4GuestSubnetNetworkMapDao).remove(ipv4GuestSubnetNetworkMapId);
+ }
+
+ @Test
+ public void testReleaseIpv4SubnetForGuestNetwork() {
+ when(ipv4GuestSubnetNetworkMapDao.findByNetworkId(networkId)).thenReturn(ipv4GuestSubnetNetworkMap);
+ when(ipv4GuestSubnetNetworkMap.getId()).thenReturn(ipv4GuestSubnetNetworkMapId);
+
+ routedIpv4Manager.releaseIpv4SubnetForGuestNetwork(networkId);
+
+ verify(ipv4GuestSubnetNetworkMapDao).remove(ipv4GuestSubnetNetworkMapId);
+ }
+
+ @Test
+ public void testReleaseIpv4SubnetForVpc() {
+ when(ipv4GuestSubnetNetworkMapDao.findByVpcId(vpcId)).thenReturn(ipv4GuestSubnetNetworkMap);
+ when(ipv4GuestSubnetNetworkMap.getId()).thenReturn(ipv4GuestSubnetNetworkMapId);
+
+ routedIpv4Manager.releaseIpv4SubnetForVpc(vpcId);
+
+ verify(ipv4GuestSubnetNetworkMapDao).remove(ipv4GuestSubnetNetworkMapId);
+ }
+
+ @Test
+ public void testCreateIpv4SubnetForGuestNetworkResponse() {
+ when(ipv4GuestSubnetNetworkMap.getCreated()).thenReturn(created);
+ when(ipv4GuestSubnetNetworkMap.getSubnet()).thenReturn(subnet);
+ when(ipv4GuestSubnetNetworkMap.getState()).thenReturn(ipv4GuestSubnetNetworkMapState);
+ when(ipv4GuestSubnetNetworkMap.getUuid()).thenReturn(uuid);
+
+ when(ipv4GuestSubnetNetworkMap.getNetworkId()).thenReturn(networkId);
+ apiDBUtilsMocked.when(() -> ApiDBUtils.findNetworkById(Mockito.anyLong())).thenReturn(network);
+ when(network.getName()).thenReturn(networkName);
+ when(network.getUuid()).thenReturn(networkUuid);
+ when(network.getDataCenterId()).thenReturn(zoneId);
+
+ when(ipv4GuestSubnetNetworkMap.getVpcId()).thenReturn(vpcId);
+ apiDBUtilsMocked.when(() -> ApiDBUtils.findVpcById(Mockito.anyLong())).thenReturn(vpc);
+ when(vpc.getName()).thenReturn(vpcName);
+ when(vpc.getUuid()).thenReturn(vpcUuid);
+ when(vpc.getZoneId()).thenReturn(zoneId);
+
+ when(ipv4GuestSubnetNetworkMap.getParentId()).thenReturn(zoneSubnetId);
+ when(dataCenterIpv4GuestSubnetDao.findById(zoneSubnetId)).thenReturn(subnetVO);
+ when(subnetVO.getSubnet()).thenReturn(subnet);
+ when(subnetVO.getUuid()).thenReturn(zoneSubnetUuid);
+ when(subnetVO.getDataCenterId()).thenReturn(zoneId);
+
+ apiDBUtilsMocked.when(() -> ApiDBUtils.findZoneById(zoneId)).thenReturn(zone);
+ when(zone.getName()).thenReturn(zoneName);
+ when(zone.getUuid()).thenReturn(zoneUuid);
+
+ Ipv4SubnetForGuestNetworkResponse response = routedIpv4Manager.createIpv4SubnetForGuestNetworkResponse(ipv4GuestSubnetNetworkMap);
+
+ Assert.assertEquals(created, response.getCreated());
+ Assert.assertEquals(subnet, response.getSubnet());
+ Assert.assertEquals(ipv4GuestSubnetNetworkMapState.name(), response.getState());
+ Assert.assertEquals(uuid, response.getId());
+ Assert.assertEquals(networkName, response.getNetworkName());
+ Assert.assertEquals(networkUuid, response.getNetworkId());
+ Assert.assertEquals(vpcName, response.getVpcName());
+ Assert.assertEquals(vpcUuid, response.getVpcId());
+ Assert.assertEquals(subnet, response.getParentSubnet());
+ Assert.assertEquals(zoneSubnetUuid, response.getParentId());
+ Assert.assertEquals(zoneUuid, response.getZoneId());
+ Assert.assertEquals(zoneName, response.getZoneName());
+ Assert.assertEquals("ipv4subnetforguestnetwork", response.getObjectName());
+ }
+
+ @Test
+ public void testGetOrCreateIpv4SubnetForGuestNetworkOrVpcInternalByCidr1() {
+ when(ipv4GuestSubnetNetworkMapDao.findBySubnet(subnet)).thenReturn(ipv4GuestSubnetNetworkMap);
+ when(ipv4GuestSubnetNetworkMap.getNetworkId()).thenReturn(null);
+ when(ipv4GuestSubnetNetworkMap.getVpcId()).thenReturn(null);
+ when(ipv4GuestSubnetNetworkMap.getParentId()).thenReturn(zoneSubnetId);
+ when(dataCenterIpv4GuestSubnetDao.findById(zoneSubnetId)).thenReturn(subnetVO);
+ when(subnetVO.getDomainId()).thenReturn(domainId);
+ when(subnetVO.getAccountId()).thenReturn(accountId);
+
+ routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(subnet, domainId, accountId, zoneId);
+ }
+
+ @Test
+ public void testGetOrCreateIpv4SubnetForGuestNetworkOrVpcInternalByCidr2() {
+ when(ipv4GuestSubnetNetworkMapDao.findBySubnet(subnet)).thenReturn(null);
+
+ routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(subnet, domainId, accountId, zoneId);
+
+ verify(ipv4GuestSubnetNetworkMapDao).persist(any(Ipv4GuestSubnetNetworkMapVO.class));
+ }
+
+ @Test
+ public void testGetOrCreateIpv4SubnetForGuestNetworkOrVpcInternalByCidr3() {
+ when(ipv4GuestSubnetNetworkMapDao.findBySubnet(subnet)).thenReturn(null);
+ when(routedIpv4Manager.getParentOfNetworkCidr(zoneId, subnet)).thenReturn(subnetVO);
+ when(subnetVO.getDomainId()).thenReturn(domainId);
+ when(subnetVO.getAccountId()).thenReturn(accountId);
+
+ routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(subnet, domainId, accountId, zoneId);
+
+ verify(ipv4GuestSubnetNetworkMapDao).persist(any(Ipv4GuestSubnetNetworkMapVO.class));
+ }
+
+ @Test
+ public void testGetOrCreateIpv4SubnetForGuestNetworkOrVpcInternalByCidrSize1() {
+ Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, domainId, accountId, zoneId);
+ Assert.assertNull(result);
+ }
+
+ @Test
+ public void testGetOrCreateIpv4SubnetForGuestNetworkOrVpcInternalByCidrSize2() {
+ DataCenterIpv4GuestSubnetVO subnet1 = Mockito.mock(DataCenterIpv4GuestSubnetVO.class);
+ when(dataCenterIpv4GuestSubnetDao.listByDataCenterIdAndAccountId(zoneId, accountId)).thenReturn(Arrays.asList(subnet1));
+ DataCenterIpv4GuestSubnetVO subnet2 = Mockito.mock(DataCenterIpv4GuestSubnetVO.class);
+ when(dataCenterIpv4GuestSubnetDao.listByDataCenterIdAndDomainId(zoneId, domainId)).thenReturn(Arrays.asList(subnet2));
+ DataCenterIpv4GuestSubnetVO subnet3 = Mockito.mock(DataCenterIpv4GuestSubnetVO.class);
+ when(dataCenterIpv4GuestSubnetDao.listNonDedicatedByDataCenterId(zoneId)).thenReturn(Arrays.asList(subnet3));
+
+ doReturn(null).doReturn(null).doReturn(ipv4GuestSubnetNetworkMap).when(routedIpv4Manager).getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(eq(cidrSize), any());
+
+ Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, domainId, accountId, zoneId);
+
+ Assert.assertEquals(ipv4GuestSubnetNetworkMap, result);
+ verify(routedIpv4Manager, times(3)).getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(eq(cidrSize), any());
+ }
+
+ @Test
+ public void testGetOrCreateIpv4SubnetForGuestNetworkOrVpcInternalByCidrAndParentSubnet1() {
+ Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, subnetVO);
+ Assert.assertNull(result);
+ }
+
+ @Test
+ public void testGetOrCreateIpv4SubnetForGuestNetworkOrVpcInternalByCidrAndParentSubnet2() {
+ when(subnetVO.getId()).thenReturn(zoneSubnetId);
+ when(ipv4GuestSubnetNetworkMapDao.findFirstAvailable(zoneSubnetId, cidrSize)).thenReturn(ipv4GuestSubnetNetworkMap);
+
+ Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, subnetVO);
+ Assert.assertEquals(ipv4GuestSubnetNetworkMap, result);
+ }
+
+ @Test
+ public void testGetOrCreateIpv4SubnetForGuestNetworkOrVpcInternalByCidrAndParentSubnet3() {
+ when(subnetVO.getId()).thenReturn(zoneSubnetId);
+ when(ipv4GuestSubnetNetworkMapDao.findFirstAvailable(zoneSubnetId, cidrSize)).thenReturn(null);
+ doReturn(ipv4GuestSubnetNetworkMap).when(routedIpv4Manager).createIpv4SubnetFromParentSubnet(subnetVO, cidrSize);
+
+ Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.getOrCreateIpv4SubnetForGuestNetworkOrVpcInternal(cidrSize, subnetVO);
+ Assert.assertEquals(ipv4GuestSubnetNetworkMap, result);
+ }
+
+ @Test
+ public void testGetParentOfNetworkCidr() {
+ when(subnetVO.getId()).thenReturn(zoneSubnetId);
+ when(subnetVO.getSubnet()).thenReturn(subnet);
+ when(dataCenterIpv4GuestSubnetDao.listByDataCenterId(zoneId)).thenReturn(Arrays.asList(subnetVO));
+ when(ipv4GuestSubnetNetworkMapDao.listByParent(zoneSubnetId)).thenReturn(Arrays.asList(ipv4GuestSubnetNetworkMap));
+ when(ipv4GuestSubnetNetworkMap.getSubnet()).thenReturn(subnetForNetwork);
+
+ try {
+ routedIpv4Manager.getParentOfNetworkCidr(zoneId, newSubnetForNetworkTooSmall);
+ Assert.fail("Getting parent of network cidr should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals(String.format("Existing subnet %s has overlap with: %s", subnetForNetwork, newSubnetForNetworkTooSmall), ex.getMessage());
+ }
+
+ try {
+ routedIpv4Manager.getParentOfNetworkCidr(zoneId, newSubnetForNetworkTooBig);
+ Assert.fail("Getting parent of network cidr should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals(String.format("Existing zone subnet %s has overlap with: %s", subnet, newSubnetForNetworkTooBig), ex.getMessage());
+ }
+
+ DataCenterIpv4GuestSubnet result = routedIpv4Manager.getParentOfNetworkCidr(zoneId, newSubnetForNetwork);
+ Assert.assertEquals(subnetVO, result);
+ }
+
+ @Test
+ public void testCreateIpv4SubnetFromParentSubnetByCidr() {
+ when(subnetVO.getSubnet()).thenReturn(subnet);
+ when(subnetVO.getId()).thenReturn(zoneSubnetId);
+
+ try {
+ routedIpv4Manager.createIpv4SubnetFromParentSubnet(subnetVO, newSubnetForNetworkTooBig);
+ Assert.fail("Creating ipv4 subnet should fail.");
+ } catch (CloudRuntimeException ex) {
+ Assert.assertEquals(String.format("networkCidr %s is not within parent cidr: %s", newSubnetForNetworkTooBig, subnet), ex.getMessage());
+ }
+
+ when(ipv4GuestSubnetNetworkMap.getSubnet()).thenReturn(subnetForNetwork);
+ when(ipv4GuestSubnetNetworkMapDao.listByParent(zoneSubnetId)).thenReturn(Arrays.asList(ipv4GuestSubnetNetworkMap));
+ when(ipv4GuestSubnetNetworkMapDao.persist(any())).thenReturn(ipv4GuestSubnetNetworkMap);
+
+ Ipv4GuestSubnetNetworkMap result = routedIpv4Manager.createIpv4SubnetFromParentSubnet(subnetVO, newSubnetForNetwork);
+ Assert.assertEquals(ipv4GuestSubnetNetworkMap, result);
+ }
+
+ @Test
+ public void testCreateIpv4SubnetFromParentSubnetByCidrSize() {
+ when(subnetVO.getId()).thenReturn(zoneSubnetId);
+ when(dataCenterIpv4GuestSubnetDao.findById(zoneSubnetId)).thenReturn(subnetVO);
+
+ Ipv4GuestSubnetNetworkMapVO ipv4GuestSubnetNetworkMap1 = new Ipv4GuestSubnetNetworkMapVO(zoneSubnetId, "172.16.1.0/28", null, Ipv4GuestSubnetNetworkMap.State.Free);
+ Ipv4GuestSubnetNetworkMapVO ipv4GuestSubnetNetworkMap2 = new Ipv4GuestSubnetNetworkMapVO(zoneSubnetId, "172.16.1.64/28", null, Ipv4GuestSubnetNetworkMap.State.Free);
+ when(ipv4GuestSubnetNetworkMapDao.listByParent(zoneSubnetId)).thenReturn(Arrays.asList(ipv4GuestSubnetNetworkMap1, ipv4GuestSubnetNetworkMap2));
+ when(subnetVO.getSubnet()).thenReturn("172.16.1.0/24");
+
+ String networkCidr = routedIpv4Manager.createIpv4SubnetStringFromParentSubnet(subnetVO, 28);
+ Assert.assertEquals("172.16.1.16/28", networkCidr);
+ networkCidr = routedIpv4Manager.createIpv4SubnetStringFromParentSubnet(subnetVO, 27);
+ Assert.assertEquals("172.16.1.32/27", networkCidr);
+ networkCidr = routedIpv4Manager.createIpv4SubnetStringFromParentSubnet(subnetVO, 26);
+ Assert.assertEquals("172.16.1.128/26", networkCidr);
+ networkCidr = routedIpv4Manager.createIpv4SubnetStringFromParentSubnet(subnetVO, 25);
+ Assert.assertEquals("172.16.1.128/25", networkCidr);
+
+ Ipv4GuestSubnetNetworkMapVO ipv4GuestSubnetNetworkMap3 = new Ipv4GuestSubnetNetworkMapVO(zoneSubnetId, "172.16.1.16/28", null, Ipv4GuestSubnetNetworkMap.State.Free);
+ Ipv4GuestSubnetNetworkMapVO ipv4GuestSubnetNetworkMap4 = new Ipv4GuestSubnetNetworkMapVO(zoneSubnetId, "172.16.1.128/28", null, Ipv4GuestSubnetNetworkMap.State.Free);
+ when(ipv4GuestSubnetNetworkMapDao.listByParent(zoneSubnetId)).thenReturn(Arrays.asList(ipv4GuestSubnetNetworkMap1, ipv4GuestSubnetNetworkMap2,
+ ipv4GuestSubnetNetworkMap3, ipv4GuestSubnetNetworkMap4));
+ networkCidr = routedIpv4Manager.createIpv4SubnetStringFromParentSubnet(subnetVO, 28);
+ Assert.assertEquals("172.16.1.80/28", networkCidr);
+ networkCidr = routedIpv4Manager.createIpv4SubnetStringFromParentSubnet(subnetVO, 27);
+ Assert.assertEquals("172.16.1.32/27", networkCidr);
+ networkCidr = routedIpv4Manager.createIpv4SubnetStringFromParentSubnet(subnetVO, 26);
+ Assert.assertEquals("172.16.1.192/26", networkCidr);
+ try {
+ networkCidr = routedIpv4Manager.createIpv4SubnetStringFromParentSubnet(subnetVO, 25);
+ Assert.fail("Creating ipv4 subnet should fail.");
+ } catch (CloudRuntimeException ex) {
+ Assert.assertEquals("Failed to automatically allocate a subnet with specified cidrsize", ex.getMessage());
+ }
+ }
+
+ @Test
+ public void testAssignIpv4SubnetToNetwork() {
+ when(network.getId()).thenReturn(networkId);
+ when(network.getCidr()).thenReturn(subnet);
+ when(ipv4GuestSubnetNetworkMapDao.findBySubnet(subnet)).thenReturn(ipv4GuestSubnetNetworkMap);
+ when(ipv4GuestSubnetNetworkMap.getId()).thenReturn(ipv4GuestSubnetNetworkMapId);
+
+ routedIpv4Manager.assignIpv4SubnetToNetwork(network);
+
+ verify(ipv4GuestSubnetNetworkMapDao).update(eq(ipv4GuestSubnetNetworkMapId), any());
+ }
+
+ @Test
+ public void testAssignIpv4SubnetToVpc() {
+ when(vpc.getId()).thenReturn(vpcId);
+ when(vpc.getCidr()).thenReturn(subnet);
+ when(ipv4GuestSubnetNetworkMapDao.findBySubnet(subnet)).thenReturn(ipv4GuestSubnetNetworkMap);
+ when(ipv4GuestSubnetNetworkMap.getId()).thenReturn(ipv4GuestSubnetNetworkMapId);
+
+ routedIpv4Manager.assignIpv4SubnetToVpc(vpc);
+
+ verify(ipv4GuestSubnetNetworkMapDao).update(eq(ipv4GuestSubnetNetworkMapId), any());
+ }
+
+ @Test
+ public void testCreateRoutingFirewallRule() throws NetworkRuleConflictException {
+ CreateRoutingFirewallRuleCmd cmd = new CreateRoutingFirewallRuleCmd();
+ ReflectionTestUtils.setField(cmd, "protocol", "tcp");
+ List sourceCidrList = Arrays.asList("192.168.0.0/24", "10.0.0.0/8");
+ ReflectionTestUtils.setField(cmd, "sourceCidrList", sourceCidrList);
+ List destinationCidrlist = Arrays.asList("192.168.0.0/24", "10.0.0.0/8");
+ ReflectionTestUtils.setField(cmd, "destinationCidrlist", destinationCidrlist);
+ ReflectionTestUtils.setField(cmd, "publicStartPort", 1111);
+ ReflectionTestUtils.setField(cmd, "publicEndPort", 2222);
+ ReflectionTestUtils.setField(cmd, "networkId", networkId);
+
+ when(networkModel.getNetwork(networkId)).thenReturn(network);
+ when(network.getId()).thenReturn(networkId);
+ when(network.getDomainId()).thenReturn(domainId);
+ when(network.getAccountId()).thenReturn(accountId);
+ VirtualRouterElement virtualRouterElement = new VirtualRouterElement();
+ when(networkModel.getNetworkServiceCapabilities(networkId, Network.Service.Firewall)).thenReturn(virtualRouterElement.getCapabilities().get(Network.Service.Firewall));
+ when(firewallDao.persist(any())).thenReturn(rule);
+ when(firewallDao.setStateToAdd(any())).thenReturn(true);
+
+ // create Ingress rule
+ ReflectionTestUtils.setField(cmd, "trafficType", "ingress");
+ FirewallRule result = routedIpv4Manager.createRoutingFirewallRule(cmd);
+ Assert.assertNotNull(result);
+ Assert.assertEquals(rule, result);
+
+ // create Egress rule
+ ReflectionTestUtils.setField(cmd, "trafficType", "egress");
+ result = routedIpv4Manager.createRoutingFirewallRule(cmd);
+ Assert.assertNotNull(result);
+ Assert.assertEquals(rule, result);
+ }
+
+ @Test
+ public void testUpdateRoutingFirewallRule1() {
+ UpdateRoutingFirewallRuleCmd cmd = new UpdateRoutingFirewallRuleCmd();
+ ReflectionTestUtils.setField(cmd, "id", ruleId);
+
+ when(firewallDao.findById(ruleId)).thenReturn(rule);
+ when(rule.getId()).thenReturn(ruleId);
+ when(rule.getTrafficType()).thenReturn(FirewallRule.TrafficType.Ingress);
+
+ routedIpv4Manager.updateRoutingFirewallRule(cmd);
+
+ verify(firewallManager).updateIngressFirewallRule(ruleId, null, true);
+ }
+
+ @Test
+ public void testUpdateRoutingFirewallRule2() {
+ UpdateRoutingFirewallRuleCmd cmd = new UpdateRoutingFirewallRuleCmd();
+ ReflectionTestUtils.setField(cmd, "id", ruleId);
+
+ when(firewallDao.findById(ruleId)).thenReturn(rule);
+ when(rule.getId()).thenReturn(ruleId);
+ when(rule.getTrafficType()).thenReturn(FirewallRule.TrafficType.Egress);
+
+ routedIpv4Manager.updateRoutingFirewallRule(cmd);
+
+ verify(firewallManager).updateEgressFirewallRule(ruleId, null, true);
+ }
+
+ @Test
+ public void testRevokeRoutingFirewallRule1() {
+ when(firewallDao.findById(ruleId)).thenReturn(rule);
+ when(rule.getId()).thenReturn(ruleId);
+ when(rule.getTrafficType()).thenReturn(FirewallRule.TrafficType.Ingress);
+
+ routedIpv4Manager.revokeRoutingFirewallRule(ruleId);
+
+ verify(firewallManager).revokeIngressFirewallRule(ruleId, true);
+ }
+
+ @Test
+ public void testRevokeRoutingFirewallRule2() {
+ when(firewallDao.findById(ruleId)).thenReturn(rule);
+ when(rule.getId()).thenReturn(ruleId);
+ when(rule.getTrafficType()).thenReturn(FirewallRule.TrafficType.Egress);
+
+ routedIpv4Manager.revokeRoutingFirewallRule(ruleId);
+
+ verify(firewallManager).revokeEgressFirewallRule(ruleId, true);
+ }
+
+ @Test
+ public void testApplyRoutingFirewallRule1() {
+ when(firewallDao.findById(ruleId)).thenReturn(rule);
+ when(rule.getPurpose()).thenReturn(FirewallRule.Purpose.Firewall);
+ when(rule.getNetworkId()).thenReturn(networkId);
+
+ FirewallRuleVO rule1 = Mockito.mock(FirewallRuleVO.class);
+ when(firewallDao.listByNetworkPurposeTrafficType(networkId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Egress)).thenReturn(Arrays.asList(rule1));
+ FirewallRuleVO rule2 = Mockito.mock(FirewallRuleVO.class);
+ when(firewallDao.listByNetworkPurposeTrafficType(networkId, FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Ingress)).thenReturn(Arrays.asList(rule2));
+ when(firewallManager.applyFirewallRules(any(), eq(false), any())).thenReturn(true);
+
+ boolean result = routedIpv4Manager.applyRoutingFirewallRule(ruleId);
+ Assert.assertTrue(result);
+ }
+
+ @Test
+ public void testApplyRoutingFirewallRule2() {
+ when(firewallDao.findById(ruleId)).thenReturn(rule);
+ when(rule.getPurpose()).thenReturn(FirewallRule.Purpose.LoadBalancing);
+
+ boolean result = routedIpv4Manager.applyRoutingFirewallRule(ruleId);
+ Assert.assertFalse(result);
+ }
+
+ @Test
+ public void testCreateBgpPeer() {
+ CreateBgpPeerCmd cmd = new CreateBgpPeerCmd();
+ ReflectionTestUtils.setField(cmd, "zoneId", zoneId);
+ ReflectionTestUtils.setField(cmd, "ip4Address", ip4Address);
+ ReflectionTestUtils.setField(cmd, "ip6Address", ip6Address);
+ ReflectionTestUtils.setField(cmd, "asNumber", asNumber);
+ ReflectionTestUtils.setField(cmd, "password", password);
+
+ when(bgpPeerDao.findByZoneAndAsNumberAndAddress(zoneId, asNumber, ip4Address, null)).thenReturn(null);
+ when(bgpPeerDao.findByZoneAndAsNumberAndAddress(zoneId, asNumber, null, ip6Address)).thenReturn(null);
+
+ routedIpv4Manager.createBgpPeer(cmd);
+
+ verify(bgpPeerDao).persist(any(BgpPeerVO.class), any(Map.class));
+ }
+
+ @Test
+ public void testCreateBgpPeerResponse() {
+ BgpPeerVO bgpPeer = Mockito.mock(BgpPeerVO.class);
+ when(bgpPeer.getDataCenterId()).thenReturn(zoneId);
+ when(bgpPeer.getDomainId()).thenReturn(domainId);
+ when(bgpPeer.getAccountId()).thenReturn(accountId);
+ when(bgpPeer.getCreated()).thenReturn(created);
+ when(bgpPeer.getAsNumber()).thenReturn(asNumber);
+ when(bgpPeer.getUuid()).thenReturn(bgpPeerUuid);
+ when(bgpPeer.getIp4Address()).thenReturn(ip4Address);
+ when(bgpPeer.getIp6Address()).thenReturn(ip6Address);
+
+ apiDBUtilsMocked.when(() -> ApiDBUtils.findZoneById(zoneId)).thenReturn(zone);
+ when(zone.getName()).thenReturn(zoneName);
+ when(zone.getUuid()).thenReturn(zoneUuid);
+
+ apiDBUtilsMocked.when(() -> ApiDBUtils.findDomainById(domainId)).thenReturn(domain);
+ when(domain.getName()).thenReturn(domainName);
+ when(domain.getUuid()).thenReturn(domainUuid);
+
+ apiDBUtilsMocked.when(() -> ApiDBUtils.findAccountById(accountId)).thenReturn(account);
+ when(account.getType()).thenReturn(Account.Type.PROJECT);
+ when(account.getId()).thenReturn(accountId);
+
+ apiDBUtilsMocked.when(() -> ApiDBUtils.findProjectByProjectAccountId(accountId)).thenReturn(project);
+ when(project.getName()).thenReturn(projectName);
+ when(project.getUuid()).thenReturn(projectUuid);
+
+ BgpPeerResponse response = routedIpv4Manager.createBgpPeerResponse(bgpPeer);
+
+ Assert.assertEquals(created, response.getCreated());
+ Assert.assertEquals(asNumber, response.getAsNumber());
+ Assert.assertEquals(ip4Address, response.getIp4Address());
+ Assert.assertEquals(ip6Address, response.getIp6Address());
+ Assert.assertEquals(bgpPeerUuid, response.getId());
+ Assert.assertEquals(zoneUuid, response.getZoneId());
+ Assert.assertEquals(zoneName, response.getZoneName());
+ Assert.assertEquals(domainUuid, response.getDomainId());
+ Assert.assertEquals(domainName, response.getDomainName());
+ Assert.assertEquals(projectName, response.getProjectName());
+ Assert.assertEquals(projectUuid, response.getProjectId());
+ Assert.assertNull(response.getAccountName());
+
+ Assert.assertEquals("bgppeer", response.getObjectName());
+
+ }
+
+ @Test
+ public void testDeleteBgpPeer1() {
+ DeleteBgpPeerCmd cmd = new DeleteBgpPeerCmd();
+ ReflectionTestUtils.setField(cmd, "id", bgpPeerId);
+
+ BgpPeerNetworkMapVO bgpPeerNetworkMapVO = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMapDao.listByBgpPeerId(bgpPeerId)).thenReturn(Arrays.asList(bgpPeerNetworkMapVO));
+
+ try {
+ routedIpv4Manager.deleteBgpPeer(cmd);
+ Assert.fail("Deleting BGP peer should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals("The BGP peer is being used by 1 guest networks.", ex.getMessage());
+ }
+ }
+
+ @Test
+ public void testDeleteBgpPeer2() {
+ DeleteBgpPeerCmd cmd = new DeleteBgpPeerCmd();
+ ReflectionTestUtils.setField(cmd, "id", bgpPeerId);
+
+ routedIpv4Manager.deleteBgpPeer(cmd);
+
+ verify(bgpPeerDao).remove(bgpPeerId);
+ }
+
+ @Test
+ public void testUpdateBgpPeer() {
+ UpdateBgpPeerCmd cmd = new UpdateBgpPeerCmd();
+ ReflectionTestUtils.setField(cmd, "id", bgpPeerId);
+ ReflectionTestUtils.setField(cmd, "asNumber", asNumber+1);
+ ReflectionTestUtils.setField(cmd, "ip4Address", ip4Address + "1");
+ ReflectionTestUtils.setField(cmd, "ip6Address", ip6Address + "1");
+
+ when(bgpPeerDao.findById(bgpPeerId)).thenReturn(bgpPeer);
+ when(bgpPeer.getDataCenterId()).thenReturn(zoneId);
+
+ routedIpv4Manager.updateBgpPeer(cmd);
+
+ verify(bgpPeerDao).update(bgpPeerId, bgpPeer);
+ verify(bgpPeerDao).findByZoneAndAsNumberAndAddress(zoneId, asNumber+1, ip4Address + "1", null);
+ verify(bgpPeerDao).findByZoneAndAsNumberAndAddress(zoneId, asNumber+1, null, ip6Address + "1");
+ }
+
+ @Test
+ public void testDedicateBgpPeer() {
+ DedicateBgpPeerCmd cmd = new DedicateBgpPeerCmd();
+ ReflectionTestUtils.setField(cmd,"id", bgpPeerId);
+ ReflectionTestUtils.setField(cmd,"domainId", domainId);
+ ReflectionTestUtils.setField(cmd,"accountName", accountName);
+ ReflectionTestUtils.setField(cmd,"projectId", null);
+
+ when(bgpPeerDao.findById(bgpPeerId)).thenReturn(bgpPeer);
+ when(accountManager.finalyzeAccountId(accountName, domainId, null, false)).thenReturn(accountId);
+ when(accountManager.getAccount(accountId)).thenReturn(account);
+ when(account.getDomainId()).thenReturn(domainId);
+
+ routedIpv4Manager.dedicateBgpPeer(cmd);
+
+ verify(bgpPeerDao).update(bgpPeerId, bgpPeer);
+ verify(bgpPeerNetworkMapDao).listUsedNetworksByOtherDomains(bgpPeerId, domainId);
+ verify(bgpPeerNetworkMapDao).listUsedVpcsByOtherDomains(bgpPeerId, domainId);
+ verify(bgpPeerNetworkMapDao).listUsedNetworksByOtherAccounts(bgpPeerId, accountId);
+ verify(bgpPeerNetworkMapDao).listUsedVpcsByOtherAccounts(bgpPeerId, accountId);
+ }
+
+ @Test
+ public void testReleaseDedicatedBgpPeer() {
+ ReleaseDedicatedBgpPeerCmd cmd = new ReleaseDedicatedBgpPeerCmd();
+ ReflectionTestUtils.setField(cmd,"id", bgpPeerId);
+ when(bgpPeerDao.findById(bgpPeerId)).thenReturn(bgpPeer);
+
+ routedIpv4Manager.releaseDedicatedBgpPeer(cmd);
+
+ verify(bgpPeer).setDomainId(null);
+ verify(bgpPeer).setAccountId(null);
+ verify(bgpPeerDao).update(bgpPeerId, bgpPeer);
+ }
+
+ @Test
+ public void testChangeBgpPeersForNetwork() {
+ ChangeBgpPeersForNetworkCmd cmd = new ChangeBgpPeersForNetworkCmd();
+ ReflectionTestUtils.setField(cmd,"networkId", networkId);
+ List bgpPeerIds = Arrays.asList(bgpPeerId);
+ ReflectionTestUtils.setField(cmd,"bgpPeerIds", bgpPeerIds);
+
+ when(networkDao.findById(networkId)).thenReturn(network);
+
+ try {
+ when(network.getVpcId()).thenReturn(vpcId);
+ routedIpv4Manager.changeBgpPeersForNetwork(cmd);
+ Assert.fail("Changing BGP peers for guest network should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals("The BGP peers of VPC tiers will inherit from the VPC, do not add separately.", ex.getMessage());
+ }
+
+ when(network.getVpcId()).thenReturn(null);
+ when(network.getAccountId()).thenReturn(accountId);
+ when(accountManager.getAccount(accountId)).thenReturn(account);
+ when(network.getNetworkOfferingId()).thenReturn(networkOfferingId);
+ when(networkOfferingDao.findById(networkOfferingId)).thenReturn(networkOffering);
+ when(network.getDataCenterId()).thenReturn(zoneId);
+
+ try {
+ when(networkOffering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED);
+ when(networkOffering.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Static);
+ routedIpv4Manager.changeBgpPeersForNetwork(cmd);
+ Assert.fail("Changing BGP peers for guest network should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals("The network does not support Dynamic routing", ex.getMessage());
+ }
+
+ when(networkOffering.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Dynamic);
+
+ doNothing().when(routedIpv4Manager).validateBgpPeers(account, zoneId, bgpPeerIds);
+ doReturn(network).when(routedIpv4Manager).changeBgpPeersForNetworkInternal(network, bgpPeerIds);
+
+ routedIpv4Manager.changeBgpPeersForNetwork(cmd);
+
+ verify(routedIpv4Manager).validateBgpPeers(account, zoneId, bgpPeerIds);
+ verify(routedIpv4Manager).changeBgpPeersForNetworkInternal(network, bgpPeerIds);
+ }
+
+ @Test
+ public void testChangeBgpPeersForNetworkInternal() throws ResourceUnavailableException {
+ Long bgpPeerId1 = 11L; // to be kept
+ Long bgpPeerId2 = 12L; // to be removed
+ Long bgpPeerId3 = 13L; // to be added
+ Long bgpPeerNetworkMapId2 = 14L; // to be removed
+
+ when(network.getId()).thenReturn(networkId);
+ when(networkDao.findById(networkId)).thenReturn(network);
+
+ BgpPeerNetworkMapVO bgpPeerNetworkMap1 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap1.getBgpPeerId()).thenReturn(bgpPeerId1);
+ when(bgpPeerNetworkMap1.getState()).thenReturn(BgpPeer.State.Active);
+ BgpPeerNetworkMapVO bgpPeerNetworkMap2 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap2.getBgpPeerId()).thenReturn(bgpPeerId2);
+ when(bgpPeerNetworkMap2.getState()).thenReturn(BgpPeer.State.Revoke);
+ BgpPeerNetworkMapVO bgpPeerNetworkMap3 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap3.getState()).thenReturn(BgpPeer.State.Add);
+
+ when(bgpPeerNetworkMap2.getId()).thenReturn(bgpPeerNetworkMapId2);
+
+ when(bgpPeerNetworkMapDao.listByNetworkId(networkId)).thenReturn(Arrays.asList(bgpPeerNetworkMap1, bgpPeerNetworkMap2))
+ .thenReturn(Arrays.asList(bgpPeerNetworkMap1, bgpPeerNetworkMap2, bgpPeerNetworkMap3));
+ when(bgpService.applyBgpPeers(network, false)).thenReturn(true);
+
+ Network result = routedIpv4Manager.changeBgpPeersForNetworkInternal(network, Arrays.asList(bgpPeerId1, bgpPeerId3));
+ Assert.assertEquals(network, result);
+
+ verify(bgpPeerNetworkMap2).setState(BgpPeer.State.Revoke);
+ verify(bgpPeerNetworkMapDao).persist(any());
+ verify(bgpPeerNetworkMap3).setState(BgpPeer.State.Active);
+ verify(bgpPeerNetworkMapDao).remove(bgpPeerNetworkMapId2);
+ }
+
+ @Test
+ public void testChangeBgpPeersForNetworkInternalFailure() throws ResourceUnavailableException {
+ Long bgpPeerId1 = 11L; // to be kept
+ Long bgpPeerId2 = 12L; // to be removed, but finally not
+ Long bgpPeerId3 = 13L; // to be added, but finally not
+ Long bgpPeerNetworkMapId3 = 15L; // to be added, but finally not
+
+ when(network.getId()).thenReturn(networkId);
+
+ BgpPeerNetworkMapVO bgpPeerNetworkMap1 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap1.getBgpPeerId()).thenReturn(bgpPeerId1);
+ when(bgpPeerNetworkMap1.getState()).thenReturn(BgpPeer.State.Active);
+ BgpPeerNetworkMapVO bgpPeerNetworkMap2 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap2.getBgpPeerId()).thenReturn(bgpPeerId2);
+ when(bgpPeerNetworkMap2.getState()).thenReturn(BgpPeer.State.Revoke);
+ BgpPeerNetworkMapVO bgpPeerNetworkMap3 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap3.getState()).thenReturn(BgpPeer.State.Add);
+
+ when(bgpPeerNetworkMap3.getId()).thenReturn(bgpPeerNetworkMapId3);
+
+ when(bgpPeerNetworkMapDao.listByNetworkId(networkId)).thenReturn(Arrays.asList(bgpPeerNetworkMap1, bgpPeerNetworkMap2))
+ .thenReturn(Arrays.asList(bgpPeerNetworkMap1, bgpPeerNetworkMap2, bgpPeerNetworkMap3));
+ when(bgpService.applyBgpPeers(network, false)).thenReturn(false);
+
+ Network result = routedIpv4Manager.changeBgpPeersForNetworkInternal(network, Arrays.asList(bgpPeerId1, bgpPeerId3));
+ Assert.assertNull(result);
+
+ verify(bgpPeerNetworkMap2).setState(BgpPeer.State.Revoke);
+ verify(bgpPeerNetworkMapDao).persist(any());
+ verify(bgpPeerNetworkMap2).setState(BgpPeer.State.Add);
+ verify(bgpPeerNetworkMapDao).remove(bgpPeerNetworkMapId3);
+ }
+
+ @Test
+ public void testValidateBgpPeers() {
+ List bgpPeerIds = Arrays.asList(bgpPeerId);
+ try {
+ routedIpv4Manager.validateBgpPeers(account, zoneId, bgpPeerIds);
+ Assert.fail("Validating BGP peers for guest network should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals(String.format("Invalid BGP peer ID: %s", bgpPeerId), ex.getMessage());
+ }
+
+ when(account.getDomainId()).thenReturn(domainId);
+ when(account.getAccountId()).thenReturn(accountId);
+ when(bgpPeerDao.findById(bgpPeerId)).thenReturn(bgpPeer);
+ when(bgpPeer.getUuid()).thenReturn(bgpPeerUuid);
+ try {
+ when(bgpPeer.getDataCenterId()).thenReturn(zoneId + 1);
+ routedIpv4Manager.validateBgpPeers(account, zoneId, bgpPeerIds);
+ Assert.fail("Validating BGP peers for guest network should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals(String.format("BGP peer (ID: %s) belongs to a different zone", bgpPeerUuid), ex.getMessage());
+ }
+
+ when(bgpPeer.getDataCenterId()).thenReturn(zoneId);
+ try {
+ when(bgpPeer.getDomainId()).thenReturn(domainId + 1);
+ routedIpv4Manager.validateBgpPeers(account, zoneId, bgpPeerIds);
+ Assert.fail("Validating BGP peers for guest network should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals(String.format("BGP peer (ID: %s) belongs to a different domain", bgpPeerUuid), ex.getMessage());
+ }
+
+ when(bgpPeer.getDomainId()).thenReturn(domainId);
+ try {
+ when(bgpPeer.getAccountId()).thenReturn(accountId + 1);
+ routedIpv4Manager.validateBgpPeers(account, zoneId, bgpPeerIds);
+ Assert.fail("Validating BGP peers for guest network should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals(String.format("BGP peer (ID: %s) belongs to a different account", bgpPeerUuid), ex.getMessage());
+ }
+
+ when(bgpPeer.getAccountId()).thenReturn(accountId);
+ routedIpv4Manager.validateBgpPeers(account, zoneId, bgpPeerIds);
+ }
+
+ @Test
+ public void testChangeBgpPeersForVpc() {
+ ChangeBgpPeersForVpcCmd cmd = new ChangeBgpPeersForVpcCmd();
+ ReflectionTestUtils.setField(cmd,"vpcId", vpcId);
+ List bgpPeerIds = Arrays.asList(bgpPeerId);
+ ReflectionTestUtils.setField(cmd,"bgpPeerIds", bgpPeerIds);
+
+ when(vpcDao.findById(vpcId)).thenReturn(vpc);
+ when(vpc.getAccountId()).thenReturn(accountId);
+ when(accountManager.getAccount(accountId)).thenReturn(account);
+ when(vpc.getVpcOfferingId()).thenReturn(vpcOfferingId);
+ when(vpcOfferingDao.findById(vpcOfferingId)).thenReturn(vpcOffering);
+ when(vpc.getZoneId()).thenReturn(zoneId);
+
+ try {
+ when(vpcOffering.getNetworkMode()).thenReturn(NetworkOffering.NetworkMode.ROUTED);
+ when(vpcOffering.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Static);
+ routedIpv4Manager.changeBgpPeersForVpc(cmd);
+ Assert.fail("Changing BGP peers for VPC should fail.");
+ } catch (InvalidParameterValueException ex) {
+ Assert.assertEquals("The VPC does not support Dynamic routing", ex.getMessage());
+ }
+
+ when(vpcOffering.getRoutingMode()).thenReturn(NetworkOffering.RoutingMode.Dynamic);
+
+ doNothing().when(routedIpv4Manager).validateBgpPeers(account, zoneId, bgpPeerIds);
+ doReturn(vpc).when(routedIpv4Manager).changeBgpPeersForVpcInternal(vpc, bgpPeerIds);
+
+ routedIpv4Manager.changeBgpPeersForVpc(cmd);
+
+ verify(routedIpv4Manager).validateBgpPeers(account, zoneId, bgpPeerIds);
+ verify(routedIpv4Manager).changeBgpPeersForVpcInternal(vpc, bgpPeerIds);
+ }
+
+ @Test
+ public void testChangeBgpPeersForVpcInternal() throws ResourceUnavailableException {
+ Long bgpPeerId1 = 11L; // to be kept
+ Long bgpPeerId2 = 12L; // to be removed
+ Long bgpPeerId3 = 13L; // to be added
+ Long bgpPeerNetworkMapId2 = 14L; // to be removed
+
+ when(vpc.getId()).thenReturn(vpcId);
+ when(vpcDao.findById(vpcId)).thenReturn(vpc);
+
+ BgpPeerNetworkMapVO bgpPeerNetworkMap1 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap1.getBgpPeerId()).thenReturn(bgpPeerId1);
+ when(bgpPeerNetworkMap1.getState()).thenReturn(BgpPeer.State.Active);
+ BgpPeerNetworkMapVO bgpPeerNetworkMap2 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap2.getBgpPeerId()).thenReturn(bgpPeerId2);
+ when(bgpPeerNetworkMap2.getState()).thenReturn(BgpPeer.State.Revoke);
+ BgpPeerNetworkMapVO bgpPeerNetworkMap3 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap3.getState()).thenReturn(BgpPeer.State.Add);
+
+ when(bgpPeerNetworkMap2.getId()).thenReturn(bgpPeerNetworkMapId2);
+
+ when(bgpPeerNetworkMapDao.listByVpcId(vpcId)).thenReturn(Arrays.asList(bgpPeerNetworkMap1, bgpPeerNetworkMap2))
+ .thenReturn(Arrays.asList(bgpPeerNetworkMap1, bgpPeerNetworkMap2, bgpPeerNetworkMap3));
+ when(bgpService.applyBgpPeers(vpc, false)).thenReturn(true);
+
+ Vpc result = routedIpv4Manager.changeBgpPeersForVpcInternal(vpc, Arrays.asList(bgpPeerId1, bgpPeerId3));
+ Assert.assertEquals(vpc, result);
+
+ verify(bgpPeerNetworkMap2).setState(BgpPeer.State.Revoke);
+ verify(bgpPeerNetworkMapDao).persist(any());
+ verify(bgpPeerNetworkMap3).setState(BgpPeer.State.Active);
+ verify(bgpPeerNetworkMapDao).remove(bgpPeerNetworkMapId2);
+ }
+
+ @Test
+ public void testChangeBgpPeersForVpcInternalFailure() throws ResourceUnavailableException {
+ Long bgpPeerId1 = 11L; // to be kept
+ Long bgpPeerId2 = 12L; // to be removed, but finally not
+ Long bgpPeerId3 = 13L; // to be added, but finally not
+ Long bgpPeerNetworkMapId3 = 15L; // to be added, but finally not
+
+ when(vpc.getId()).thenReturn(vpcId);
+
+ BgpPeerNetworkMapVO bgpPeerNetworkMap1 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap1.getBgpPeerId()).thenReturn(bgpPeerId1);
+ when(bgpPeerNetworkMap1.getState()).thenReturn(BgpPeer.State.Active);
+ BgpPeerNetworkMapVO bgpPeerNetworkMap2 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap2.getBgpPeerId()).thenReturn(bgpPeerId2);
+ when(bgpPeerNetworkMap2.getState()).thenReturn(BgpPeer.State.Revoke);
+ BgpPeerNetworkMapVO bgpPeerNetworkMap3 = Mockito.mock(BgpPeerNetworkMapVO.class);
+ when(bgpPeerNetworkMap3.getState()).thenReturn(BgpPeer.State.Add);
+
+ when(bgpPeerNetworkMap3.getId()).thenReturn(bgpPeerNetworkMapId3);
+
+ when(bgpPeerNetworkMapDao.listByVpcId(vpcId)).thenReturn(Arrays.asList(bgpPeerNetworkMap1, bgpPeerNetworkMap2))
+ .thenReturn(Arrays.asList(bgpPeerNetworkMap1, bgpPeerNetworkMap2, bgpPeerNetworkMap3));
+ when(bgpService.applyBgpPeers(vpc, false)).thenReturn(false);
+
+ Vpc result = routedIpv4Manager.changeBgpPeersForVpcInternal(vpc, Arrays.asList(bgpPeerId1, bgpPeerId3));
+ Assert.assertNull(result);
+
+ verify(bgpPeerNetworkMap2).setState(BgpPeer.State.Revoke);
+ verify(bgpPeerNetworkMapDao).persist(any());
+ verify(bgpPeerNetworkMap2).setState(BgpPeer.State.Add);
+ verify(bgpPeerNetworkMapDao).remove(bgpPeerNetworkMapId3);
+ }
+
+ @Test
+ public void testRemoveIpv4SubnetsForZoneByAccountId() {
+ when(dataCenterIpv4GuestSubnetDao.listByAccountId(accountId)).thenReturn(Arrays.asList(subnetVO));
+ when(subnetVO.getId()).thenReturn(zoneSubnetId);
+
+ routedIpv4Manager.removeIpv4SubnetsForZoneByAccountId(accountId);
+
+ verify(ipv4GuestSubnetNetworkMapDao).deleteByParentId(zoneSubnetId);
+ verify(dataCenterIpv4GuestSubnetDao).remove(zoneSubnetId);
+ }
+
+ @Test
+ public void testRemoveIpv4SubnetsForZoneByDomainId() {
+ when(dataCenterIpv4GuestSubnetDao.listByDomainId(domainId)).thenReturn(Arrays.asList(subnetVO));
+ when(subnetVO.getId()).thenReturn(zoneSubnetId);
+
+ routedIpv4Manager.removeIpv4SubnetsForZoneByDomainId(domainId);
+
+ verify(ipv4GuestSubnetNetworkMapDao).deleteByParentId(zoneSubnetId);
+ verify(dataCenterIpv4GuestSubnetDao).remove(zoneSubnetId);
+ }
+}
diff --git a/ui/src/config/section/infra/zones.js b/ui/src/config/section/infra/zones.js
index 25535979e4d..b4a34372e10 100644
--- a/ui/src/config/section/infra/zones.js
+++ b/ui/src/config/section/infra/zones.js
@@ -74,13 +74,16 @@ export default {
component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/PhysicalNetworksTab.vue')))
}, {
name: 'ipv4.subnets',
- component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/Ipv4GuestSubnetsTab.vue')))
+ component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/Ipv4GuestSubnetsTab.vue'))),
+ show: (record) => { return record.routedmodeenabled && 'listIpv4SubnetsForZone' in store.getters.apis }
}, {
name: 'asnumber',
- component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/AsNumbersTab.vue')))
+ component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/AsNumbersTab.vue'))),
+ show: (record) => { return record.routedmodeenabled && 'listASNumbers' in store.getters.apis }
}, {
name: 'bgp.peers',
- component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/BgpPeersTab.vue')))
+ component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/BgpPeersTab.vue'))),
+ show: (record) => { return record.routedmodeenabled && 'listBgpPeers' in store.getters.apis }
}, {
name: 'system.vms',
component: shallowRef(defineAsyncComponent(() => import('@/views/infra/zone/SystemVmsTab.vue'))),
diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js
index 79eae34f537..3bb2c75eabf 100644
--- a/ui/src/config/section/network.js
+++ b/ui/src/config/section/network.js
@@ -872,7 +872,14 @@ export default {
icon: 'partition-outlined',
permission: ['listASNumbers'],
show: () => {
- return ['Admin'].includes(store.getters.userInfo.roletype)
+ if (!store.getters.zones || store.getters.zones.length === 0) {
+ return false
+ }
+ const AdvancedZonesWithRoutedmode = store.getters.zones.filter(zone => zone.routedmodeenabled)
+ if (isAdmin() && (AdvancedZonesWithRoutedmode && AdvancedZonesWithRoutedmode.length > 0)) {
+ return true
+ }
+ return false
},
filters: ['all', 'allocatedonly', 'free'],
columns: ['asnumber', 'allocationstate', 'asnrange', 'associatednetworkname', 'vpcname', 'allocated', 'account', 'domain', 'zonename'],
@@ -1446,7 +1453,11 @@ export default {
if (!store.getters.zones || store.getters.zones.length === 0) {
return false
}
- return isAdmin()
+ const AdvancedZonesWithRoutedmode = store.getters.zones.filter(zone => zone.routedmodeenabled)
+ if (isAdmin() && (AdvancedZonesWithRoutedmode && AdvancedZonesWithRoutedmode.length > 0)) {
+ return true
+ }
+ return false
},
actions: [
{
diff --git a/ui/src/views/network/CreateIpv4SubnetForNetwork.vue b/ui/src/views/network/CreateIpv4SubnetForNetwork.vue
index 44af8246066..af1ef8c0e25 100644
--- a/ui/src/views/network/CreateIpv4SubnetForNetwork.vue
+++ b/ui/src/views/network/CreateIpv4SubnetForNetwork.vue
@@ -150,6 +150,7 @@ export default {
const params = { showicon: true }
api('listZones', params).then(json => {
this.zones = json.listzonesresponse.zone || []
+ this.zones = this.zones.filter(zone => zone.routedmodeenabled)
this.form.zoneid = this.zones[0].id || ''
this.fetchParentSubnets(this.form.zoneid)
}).finally(() => {
diff --git a/ui/src/views/network/CreateIsolatedNetworkForm.vue b/ui/src/views/network/CreateIsolatedNetworkForm.vue
index b60223219e7..2c3fa17fa35 100644
--- a/ui/src/views/network/CreateIsolatedNetworkForm.vue
+++ b/ui/src/views/network/CreateIsolatedNetworkForm.vue
@@ -552,8 +552,9 @@ export default {
this.selectedNetworkOffering = {}
api('listNetworkOfferings', params).then(json => {
this.networkOfferings = json.listnetworkofferingsresponse.networkoffering
- if (this.selectedZone.isnsxenabled) {
- this.networkOfferings = this.networkOfferings.filter(offering => offering.fornsx)
+ this.networkOfferings = this.networkOfferings.filter(offering => offering.fornsx === this.selectedZone.isnsxenabled)
+ if (!this.selectedZone.routedmodeenabled) {
+ this.networkOfferings = this.networkOfferings.filter(offering => offering.networkmode !== 'ROUTED')
}
}).catch(error => {
this.$notifyError(error)
diff --git a/ui/src/views/network/CreateVpc.vue b/ui/src/views/network/CreateVpc.vue
index b0a56ca724a..6fb9a712262 100644
--- a/ui/src/views/network/CreateVpc.vue
+++ b/ui/src/views/network/CreateVpc.vue
@@ -345,6 +345,10 @@ export default {
this.loadingOffering = true
api('listVPCOfferings', { zoneid: this.form.zoneid, state: 'Enabled' }).then((response) => {
this.vpcOfferings = response.listvpcofferingsresponse.vpcoffering
+ this.vpcOfferings = this.vpcOfferings.filter(offering => offering.fornsx === this.selectedZone.isnsxenabled)
+ if (!this.selectedZone.routedmodeenabled) {
+ this.vpcOfferings = this.vpcOfferings.filter(offering => offering.networkmode !== 'ROUTED')
+ }
this.form.vpcofferingid = this.vpcOfferings[0].id || ''
this.selectedVpcOffering = this.vpcOfferings[0] || {}
}).finally(() => {
diff --git a/ui/src/views/offering/AddNetworkOffering.vue b/ui/src/views/offering/AddNetworkOffering.vue
index 66d7a4a675e..f637823812e 100644
--- a/ui/src/views/offering/AddNetworkOffering.vue
+++ b/ui/src/views/offering/AddNetworkOffering.vue
@@ -615,6 +615,7 @@ export default {
zones: [],
zoneLoading: false,
ipv6NetworkOfferingEnabled: false,
+ routedNetworkEnabled: false,
loading: false,
networkmode: '',
networkmodes: [
@@ -704,6 +705,7 @@ export default {
this.fetchSupportedServiceData()
this.fetchServiceOfferingData()
this.fetchIpv6NetworkOfferingConfiguration()
+ this.fetchRoutedNetworkConfiguration()
},
isAdmin () {
return isAdmin()
@@ -732,6 +734,17 @@ export default {
this.ipv6NetworkOfferingEnabled = value === 'true'
})
},
+ fetchRoutedNetworkConfiguration () {
+ this.routedNetworkEnabled = false
+ var params = { name: 'routed.network.vpc.enabled' }
+ api('listConfigurations', params).then(json => {
+ var value = json?.listconfigurationsresponse?.configuration?.[0].value || null
+ this.routedNetworkEnabled = value === 'true'
+ if (!this.routedNetworkEnabled) {
+ this.networkmodes.pop()
+ }
+ })
+ },
fetchZoneData () {
const params = {}
params.showicon = true
diff --git a/ui/src/views/offering/AddVpcOffering.vue b/ui/src/views/offering/AddVpcOffering.vue
index c914467a088..450ee117715 100644
--- a/ui/src/views/offering/AddVpcOffering.vue
+++ b/ui/src/views/offering/AddVpcOffering.vue
@@ -283,6 +283,7 @@ export default {
sourceNatServiceChecked: false,
selectedServiceProviderMap: {},
ipv6NetworkOfferingEnabled: false,
+ routedNetworkEnabled: false,
routingMode: 'static',
networkmode: '',
networkmodes: [
@@ -351,6 +352,7 @@ export default {
this.fetchZoneData()
this.fetchSupportedServiceData()
this.fetchIpv6NetworkOfferingConfiguration()
+ this.fetchRoutedNetworkConfiguration()
},
isAdmin () {
return isAdmin()
@@ -363,6 +365,17 @@ export default {
this.ipv6NetworkOfferingEnabled = value === 'true'
})
},
+ fetchRoutedNetworkConfiguration () {
+ this.routedNetworkEnabled = false
+ var params = { name: 'routed.network.vpc.enabled' }
+ api('listConfigurations', params).then(json => {
+ var value = json?.listconfigurationsresponse?.configuration?.[0].value || null
+ this.routedNetworkEnabled = value === 'true'
+ if (!this.routedNetworkEnabled) {
+ this.networkmodes.pop()
+ }
+ })
+ },
fetchDomainData () {
const params = {}
params.listAll = true
From 34056d956ca66cd7d0920f791bcc15a1c31df207 Mon Sep 17 00:00:00 2001
From: Wei Zhou
Date: Mon, 2 Dec 2024 05:56:20 +0100
Subject: [PATCH 07/12] Improvement: management server peer states (#9885)
* Improvement: management server peer states
* Update pr9885: consider new mgmt server node which has msId=managementServerNodeId
* Update pr9885: update global config description
* Update pr9885: update label on UI
* framework: Do not update mshost_peer when mgmt server is Up as it will be updated by status update
* mgmt: Update state to Up when mgmt server writes heartbeat to db
* mgmt: change Service IP to Management IP
---------
Co-authored-by: Boris Stoyanov - a.k.a Bobby
---
.../server/ManagementServerHostStats.java | 2 +
.../apache/cloudstack/api/ApiConstants.java | 8 +
.../admin/management/ListMgmtsCmd.java | 10 +
.../response/ManagementServerResponse.java | 21 +++
.../PeerManagementServerNodeResponse.java | 100 ++++++++++
...spring-engine-schema-core-daos-context.xml | 1 +
.../cloud.idempotent_add_foreign_key.sql | 28 +++
.../META-INF/db/schema-41910to42000.sql | 7 +
.../db/views/cloud.mshost_peer_view.sql | 44 +++++
.../com/cloud/cluster/ClusterManager.java | 4 +-
.../com/cloud/cluster/ClusterManagerImpl.java | 98 +++++++---
.../ManagementServerHostPeerJoinVO.java | 177 ++++++++++++++++++
.../dao/ManagementServerHostDaoImpl.java | 2 +-
.../dao/ManagementServerHostPeerDao.java | 9 +-
.../dao/ManagementServerHostPeerDaoImpl.java | 42 ++++-
.../dao/ManagementServerHostPeerJoinDao.java | 27 +++
.../ManagementServerHostPeerJoinDaoImpl.java | 42 +++++
.../com/cloud/api/query/QueryManagerImpl.java | 36 +++-
.../ManagementServerHostStatsEntry.java | 10 +
.../java/com/cloud/server/StatsCollector.java | 24 +++
ui/public/locales/en.json | 12 +-
.../config/section/infra/managementServers.js | 4 +
.../views/infra/ManagementServerPeerTab.vue | 111 +++++++++++
23 files changed, 782 insertions(+), 37 deletions(-)
create mode 100644 api/src/main/java/org/apache/cloudstack/api/response/PeerManagementServerNodeResponse.java
create mode 100644 engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_foreign_key.sql
create mode 100644 engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_peer_view.sql
create mode 100644 framework/cluster/src/main/java/com/cloud/cluster/ManagementServerHostPeerJoinVO.java
create mode 100644 framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerJoinDao.java
create mode 100644 framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerJoinDaoImpl.java
create mode 100644 ui/src/views/infra/ManagementServerPeerTab.vue
diff --git a/api/src/main/java/com/cloud/server/ManagementServerHostStats.java b/api/src/main/java/com/cloud/server/ManagementServerHostStats.java
index 1f201d7689f..1eea7addba3 100644
--- a/api/src/main/java/com/cloud/server/ManagementServerHostStats.java
+++ b/api/src/main/java/com/cloud/server/ManagementServerHostStats.java
@@ -32,6 +32,8 @@ public interface ManagementServerHostStats {
String getManagementServerHostUuid();
+ long getManagementServerRunId();
+
long getSessions();
double getCpuUtilization();
diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
index 6df8d7f5f18..a6c6991be24 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -381,6 +381,14 @@ public class ApiConstants {
public static final String PATH = "path";
public static final String PAYLOAD = "payload";
public static final String PAYLOAD_URL = "payloadurl";
+ public static final String PEERS = "peers";
+ public static final String PEER_ID = "peerid";
+ public static final String PEER_NAME = "peername";
+ public static final String PEER_MSID = "peermsid";
+ public static final String PEER_RUNID = "peerrunid";
+ public static final String PEER_SERVICE_IP = "peerserviceip";
+ public static final String PEER_SERVICE_PORT = "peerserviceport";
+ public static final String PEER_STATE = "peerstate";
public static final String POD_ID = "podid";
public static final String POD_NAME = "podname";
public static final String POD_IDS = "podids";
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/management/ListMgmtsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/ListMgmtsCmd.java
index a68ed62857a..6b72deb0775 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/admin/management/ListMgmtsCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/management/ListMgmtsCmd.java
@@ -23,6 +23,7 @@ import org.apache.cloudstack.api.BaseListCmd;
import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.ManagementServerResponse;
+import org.apache.commons.lang3.BooleanUtils;
@APICommand(name = "listManagementServers", description = "Lists management servers.", responseObject = ManagementServerResponse.class,
requestHasSensitiveInfo = false, responseHasSensitiveInfo = false)
@@ -39,6 +40,11 @@ public class ListMgmtsCmd extends BaseListCmd {
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "the name of the management server")
private String hostName;
+ @Parameter(name = ApiConstants.PEERS, type = CommandType.BOOLEAN,
+ description = "Whether to return the management server peers or not. By default, the management server peers will not be returned.",
+ since = "4.20.0.0")
+ private Boolean peers;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -51,6 +57,10 @@ public class ListMgmtsCmd extends BaseListCmd {
return hostName;
}
+ public Boolean getPeers() {
+ return BooleanUtils.toBooleanDefaultIfNull(peers, false);
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ManagementServerResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ManagementServerResponse.java
index a471045eb67..fc7d3b722ab 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/ManagementServerResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/ManagementServerResponse.java
@@ -24,7 +24,9 @@ import org.apache.cloudstack.api.BaseResponse;
import org.apache.cloudstack.api.EntityReference;
import org.apache.cloudstack.management.ManagementServerHost.State;
+import java.util.ArrayList;
import java.util.Date;
+import java.util.List;
@EntityReference(value = ManagementServerHost.class)
public class ManagementServerResponse extends BaseResponse {
@@ -76,6 +78,10 @@ public class ManagementServerResponse extends BaseResponse {
@Param(description = "the IP Address for this Management Server")
private String serviceIp;
+ @SerializedName(ApiConstants.PEERS)
+ @Param(description = "the Management Server Peers")
+ private List peers;
+
public String getId() {
return this.id;
}
@@ -171,4 +177,19 @@ public class ManagementServerResponse extends BaseResponse {
public String getKernelVersion() {
return kernelVersion;
}
+
+ public List getPeers() {
+ return peers;
+ }
+
+ public void setPeers(List peers) {
+ this.peers = peers;
+ }
+
+ public void addPeer(PeerManagementServerNodeResponse peer) {
+ if (peers == null) {
+ peers = new ArrayList<>();
+ }
+ peers.add(peer);
+ }
}
diff --git a/api/src/main/java/org/apache/cloudstack/api/response/PeerManagementServerNodeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/PeerManagementServerNodeResponse.java
new file mode 100644
index 00000000000..802294171fa
--- /dev/null
+++ b/api/src/main/java/org/apache/cloudstack/api/response/PeerManagementServerNodeResponse.java
@@ -0,0 +1,100 @@
+// 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.
+package org.apache.cloudstack.api.response;
+
+import com.cloud.serializer.Param;
+import com.google.gson.annotations.SerializedName;
+import org.apache.cloudstack.api.ApiConstants;
+import org.apache.cloudstack.api.BaseResponse;
+import org.apache.cloudstack.management.ManagementServerHost.State;
+
+import java.util.Date;
+
+public class PeerManagementServerNodeResponse extends BaseResponse {
+
+ @SerializedName(ApiConstants.STATE)
+ @Param(description = "the state of the management server peer")
+ private State state;
+
+ @SerializedName(ApiConstants.LAST_UPDATED)
+ @Param(description = "the last updated time of the management server peer state")
+ private Date lastUpdated;
+
+ @SerializedName(ApiConstants.PEER_ID)
+ @Param(description = "the ID of the peer management server")
+ private String peerId;
+
+ @SerializedName(ApiConstants.PEER_NAME)
+ @Param(description = "the name of the peer management server")
+ private String peerName;
+
+ @SerializedName(ApiConstants.PEER_MSID)
+ @Param(description = "the management ID of the peer management server")
+ private String peerMsId;
+
+ @SerializedName(ApiConstants.PEER_RUNID)
+ @Param(description = "the run ID of the peer management server")
+ private String peerRunId;
+
+ @SerializedName(ApiConstants.PEER_STATE)
+ @Param(description = "the state of the peer management server")
+ private String peerState;
+
+ @SerializedName(ApiConstants.PEER_SERVICE_IP)
+ @Param(description = "the IP Address for the peer Management Server")
+ private String peerServiceIp;
+
+ @SerializedName(ApiConstants.PEER_SERVICE_PORT)
+ @Param(description = "the service port for the peer Management Server")
+ private String peerServicePort;
+
+ public void setState(State state) {
+ this.state = state;
+ }
+
+ public void setLastUpdated(Date lastUpdated) {
+ this.lastUpdated = lastUpdated;
+ }
+
+ public void setPeerId(String peerId) {
+ this.peerId = peerId;
+ }
+
+ public void setPeerName(String peerName) {
+ this.peerName = peerName;
+ }
+
+ public void setPeerMsId(String peerMsId) {
+ this.peerMsId = peerMsId;
+ }
+
+ public void setPeerRunId(String peerRunId) {
+ this.peerRunId = peerRunId;
+ }
+
+ public void setPeerState(String peerState) {
+ this.peerState = peerState;
+ }
+
+ public void setPeerServiceIp(String peerServiceIp) {
+ this.peerServiceIp = peerServiceIp;
+ }
+
+ public void setPeerServicePort(String peerServicePort) {
+ this.peerServicePort = peerServicePort;
+ }
+}
diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
index 171685ce413..4f22234d7bf 100644
--- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
+++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml
@@ -117,6 +117,7 @@
+
diff --git a/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_foreign_key.sql b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_foreign_key.sql
new file mode 100644
index 00000000000..754c02acb93
--- /dev/null
+++ b/engine/schema/src/main/resources/META-INF/db/procedures/cloud.idempotent_add_foreign_key.sql
@@ -0,0 +1,28 @@
+-- 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_ADD_FOREIGN_KEY`;
+
+CREATE PROCEDURE `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY` (
+ IN in_table_name VARCHAR(200)
+ , IN in_key_name VARCHAR(200)
+ , IN in_foreign_key VARCHAR(200)
+ , IN in_references VARCHAR(1000)
+)
+BEGIN
+
+ DECLARE CONTINUE HANDLER FOR 1061 BEGIN END; SET @ddl = CONCAT_WS(' ', 'ALTER TABLE ', in_table_name, ' ADD CONSTRAINT ', in_key_name, ' FOREIGN KEY ', in_foreign_key, ' REFERENCES ', in_references, ' ON DELETE CASCADE'); PREPARE stmt FROM @ddl; EXECUTE stmt; DEALLOCATE PREPARE stmt; END;
diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
index c36b71c2f25..97ee1df8b67 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-41910to42000.sql
@@ -425,3 +425,10 @@ INSERT IGNORE INTO `cloud`.`guest_os_hypervisor` (uuid, hypervisor_type, hypervi
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vm_instance', 'delete_protection', 'boolean DEFAULT FALSE COMMENT "delete protection for vm" ');
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.volumes', 'delete_protection', 'boolean DEFAULT FALSE COMMENT "delete protection for volumes" ');
+
+-- Modify index for mshost_peer
+DELETE FROM `cloud`.`mshost_peer`;
+CALL `cloud`.`IDEMPOTENT_DROP_FOREIGN_KEY`('cloud.mshost_peer','fk_mshost_peer__owner_mshost');
+CALL `cloud`.`IDEMPOTENT_DROP_INDEX`('i_mshost_peer__owner_peer_runid','mshost_peer');
+CALL `cloud`.`IDEMPOTENT_ADD_UNIQUE_KEY`('cloud.mshost_peer', 'i_mshost_peer__owner_peer', '(owner_mshost, peer_mshost)');
+CALL `cloud`.`IDEMPOTENT_ADD_FOREIGN_KEY`('cloud.mshost_peer', 'fk_mshost_peer__owner_mshost', '(owner_mshost)', '`mshost`(`id`)');
diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_peer_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_peer_view.sql
new file mode 100644
index 00000000000..5f741449d85
--- /dev/null
+++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.mshost_peer_view.sql
@@ -0,0 +1,44 @@
+-- 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 VIEW IF EXISTS `cloud`.`mshost_peer_view`;
+
+CREATE VIEW `cloud`.`mshost_peer_view` AS
+SELECT
+ `mshost_peer`.`id` AS `id`,
+ `mshost_peer`.`peer_state` AS `peer_state`,
+ `mshost_peer`.`last_update` AS `last_update`,
+ `owner_mshost`.`id` AS `owner_mshost_id`,
+ `owner_mshost`.`msid` AS `owner_mshost_msid`,
+ `owner_mshost`.`runid` AS `owner_mshost_runid`,
+ `owner_mshost`.`name` AS `owner_mshost_name`,
+ `owner_mshost`.`uuid` AS `owner_mshost_uuid`,
+ `owner_mshost`.`state` AS `owner_mshost_state`,
+ `owner_mshost`.`service_ip` AS `owner_mshost_service_ip`,
+ `owner_mshost`.`service_port` AS `owner_mshost_service_port`,
+ `peer_mshost`.`id` AS `peer_mshost_id`,
+ `peer_mshost`.`msid` AS `peer_mshost_msid`,
+ `peer_mshost`.`runid` AS `peer_mshost_runid`,
+ `peer_mshost`.`name` AS `peer_mshost_name`,
+ `peer_mshost`.`uuid` AS `peer_mshost_uuid`,
+ `peer_mshost`.`state` AS `peer_mshost_state`,
+ `peer_mshost`.`service_ip` AS `peer_mshost_service_ip`,
+ `peer_mshost`.`service_port` AS `peer_mshost_service_port`
+FROM `cloud`.`mshost_peer`
+LEFT JOIN `cloud`.`mshost` AS owner_mshost on `mshost_peer`.`owner_mshost` = `owner_mshost`.`id`
+LEFT JOIN `cloud`.`mshost` AS peer_mshost on `mshost_peer`.`peer_mshost` = `peer_mshost`.`id`;
diff --git a/framework/cluster/src/main/java/com/cloud/cluster/ClusterManager.java b/framework/cluster/src/main/java/com/cloud/cluster/ClusterManager.java
index 54f575830e4..7fdaa6f7f77 100644
--- a/framework/cluster/src/main/java/com/cloud/cluster/ClusterManager.java
+++ b/framework/cluster/src/main/java/com/cloud/cluster/ClusterManager.java
@@ -27,9 +27,9 @@ import com.cloud.utils.component.Manager;
public interface ClusterManager extends Manager {
static final String ALERT_SUBJECT = "cluster-alert";
final ConfigKey HeartbeatInterval = new ConfigKey(Integer.class, "cluster.heartbeat.interval", "management-server", "1500",
- "Interval to check for the heart beat between management server nodes", false);
+ "Interval (in milliseconds) to check for the heart beat between management server nodes", false);
final ConfigKey HeartbeatThreshold = new ConfigKey(Integer.class, "cluster.heartbeat.threshold", "management-server", "150000",
- "Threshold before self-fence the management server", true);
+ "Threshold (in milliseconds) before self-fence the management server. The threshold should be larger than management.server.stats.interval", true);
/**
* Adds a new packet to the incoming queue.
diff --git a/framework/cluster/src/main/java/com/cloud/cluster/ClusterManagerImpl.java b/framework/cluster/src/main/java/com/cloud/cluster/ClusterManagerImpl.java
index 32fdf782696..0ec566a4194 100644
--- a/framework/cluster/src/main/java/com/cloud/cluster/ClusterManagerImpl.java
+++ b/framework/cluster/src/main/java/com/cloud/cluster/ClusterManagerImpl.java
@@ -758,21 +758,16 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
}
switch (msg.getMessageType()) {
- case nodeAdded: {
- final List l = msg.getNodes();
- if (l != null && l.size() > 0) {
- for (final ManagementServerHostVO mshost : l) {
- _mshostPeerDao.updatePeerInfo(_mshostId, mshost.getId(), mshost.getRunid(), ManagementServerHost.State.Up);
- }
- }
- }
+ case nodeAdded:
break;
case nodeRemoved: {
final List l = msg.getNodes();
if (l != null && l.size() > 0) {
for (final ManagementServerHostVO mshost : l) {
- _mshostPeerDao.updatePeerInfo(_mshostId, mshost.getId(), mshost.getRunid(), ManagementServerHost.State.Down);
+ if (mshost.getId() != _mshostId) {
+ _mshostPeerDao.updatePeerInfo(_mshostId, mshost.getId(), mshost.getRunid(), ManagementServerHost.State.Down);
+ }
}
}
}
@@ -823,8 +818,9 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
final List downHostList = new ArrayList();
for (final ManagementServerHostVO host : inactiveList) {
- if (!pingManagementNode(host)) {
- logger.warn("Management node " + host.getId() + " is detected inactive by timestamp and also not pingable");
+ // Check if peer state is Up in the period
+ if (!_mshostPeerDao.isPeerUpState(_mshostId, host.getId(), new Date(cutTime.getTime() - HeartbeatThreshold.value()))) {
+ logger.warn("Management node " + host.getId() + " is detected inactive by timestamp and did not send node status to this node");
downHostList.add(host);
}
}
@@ -898,6 +894,44 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
final Profiler profilerInvalidatedNodeList = new Profiler();
profilerInvalidatedNodeList.start();
+ processInvalidatedNodes(invalidatedNodeList);
+ profilerInvalidatedNodeList.stop();
+
+ final Profiler profilerRemovedList = new Profiler();
+ profilerRemovedList.start();
+ processRemovedNodes(cutTime, removedNodeList);
+ profilerRemovedList.stop();
+
+ final Profiler profilerNewList = new Profiler();
+ profilerNewList.start();
+ processNewNodes(cutTime, currentList);
+ profilerNewList.stop();
+
+ final Profiler profilerInactiveList = new Profiler();
+ profilerInactiveList.start();
+ processInactiveNodes(cutTime);
+ profilerInactiveList.stop();
+
+ profiler.stop();
+
+ logger.debug(String.format("Peer scan is finished. profiler: %s , profilerQueryActiveList: %s, " +
+ ", profilerSyncClusterInfo: %s, profilerInvalidatedNodeList: %s, profilerRemovedList: %s," +
+ ", profilerNewList: %s, profilerInactiveList: %s",
+ profiler, profilerQueryActiveList, profilerSyncClusterInfo, profilerInvalidatedNodeList, profilerRemovedList,
+ profilerNewList, profilerInactiveList));
+
+ if (profiler.getDurationInMillis() >= HeartbeatInterval.value()) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Peer scan takes too long to finish. profiler: %s , profilerQueryActiveList: %s, " +
+ ", profilerSyncClusterInfo: %s, profilerInvalidatedNodeList: %s, profilerRemovedList: %s," +
+ ", profilerNewList: %s, profilerInactiveList: %s",
+ profiler, profilerQueryActiveList, profilerSyncClusterInfo, profilerInvalidatedNodeList, profilerRemovedList,
+ profilerNewList, profilerInactiveList));
+ }
+ }
+ }
+
+ private void processInvalidatedNodes(List invalidatedNodeList) {
// process invalidated node list
if (invalidatedNodeList.size() > 0) {
for (final ManagementServerHostVO mshost : invalidatedNodeList) {
@@ -911,16 +945,16 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeRemoved, invalidatedNodeList));
}
- profilerInvalidatedNodeList.stop();
+ }
- final Profiler profilerRemovedList = new Profiler();
- profilerRemovedList.start();
+ private void processRemovedNodes(Date cutTime, List removedNodeList) {
// process removed node list
final Iterator it = removedNodeList.iterator();
while (it.hasNext()) {
final ManagementServerHostVO mshost = it.next();
- if (!pingManagementNode(mshost)) {
- logger.warn("Management node " + mshost.getId() + " is detected inactive by timestamp and also not pingable");
+ // Check if peer state is Up in the period
+ if (!_mshostPeerDao.isPeerUpState(_mshostId, mshost.getId(), new Date(cutTime.getTime() - HeartbeatThreshold.value()))) {
+ logger.warn("Management node " + mshost.getId() + " is detected inactive by timestamp and did not send node status to this node");
_activePeers.remove(mshost.getId());
try {
JmxUtil.unregisterMBean("ClusterManager", "Node " + mshost.getId());
@@ -928,7 +962,7 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
logger.warn("Unable to deregiester cluster node from JMX monitoring due to exception " + e.toString());
}
} else {
- logger.info("Management node " + mshost.getId() + " is detected inactive by timestamp but is pingable");
+ logger.info("Management node " + mshost.getId() + " is detected inactive by timestamp but sent node status to this node");
it.remove();
}
}
@@ -936,8 +970,9 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
if (removedNodeList.size() > 0) {
queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeRemoved, removedNodeList));
}
- profilerRemovedList.stop();
+ }
+ private void processNewNodes(Date cutTime, List currentList) {
final List newNodeList = new ArrayList();
for (final ManagementServerHostVO mshost : currentList) {
if (!_activePeers.containsKey(mshost.getId())) {
@@ -959,18 +994,31 @@ public class ClusterManagerImpl extends ManagerBase implements ClusterManager, C
if (newNodeList.size() > 0) {
queueNotification(new ClusterManagerMessage(ClusterManagerMessage.MessageType.nodeAdded, newNodeList));
}
+ }
- profiler.stop();
-
- if (profiler.getDurationInMillis() >= HeartbeatInterval.value()) {
- if (logger.isDebugEnabled()) {
- logger.debug("Peer scan takes too long to finish. profiler: " + profiler.toString() + ", profilerQueryActiveList: " +
- profilerQueryActiveList.toString() + ", profilerSyncClusterInfo: " + profilerSyncClusterInfo.toString() + ", profilerInvalidatedNodeList: " +
- profilerInvalidatedNodeList.toString() + ", profilerRemovedList: " + profilerRemovedList.toString());
+ private void processInactiveNodes(Date cutTime) {
+ final List inactiveList = _mshostDao.getInactiveList(new Date(cutTime.getTime() - HeartbeatThreshold.value()));
+ if (inactiveList.size() > 0) {
+ if (logger.isInfoEnabled()) {
+ logger.info(String.format("Found %s inactive management server node based on timestamp", inactiveList.size()));
}
+ for (final ManagementServerHostVO host : inactiveList) {
+ logger.info(String.format("management server node msid: %s, name: %s, service ip: %s, version: %s",
+ host.getMsid(), host.getName(), host.getServiceIP(), host.getVersion()));
+ // Check if any peer state is Up in the period
+ if (ManagementServerHost.State.Up.equals(host.getState()) &&
+ !_mshostPeerDao.isPeerUpState(host.getId(), new Date(cutTime.getTime() - HeartbeatThreshold.value()))) {
+ logger.warn("Management node " + host.getId() + " is detected inactive by timestamp and did not send node status to all other nodes");
+ host.setState(ManagementServerHost.State.Down);
+ _mshostDao.update(host.getId(), host);
+ }
+ }
+ } else {
+ logger.info("No inactive management server node found");
}
}
+
private static ManagementServerHostVO getInListById(final Long id, final List l) {
for (final ManagementServerHostVO mshost : l) {
if (mshost.getId() == id) {
diff --git a/framework/cluster/src/main/java/com/cloud/cluster/ManagementServerHostPeerJoinVO.java b/framework/cluster/src/main/java/com/cloud/cluster/ManagementServerHostPeerJoinVO.java
new file mode 100644
index 00000000000..673db160b3c
--- /dev/null
+++ b/framework/cluster/src/main/java/com/cloud/cluster/ManagementServerHostPeerJoinVO.java
@@ -0,0 +1,177 @@
+// 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.
+package com.cloud.cluster;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.Table;
+import javax.persistence.Temporal;
+import javax.persistence.TemporalType;
+
+import org.apache.cloudstack.management.ManagementServerHost;
+
+@Entity
+@Table(name = "mshost_peer_view")
+public class ManagementServerHostPeerJoinVO {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @Column(name = "id")
+ private long id;
+
+ @Column(name = "peer_state")
+ @Enumerated(value = EnumType.STRING)
+ private ManagementServerHost.State peerState;
+
+ @Temporal(TemporalType.TIMESTAMP)
+ @Column(name = "last_update")
+ private Date lastUpdateTime;
+
+ @Column(name = "owner_mshost_id")
+ private long ownerMshostId;
+
+ @Column(name = "owner_mshost_msid")
+ private long ownerMshostMsId;
+
+ @Column(name = "owner_mshost_runid")
+ private long ownerMshostRunId;
+
+ @Column(name = "owner_mshost_name")
+ private String ownerMshostName;
+
+ @Column(name = "owner_mshost_uuid")
+ private String ownerMshostUuid;
+
+ @Column(name = "owner_mshost_state")
+ private String ownerMshostState;
+
+ @Column(name = "owner_mshost_service_ip")
+ private String ownerMshostServiceIp;
+
+ @Column(name = "owner_mshost_service_port")
+ private Integer ownerMshostServicePort;
+
+ @Column(name = "peer_mshost_id")
+ private long peerMshostId;
+
+ @Column(name = "peer_mshost_msid")
+ private long peerMshostMsId;
+
+ @Column(name = "peer_mshost_runid")
+ private long peerMshostRunId;
+
+ @Column(name = "peer_mshost_name")
+ private String peerMshostName;
+
+ @Column(name = "peer_mshost_uuid")
+ private String peerMshostUuid;
+
+ @Column(name = "peer_mshost_state")
+ private String peerMshostState;
+
+ @Column(name = "peer_mshost_service_ip")
+ private String peerMshostServiceIp;
+
+ @Column(name = "peer_mshost_service_port")
+ private Integer peerMshostServicePort;
+
+ public ManagementServerHostPeerJoinVO() {
+ }
+
+ public long getId() {
+ return id;
+ }
+
+ public ManagementServerHost.State getPeerState() {
+ return peerState;
+ }
+
+ public Date getLastUpdateTime() {
+ return lastUpdateTime;
+ }
+
+ public long getOwnerMshostId() {
+ return ownerMshostId;
+ }
+
+ public long getOwnerMshostMsId() {
+ return ownerMshostMsId;
+ }
+
+ public long getOwnerMshostRunId() {
+ return ownerMshostRunId;
+ }
+
+ public String getOwnerMshostName() {
+ return ownerMshostName;
+ }
+
+ public String getOwnerMshostUuid() {
+ return ownerMshostUuid;
+ }
+
+ public String getOwnerMshostState() {
+ return ownerMshostState;
+ }
+
+ public String getOwnerMshostServiceIp() {
+ return ownerMshostServiceIp;
+ }
+
+ public Integer getOwnerMshostServicePort() {
+ return ownerMshostServicePort;
+ }
+
+ public long getPeerMshostId() {
+ return peerMshostId;
+ }
+
+ public long getPeerMshostMsId() {
+ return peerMshostMsId;
+ }
+
+ public long getPeerMshostRunId() {
+ return peerMshostRunId;
+ }
+
+ public String getPeerMshostName() {
+ return peerMshostName;
+ }
+
+ public String getPeerMshostUuid() {
+ return peerMshostUuid;
+ }
+
+ public String getPeerMshostState() {
+ return peerMshostState;
+ }
+
+ public String getPeerMshostServiceIp() {
+ return peerMshostServiceIp;
+ }
+
+ public Integer getPeerMshostServicePort() {
+ return peerMshostServicePort;
+ }
+}
diff --git a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDaoImpl.java b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDaoImpl.java
index 7b69889c853..27b6d52f61b 100644
--- a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDaoImpl.java
+++ b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostDaoImpl.java
@@ -130,7 +130,7 @@ public class ManagementServerHostDaoImpl extends GenericDaoBase {
void clearPeerInfo(long ownerMshost);
void updatePeerInfo(long ownerMshost, long peerMshost, long peerRunid, ManagementServerHost.State peerState);
- int countStateSeenInPeers(long mshost, long runid, ManagementServerHost.State state);
+ int countStateSeenInPeers(long peerMshost, long runid, ManagementServerHost.State state);
+
+ boolean isPeerUpState(long peerMshost, Date cutTime);
+
+ boolean isPeerUpState(long ownerMshost, long peerMshost, Date cutTime);
+
}
diff --git a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerDaoImpl.java b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerDaoImpl.java
index 827be4fe299..ec69f5817ac 100644
--- a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerDaoImpl.java
+++ b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerDaoImpl.java
@@ -16,10 +16,10 @@
// under the License.
package com.cloud.cluster.dao;
+import java.util.Date;
import java.util.List;
-
import org.apache.cloudstack.management.ManagementServerHost;
import com.cloud.cluster.ManagementServerHostPeerVO;
import com.cloud.utils.db.DB;
@@ -33,10 +33,12 @@ public class ManagementServerHostPeerDaoImpl extends GenericDaoBase ClearPeerSearch;
private final SearchBuilder FindForUpdateSearch;
private final SearchBuilder CountSearch;
+ private final SearchBuilder ActiveSearch;
public ManagementServerHostPeerDaoImpl() {
ClearPeerSearch = createSearchBuilder();
ClearPeerSearch.and("ownerMshost", ClearPeerSearch.entity().getOwnerMshost(), SearchCriteria.Op.EQ);
+ ClearPeerSearch.or("peerMshost", ClearPeerSearch.entity().getPeerMshost(), SearchCriteria.Op.EQ);
ClearPeerSearch.done();
FindForUpdateSearch = createSearchBuilder();
@@ -50,6 +52,13 @@ public class ManagementServerHostPeerDaoImpl extends GenericDaoBase sc = ClearPeerSearch.create();
sc.setParameters("ownerMshost", ownerMshost);
+ sc.setParameters("peerMshost", ownerMshost);
expunge(sc);
}
@@ -71,11 +81,12 @@ public class ManagementServerHostPeerDaoImpl extends GenericDaoBase sc = FindForUpdateSearch.create();
sc.setParameters("ownerMshost", ownerMshost);
sc.setParameters("peerMshost", peerMshost);
- sc.setParameters("peerRunid", peerRunid);
List l = listBy(sc);
if (l.size() == 1) {
ManagementServerHostPeerVO peer = l.get(0);
+ peer.setPeerRunid(peerRunid);
peer.setPeerState(peerState);
+ peer.setLastUpdateTime(new Date());
update(peer.getId(), peer);
} else {
ManagementServerHostPeerVO peer = new ManagementServerHostPeerVO(ownerMshost, peerMshost, peerRunid, peerState);
@@ -90,13 +101,36 @@ public class ManagementServerHostPeerDaoImpl extends GenericDaoBase sc = CountSearch.create();
- sc.setParameters("peerMshost", mshost);
+ sc.setParameters("peerMshost", peerMshost);
sc.setParameters("peerRunid", runid);
sc.setParameters("peerState", state);
List l = listBy(sc);
return l.size();
}
+
+ @Override
+ @DB
+ public boolean isPeerUpState(long peerMshost, Date cutTime) {
+ SearchCriteria sc = ActiveSearch.create();
+ sc.setParameters("peerMshost", peerMshost);
+ sc.setParameters("peerState", ManagementServerHost.State.Up);
+ sc.setParameters("lastUpdateTime", cutTime);
+
+ return listBy(sc).size() > 0;
+ }
+
+ @Override
+ @DB
+ public boolean isPeerUpState(long ownerMshost, long peerMshost, Date cutTime) {
+ SearchCriteria sc = ActiveSearch.create();
+ sc.setParameters("ownerMshost", ownerMshost);
+ sc.setParameters("peerMshost", peerMshost);
+ sc.setParameters("peerState", ManagementServerHost.State.Up);
+ sc.setParameters("lastUpdateTime", cutTime);
+
+ return listBy(sc).size() > 0;
+ }
}
diff --git a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerJoinDao.java b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerJoinDao.java
new file mode 100644
index 00000000000..46f87b6484c
--- /dev/null
+++ b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerJoinDao.java
@@ -0,0 +1,27 @@
+// 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.
+package com.cloud.cluster.dao;
+
+import com.cloud.cluster.ManagementServerHostPeerJoinVO;
+import com.cloud.utils.db.GenericDao;
+
+import java.util.List;
+
+public interface ManagementServerHostPeerJoinDao extends GenericDao {
+
+ List listByOwnerMshostId(long ownerMshostId);
+}
diff --git a/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerJoinDaoImpl.java b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerJoinDaoImpl.java
new file mode 100644
index 00000000000..16a17863d04
--- /dev/null
+++ b/framework/cluster/src/main/java/com/cloud/cluster/dao/ManagementServerHostPeerJoinDaoImpl.java
@@ -0,0 +1,42 @@
+// 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.
+package com.cloud.cluster.dao;
+
+import java.util.List;
+
+import com.cloud.cluster.ManagementServerHostPeerJoinVO;
+import com.cloud.utils.db.GenericDaoBase;
+import com.cloud.utils.db.SearchBuilder;
+import com.cloud.utils.db.SearchCriteria;
+
+public class ManagementServerHostPeerJoinDaoImpl extends GenericDaoBase implements ManagementServerHostPeerJoinDao {
+
+ private final SearchBuilder AllFieldSearch;
+
+ public ManagementServerHostPeerJoinDaoImpl() {
+ AllFieldSearch = createSearchBuilder();
+ AllFieldSearch.and("ownerMshostId", AllFieldSearch.entity().getOwnerMshostId(), SearchCriteria.Op.EQ);
+ AllFieldSearch.done();
+ }
+
+ @Override
+ public List listByOwnerMshostId(long ownerMshostId) {
+ SearchCriteria sc = AllFieldSearch.create();
+ sc.setParameters("ownerMshostId", ownerMshostId);
+ return listBy(sc);
+ }
+}
diff --git a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
index e7bd2174a61..25018bc2c36 100644
--- a/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
+++ b/server/src/main/java/com/cloud/api/query/QueryManagerImpl.java
@@ -36,7 +36,6 @@ import java.util.stream.Stream;
import javax.inject.Inject;
-import com.cloud.cpu.CPU;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.SecurityChecker;
@@ -114,6 +113,7 @@ import org.apache.cloudstack.api.response.IpQuarantineResponse;
import org.apache.cloudstack.api.response.ListResponse;
import org.apache.cloudstack.api.response.ManagementServerResponse;
import org.apache.cloudstack.api.response.ObjectStoreResponse;
+import org.apache.cloudstack.api.response.PeerManagementServerNodeResponse;
import org.apache.cloudstack.api.response.ProjectAccountResponse;
import org.apache.cloudstack.api.response.ProjectInvitationResponse;
import org.apache.cloudstack.api.response.ProjectResponse;
@@ -214,8 +214,11 @@ import com.cloud.api.query.vo.TemplateJoinVO;
import com.cloud.api.query.vo.UserAccountJoinVO;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.api.query.vo.VolumeJoinVO;
+import com.cloud.cluster.ManagementServerHostPeerJoinVO;
import com.cloud.cluster.ManagementServerHostVO;
import com.cloud.cluster.dao.ManagementServerHostDao;
+import com.cloud.cluster.dao.ManagementServerHostPeerJoinDao;
+import com.cloud.cpu.CPU;
import com.cloud.dc.ClusterVO;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DedicatedResourceVO;
@@ -607,6 +610,9 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
@Inject
private ClusterDao clusterDao;
+ @Inject
+ private ManagementServerHostPeerJoinDao mshostPeerJoinDao;
+
private SearchCriteria getMinimumCpuServiceOfferingJoinSearchCriteria(int cpu) {
SearchCriteria sc = _srvOfferingJoinDao.createSearchCriteria();
@@ -5342,7 +5348,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
List hostResponses = new ArrayList<>();
for (ManagementServerJoinVO host : result.first()) {
- ManagementServerResponse hostResponse = createManagementServerResponse(host);
+ ManagementServerResponse hostResponse = createManagementServerResponse(host, cmd.getPeers());
hostResponses.add(hostResponse);
}
@@ -5365,7 +5371,7 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
return managementServerJoinDao.searchAndCount(sc, null);
}
- protected ManagementServerResponse createManagementServerResponse(ManagementServerJoinVO mgmt) {
+ protected ManagementServerResponse createManagementServerResponse(ManagementServerJoinVO mgmt, boolean listPeers) {
ManagementServerResponse mgmtResponse = new ManagementServerResponse();
mgmtResponse.setId(mgmt.getUuid());
mgmtResponse.setName(mgmt.getName());
@@ -5378,10 +5384,34 @@ public class QueryManagerImpl extends MutualExclusiveIdsManagerBase implements Q
mgmtResponse.setLastServerStop(mgmt.getLastJvmStop());
mgmtResponse.setLastBoot(mgmt.getLastSystemBoot());
mgmtResponse.setServiceIp(mgmt.getServiceIP());
+ if (listPeers) {
+ List peers = mshostPeerJoinDao.listByOwnerMshostId(mgmt.getId());
+ for (ManagementServerHostPeerJoinVO peer: peers) {
+ mgmtResponse.addPeer(createPeerManagementServerNodeResponse(peer));
+ }
+ }
mgmtResponse.setObjectName("managementserver");
return mgmtResponse;
}
+ private PeerManagementServerNodeResponse createPeerManagementServerNodeResponse(ManagementServerHostPeerJoinVO peer) {
+ PeerManagementServerNodeResponse response = new PeerManagementServerNodeResponse();
+
+ response.setState(peer.getPeerState());
+ response.setLastUpdated(peer.getLastUpdateTime());
+
+ response.setPeerId(peer.getPeerMshostUuid());
+ response.setPeerName(peer.getPeerMshostName());
+ response.setPeerMsId(String.valueOf(peer.getPeerMshostMsId()));
+ response.setPeerRunId(String.valueOf(peer.getPeerMshostRunId()));
+ response.setPeerState(peer.getPeerMshostState());
+ response.setPeerServiceIp(peer.getPeerMshostServiceIp());
+ response.setPeerServicePort(String.valueOf(peer.getPeerMshostServicePort()));
+
+ response.setObjectName("peermanagementserver");
+ return response;
+ }
+
@Override
public List listRouterHealthChecks(GetRouterHealthCheckResultsCmd cmd) {
logger.info("Executing health check command " + cmd);
diff --git a/server/src/main/java/com/cloud/server/ManagementServerHostStatsEntry.java b/server/src/main/java/com/cloud/server/ManagementServerHostStatsEntry.java
index fb0084f3d76..172ab1e83eb 100644
--- a/server/src/main/java/com/cloud/server/ManagementServerHostStatsEntry.java
+++ b/server/src/main/java/com/cloud/server/ManagementServerHostStatsEntry.java
@@ -24,6 +24,7 @@ public class ManagementServerHostStatsEntry implements ManagementServerHostStats
private long managementServerHostId;
private String managementServerHostUuid;
+ private long managementServerRunId;
private Date collectionTime;
private long sessions;
@@ -94,6 +95,15 @@ public class ManagementServerHostStatsEntry implements ManagementServerHostStats
this.managementServerHostUuid = managementServerHostUuid;
}
+ @Override
+ public long getManagementServerRunId() {
+ return managementServerRunId;
+ }
+
+ public void setManagementServerRunId(long managementServerRunId) {
+ this.managementServerRunId = managementServerRunId;
+ }
+
@Override
public Date getCollectionTime(){
return collectionTime;
diff --git a/server/src/main/java/com/cloud/server/StatsCollector.java b/server/src/main/java/com/cloud/server/StatsCollector.java
index 70959b56cfd..939781f39f7 100644
--- a/server/src/main/java/com/cloud/server/StatsCollector.java
+++ b/server/src/main/java/com/cloud/server/StatsCollector.java
@@ -95,6 +95,7 @@ import com.cloud.cluster.ClusterServicePdu;
import com.cloud.cluster.ManagementServerHostVO;
import com.cloud.cluster.ManagementServerStatusVO;
import com.cloud.cluster.dao.ManagementServerHostDao;
+import com.cloud.cluster.dao.ManagementServerHostPeerDao;
import com.cloud.cluster.dao.ManagementServerStatusDao;
import com.cloud.dc.Vlan.VlanType;
import com.cloud.dc.VlanVO;
@@ -346,6 +347,8 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
@Inject
private ManagementServerStatusDao managementServerStatusDao;
@Inject
+ private ManagementServerHostPeerDao managementServerHostPeerDao;
+ @Inject
VirtualMachineManager virtualMachineManager;
private final ConcurrentHashMap managementServerHostStats = new ConcurrentHashMap<>();
@@ -796,6 +799,7 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
logger.trace("Metrics collection start...");
newEntry.setManagementServerHostId(mshost.getId());
newEntry.setManagementServerHostUuid(mshost.getUuid());
+ newEntry.setManagementServerRunId(mshost.getRunid());
newEntry.setDbLocal(isDbLocal());
newEntry.setUsageLocal(isUsageLocal());
retrieveSession(newEntry);
@@ -1153,6 +1157,9 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
try {
hostStatsEntry = gson.fromJson(pdu.getJsonPackage(),new TypeToken(){}.getType());
managementServerHostStats.put(hostStatsEntry.getManagementServerHostUuid(), hostStatsEntry);
+
+ // Update peer state to Up in mshost_peer
+ updatePeerInfo(hostStatsEntry);
} catch (JsonParseException e) {
logger.error("Exception in decoding of other MS hosts status from : " + pdu.getSourcePeer());
if (logger.isDebugEnabled()) {
@@ -1162,6 +1169,23 @@ public class StatsCollector extends ManagerBase implements ComponentMethodInterc
return null;
}
+ private void updatePeerInfo(ManagementServerHostStatsEntry hostStatsEntry) {
+ // Update msId to id of the management server if msId is same as managementServerNodeId
+ if (msId == managementServerNodeId) {
+ ManagementServerHostVO mgmtServerVo = managementServerHostDao.findByMsid(managementServerNodeId);
+ if (mgmtServerVo != null) {
+ msId = mgmtServerVo.getId();
+ } else {
+ logger.warn(String.format("Cannot find management server with msid [%s]. Therefore, do not update peer info.", managementServerNodeId));
+ return;
+ }
+ }
+ // Update peer state to Up in mshost_peer
+ if (msId != hostStatsEntry.getManagementServerHostId()) {
+ managementServerHostPeerDao.updatePeerInfo(msId, hostStatsEntry.getManagementServerHostId(), hostStatsEntry.getManagementServerRunId(), ManagementServerHost.State.Up);
+ }
+ }
+
@Override
public void onManagementNodeJoined(List extends ManagementServerHost> nodeList, long selfNodeId) {
// do nothing, but wait for the status to come through
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index eb1be420b2e..a4b5a860c08 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1291,6 +1291,7 @@
"label.l3gatewayserviceuuid": "L3 Gateway Service UUID",
"label.label": "Label",
"label.last.updated": "Last update",
+"label.lastupdated": "Last update",
"label.lastannotated": "Last annotation date",
"label.lastheartbeat": "Last heartbeat",
"label.lastsuccessfuljob": "Last successful job",
@@ -1379,6 +1380,7 @@
"label.management.ips": "Management IP addresses",
"label.management.server": "Management server",
"label.management.servers": "Management servers",
+"label.management.server.peers": "Peers",
"label.managementservers": "Number of management servers",
"label.matchall": "Match all",
"label.max": "Max.",
@@ -1667,6 +1669,13 @@
"label.payload": "Payload",
"label.payloadurl": "Payload URL",
"label.pcidevice": "GPU",
+"label.peername": "Management Server",
+"label.peermsid": "Management Server Node ID",
+"label.peerrunid": "Process Timestamp",
+"label.peerserviceip": "Management IP",
+"label.peerserviceport": "Service Port",
+"label.peerstate": "Peer State",
+"label.peerstate.lastupdated": "Peer State Updated Time",
"label.pending.jobs": "Pending Jobs",
"label.per.account": "Per Account",
"label.per.zone": "Per zone",
@@ -2035,7 +2044,7 @@
"label.service.connectivity.distributedroutercapabilitycheckbox": "Distributed router",
"label.service.connectivity.regionlevelvpccapabilitycheckbox": "Region level VPC",
"label.service.group": "Service group",
-"label.serviceip": "Service IP",
+"label.serviceip": "Management IP",
"label.service.lb.elasticlbcheckbox": "Elastic LB",
"label.service.lb.inlinemodedropdown": "Mode",
"label.service.lb.lbisolationdropdown": "LB isolation",
@@ -2150,6 +2159,7 @@
"label.startport": "Start port",
"label.startquota": "Quota value",
"label.state": "State",
+"label.state.reported": "Reported State",
"label.staticnat": "Static NAT",
"label.static": "Static",
"label.static.routes": "Static routes",
diff --git a/ui/src/config/section/infra/managementServers.js b/ui/src/config/section/infra/managementServers.js
index cda19ef7cd5..a8bf1be1a01 100644
--- a/ui/src/config/section/infra/managementServers.js
+++ b/ui/src/config/section/infra/managementServers.js
@@ -43,6 +43,10 @@ export default {
name: 'pending.jobs',
component: shallowRef(defineAsyncComponent(() => import('@/views/infra/AsyncJobsTab.vue')))
},
+ {
+ name: 'management.server.peers',
+ component: shallowRef(defineAsyncComponent(() => import('@/views/infra/ManagementServerPeerTab.vue')))
+ },
{
name: 'comments',
component: shallowRef(defineAsyncComponent(() => import('@/components/view/AnnotationsTab.vue')))
diff --git a/ui/src/views/infra/ManagementServerPeerTab.vue b/ui/src/views/infra/ManagementServerPeerTab.vue
new file mode 100644
index 00000000000..47cffca2eee
--- /dev/null
+++ b/ui/src/views/infra/ManagementServerPeerTab.vue
@@ -0,0 +1,111 @@
+// 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.
+
+
+
+
+ {{ text }}
+
+
+ {{ text }}
+
+
+ {{ $toLocaleDate(text) }}
+
+
+
+
+
From 8a1da3804c0a00dfffc85986547e20f5558be8d4 Mon Sep 17 00:00:00 2001
From: Wei Zhou
Date: Mon, 2 Dec 2024 05:58:14 +0100
Subject: [PATCH 08/12] Resize volume: add pool capacity disablethreshold for
resize and allow volume auto migration (#9761)
* server: add global settings for volume resize
* resizeVolume: support automigrate
* Address Suresh's comments
* Update api/src/main/java/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java
Co-authored-by: Suresh Kumar Anaparti
* address Suresh's comments
* UI: add autoMigrate to resizeVolume
* resizevolume: add unit tests
* resizevolume: add unit test for Allocated volume
---------
Co-authored-by: Suresh Kumar Anaparti
---
.../command/user/volume/ResizeVolumeCmd.java | 8 ++
.../com/cloud/capacity/CapacityManager.java | 12 ++
.../com/cloud/storage/StorageManager.java | 5 +
.../cloud/capacity/CapacityManagerImpl.java | 3 +-
.../ConfigurationManagerImpl.java | 1 +
.../com/cloud/storage/StorageManagerImpl.java | 25 +++-
.../cloud/storage/VolumeApiServiceImpl.java | 68 +++++++++--
.../cloud/storage/StorageManagerImplTest.java | 79 +++++++++++++
.../storage/VolumeApiServiceImplTest.java | 108 ++++++++++++++++++
ui/src/views/storage/ResizeVolume.vue | 13 +++
10 files changed, 309 insertions(+), 13 deletions(-)
diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java
index 65a3d6a7063..eb89115cf46 100644
--- a/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java
+++ b/api/src/main/java/org/apache/cloudstack/api/command/user/volume/ResizeVolumeCmd.java
@@ -73,6 +73,10 @@ public class ResizeVolumeCmd extends BaseAsyncCmd implements UserCmd {
description = "new disk offering id")
private Long newDiskOfferingId;
+ @Parameter(name = ApiConstants.AUTO_MIGRATE, type = CommandType.BOOLEAN, required = false,
+ description = "Flag to allow automatic migration of the volume to another suitable storage pool that accommodates the new size", since = "4.20.1")
+ private Boolean autoMigrate;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -129,6 +133,10 @@ public class ResizeVolumeCmd extends BaseAsyncCmd implements UserCmd {
return newDiskOfferingId;
}
+ public boolean getAutoMigrate() {
+ return autoMigrate == null ? false : autoMigrate;
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
diff --git a/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java b/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java
index 1c3edad886b..e1bb10f5d26 100644
--- a/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java
+++ b/engine/components-api/src/main/java/com/cloud/capacity/CapacityManager.java
@@ -40,6 +40,7 @@ public interface CapacityManager {
static final String StorageCapacityDisableThresholdCK = "pool.storage.capacity.disablethreshold";
static final String StorageOverprovisioningFactorCK = "storage.overprovisioning.factor";
static final String StorageAllocatedCapacityDisableThresholdCK = "pool.storage.allocated.capacity.disablethreshold";
+ static final String StorageAllocatedCapacityDisableThresholdForVolumeResizeCK = "pool.storage.allocated.resize.capacity.disablethreshold";
static final ConfigKey CpuOverprovisioningFactor =
new ConfigKey<>(
@@ -118,6 +119,17 @@ public interface CapacityManager {
"Percentage (as a value between 0 and 1) of secondary storage capacity threshold.",
true);
+ static final ConfigKey StorageAllocatedCapacityDisableThresholdForVolumeSize =
+ new ConfigKey<>(
+ ConfigKey.CATEGORY_ALERT,
+ Double.class,
+ StorageAllocatedCapacityDisableThresholdForVolumeResizeCK,
+ "0.90",
+ "Percentage (as a value between 0 and 1) of allocated storage utilization above which allocators will disable using the pool for volume resize. " +
+ "This is applicable only when volume.resize.allowed.beyond.allocation is set to true.",
+ true,
+ ConfigKey.Scope.Zone);
+
public boolean releaseVmCapacity(VirtualMachine vm, boolean moveFromReserved, boolean moveToReservered, Long hostId);
void allocateVmCapacity(VirtualMachine vm, boolean fromLastHost);
diff --git a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
index c3909bc56b0..b5153668899 100644
--- a/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
+++ b/engine/components-api/src/main/java/com/cloud/storage/StorageManager.java
@@ -209,6 +209,11 @@ public interface StorageManager extends StorageService {
ConfigKey HEURISTICS_SCRIPT_TIMEOUT = new ConfigKey<>("Advanced", Long.class, "heuristics.script.timeout", "3000",
"The maximum runtime, in milliseconds, to execute the heuristic rule; if it is reached, a timeout will happen.", true);
+ ConfigKey AllowVolumeReSizeBeyondAllocation = new ConfigKey("Advanced", Boolean.class, "volume.resize.allowed.beyond.allocation", "false",
+ "Determines whether volume size can exceed the pool capacity allocation disable threshold (pool.storage.allocated.capacity.disablethreshold) " +
+ "when resize a volume upto resize capacity disable threshold (pool.storage.allocated.resize.capacity.disablethreshold)",
+ true, ConfigKey.Scope.Zone);
+
/**
* should we execute in sequence not involving any storages?
* @return tru if commands should execute in sequence
diff --git a/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java b/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
index 421c980b209..08f055ca3a3 100644
--- a/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
+++ b/server/src/main/java/com/cloud/capacity/CapacityManagerImpl.java
@@ -1254,6 +1254,7 @@ public class CapacityManagerImpl extends ManagerBase implements CapacityManager,
@Override
public ConfigKey>[] getConfigKeys() {
return new ConfigKey>[] {CpuOverprovisioningFactor, MemOverprovisioningFactor, StorageCapacityDisableThreshold, StorageOverprovisioningFactor,
- StorageAllocatedCapacityDisableThreshold, StorageOperationsExcludeCluster, ImageStoreNFSVersion, SecondaryStorageCapacityThreshold};
+ StorageAllocatedCapacityDisableThreshold, StorageOperationsExcludeCluster, ImageStoreNFSVersion, SecondaryStorageCapacityThreshold,
+ StorageAllocatedCapacityDisableThresholdForVolumeSize };
}
}
diff --git a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
index 4a9e03ba67e..31b9a7624ad 100644
--- a/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
+++ b/server/src/main/java/com/cloud/configuration/ConfigurationManagerImpl.java
@@ -591,6 +591,7 @@ public class ConfigurationManagerImpl extends ManagerBase implements Configurati
weightBasedParametersForValidation.add(Config.LocalStorageCapacityThreshold.key());
weightBasedParametersForValidation.add(CapacityManager.StorageAllocatedCapacityDisableThreshold.key());
weightBasedParametersForValidation.add(CapacityManager.StorageCapacityDisableThreshold.key());
+ weightBasedParametersForValidation.add(CapacityManager.StorageAllocatedCapacityDisableThresholdForVolumeSize.key());
weightBasedParametersForValidation.add(DeploymentClusterPlanner.ClusterCPUCapacityDisableThreshold.key());
weightBasedParametersForValidation.add(DeploymentClusterPlanner.ClusterMemoryCapacityDisableThreshold.key());
weightBasedParametersForValidation.add(Config.AgentLoadThreshold.key());
diff --git a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
index 2ed6be39b54..f2e03cddb7c 100644
--- a/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
+++ b/server/src/main/java/com/cloud/storage/StorageManagerImpl.java
@@ -3101,7 +3101,7 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
} else {
final StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId());
final long allocatedSizeWithTemplate = _capacityMgr.getAllocatedPoolCapacity(poolVO, null);
- return checkPoolforSpace(pool, allocatedSizeWithTemplate, totalAskingSize);
+ return checkPoolforSpace(pool, allocatedSizeWithTemplate, totalAskingSize, true);
}
}
@@ -3164,6 +3164,10 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
}
protected boolean checkPoolforSpace(StoragePool pool, long allocatedSizeWithTemplate, long totalAskingSize) {
+ return checkPoolforSpace(pool, allocatedSizeWithTemplate, totalAskingSize, false);
+ }
+
+ protected boolean checkPoolforSpace(StoragePool pool, long allocatedSizeWithTemplate, long totalAskingSize, boolean forVolumeResize) {
// allocated space includes templates
StoragePoolVO poolVO = _storagePoolDao.findById(pool.getId());
@@ -3196,10 +3200,22 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
if (usedPercentage > storageAllocatedThreshold) {
if (logger.isDebugEnabled()) {
logger.debug("Insufficient un-allocated capacity on: " + pool.getId() + " for storage allocation since its allocated percentage: " + usedPercentage
- + " has crossed the allocated pool.storage.allocated.capacity.disablethreshold: " + storageAllocatedThreshold + ", skipping this pool");
+ + " has crossed the allocated pool.storage.allocated.capacity.disablethreshold: " + storageAllocatedThreshold);
+ }
+ if (!forVolumeResize) {
+ return false;
+ }
+ if (!AllowVolumeReSizeBeyondAllocation.valueIn(pool.getDataCenterId())) {
+ logger.debug(String.format("Skipping the pool %s as %s is false", pool, AllowVolumeReSizeBeyondAllocation.key()));
+ return false;
}
- return false;
+ double storageAllocatedThresholdForResize = CapacityManager.StorageAllocatedCapacityDisableThresholdForVolumeSize.valueIn(pool.getDataCenterId());
+ if (usedPercentage > storageAllocatedThresholdForResize) {
+ logger.debug(String.format("Skipping the pool %s since its allocated percentage: %s has crossed the allocated %s: %s",
+ pool, usedPercentage, CapacityManager.StorageAllocatedCapacityDisableThresholdForVolumeSize.key(), storageAllocatedThresholdForResize));
+ return false;
+ }
}
if (totalOverProvCapacity < (allocatedSizeWithTemplate + totalAskingSize)) {
@@ -4050,7 +4066,8 @@ public class StorageManagerImpl extends ManagerBase implements StorageManager, C
MountDisabledStoragePool,
VmwareCreateCloneFull,
VmwareAllowParallelExecution,
- DataStoreDownloadFollowRedirects
+ DataStoreDownloadFollowRedirects,
+ AllowVolumeReSizeBeyondAllocation
};
}
diff --git a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
index cb859f2dde9..b12575a8a08 100644
--- a/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
+++ b/server/src/main/java/com/cloud/storage/VolumeApiServiceImpl.java
@@ -1093,6 +1093,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
Long newMaxIops = cmd.getMaxIops();
Integer newHypervisorSnapshotReserve = null;
boolean shrinkOk = cmd.isShrinkOk();
+ boolean autoMigrateVolume = cmd.getAutoMigrate();
VolumeVO volume = _volsDao.findById(cmd.getEntityId());
if (volume == null) {
@@ -1154,8 +1155,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
newSize = volume.getSize();
}
- newMinIops = cmd.getMinIops();
-
if (newMinIops != null) {
if (!volume.getVolumeType().equals(Volume.Type.ROOT) && (diskOffering.isCustomizedIops() == null || !diskOffering.isCustomizedIops())) {
throw new InvalidParameterValueException("The current disk offering does not support customization of the 'Min IOPS' parameter.");
@@ -1165,8 +1164,6 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
newMinIops = volume.getMinIops();
}
- newMaxIops = cmd.getMaxIops();
-
if (newMaxIops != null) {
if (!volume.getVolumeType().equals(Volume.Type.ROOT) && (diskOffering.isCustomizedIops() == null || !diskOffering.isCustomizedIops())) {
throw new InvalidParameterValueException("The current disk offering does not support customization of the 'Max IOPS' parameter.");
@@ -1288,6 +1285,54 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
return volume;
}
+ Long newDiskOfferingId = newDiskOffering != null ? newDiskOffering.getId() : diskOffering.getId();
+
+ boolean volumeMigrateRequired = false;
+ List extends StoragePool> suitableStoragePoolsWithEnoughSpace = null;
+ StoragePoolVO storagePool = _storagePoolDao.findById(volume.getPoolId());
+ if (!storageMgr.storagePoolHasEnoughSpaceForResize(storagePool, currentSize, newSize)) {
+ if (!autoMigrateVolume) {
+ throw new CloudRuntimeException(String.format("Failed to resize volume %s since the storage pool does not have enough space to accommodate new size for the volume %s, try with automigrate set to true in order to check in the other suitable pools for the new size and then migrate & resize volume there.", volume.getUuid(), volume.getName()));
+ }
+ Pair, List extends StoragePool>> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOfferingId, currentSize, newMinIops, newMaxIops, true, false);
+ List extends StoragePool> suitableStoragePools = poolsPair.second();
+ if (CollectionUtils.isEmpty(poolsPair.first()) && CollectionUtils.isEmpty(poolsPair.second())) {
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resize failed for volume ID: %s as no suitable pool(s) found for migrating to support new disk offering or new size", volume.getUuid()));
+ }
+ final Long newSizeFinal = newSize;
+ suitableStoragePoolsWithEnoughSpace = suitableStoragePools.stream().filter(pool -> storageMgr.storagePoolHasEnoughSpaceForResize(pool, 0L, newSizeFinal)).collect(Collectors.toList());
+ if (CollectionUtils.isEmpty(suitableStoragePoolsWithEnoughSpace)) {
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume resize failed for volume ID: %s as no suitable pool(s) with enough space found.", volume.getUuid()));
+ }
+ Collections.shuffle(suitableStoragePoolsWithEnoughSpace);
+ volumeMigrateRequired = true;
+ }
+
+ boolean volumeResizeRequired = false;
+ if (currentSize != newSize || !compareEqualsIncludingNullOrZero(newMaxIops, volume.getMaxIops()) || !compareEqualsIncludingNullOrZero(newMinIops, volume.getMinIops())) {
+ volumeResizeRequired = true;
+ }
+ if (!volumeMigrateRequired && !volumeResizeRequired && newDiskOffering != null) {
+ _volsDao.updateDiskOffering(volume.getId(), newDiskOffering.getId());
+ volume = _volsDao.findById(volume.getId());
+ updateStorageWithTheNewDiskOffering(volume, newDiskOffering);
+
+ return volume;
+ }
+
+ if (volumeMigrateRequired) {
+ MigrateVolumeCmd migrateVolumeCmd = new MigrateVolumeCmd(volume.getId(), suitableStoragePoolsWithEnoughSpace.get(0).getId(), newDiskOfferingId, true);
+ try {
+ Volume result = migrateVolume(migrateVolumeCmd);
+ volume = (result != null) ? _volsDao.findById(result.getId()) : null;
+ if (volume == null) {
+ throw new CloudRuntimeException(String.format("Volume resize operation failed for volume ID: %s as migration failed to storage pool %s accommodating new size", volume.getUuid(), suitableStoragePoolsWithEnoughSpace.get(0).getId()));
+ }
+ } catch (Exception e) {
+ throw new CloudRuntimeException(String.format("Volume resize operation failed for volume ID: %s as migration failed to storage pool %s accommodating new size", volume.getUuid(), suitableStoragePoolsWithEnoughSpace.get(0).getId()));
+ }
+ }
+
UserVmVO userVm = _userVmDao.findById(volume.getInstanceId());
if (userVm != null) {
@@ -1973,6 +2018,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
public Volume changeDiskOfferingForVolume(ChangeOfferingForVolumeCmd cmd) throws ResourceAllocationException {
Long newSize = cmd.getSize();
Long newMinIops = cmd.getMinIops();
+
Long newMaxIops = cmd.getMaxIops();
Long newDiskOfferingId = cmd.getNewDiskOfferingId();
boolean shrinkOk = cmd.isShrinkOk();
@@ -2055,7 +2101,7 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
StoragePoolVO existingStoragePool = _storagePoolDao.findById(volume.getPoolId());
- Pair, List extends StoragePool>> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOffering.getId(), newSize, newMinIops, newMaxIops, true, false);
+ Pair, List extends StoragePool>> poolsPair = managementService.listStoragePoolsForSystemMigrationOfVolume(volume.getId(), newDiskOffering.getId(), currentSize, newMinIops, newMaxIops, true, false);
List extends StoragePool> suitableStoragePools = poolsPair.second();
if (!suitableStoragePools.stream().anyMatch(p -> (p.getId() == existingStoragePool.getId()))) {
@@ -2077,10 +2123,16 @@ public class VolumeApiServiceImpl extends ManagerBase implements VolumeApiServic
if (CollectionUtils.isEmpty(poolsPair.first()) && CollectionUtils.isEmpty(poolsPair.second())) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume change offering operation failed for volume ID: %s as no suitable pool(s) found for migrating to support new disk offering", volume.getUuid()));
}
- Collections.shuffle(suitableStoragePools);
- MigrateVolumeCmd migrateVolumeCmd = new MigrateVolumeCmd(volume.getId(), suitableStoragePools.get(0).getId(), newDiskOffering.getId(), true);
+ final Long newSizeFinal = newSize;
+ List extends StoragePool> suitableStoragePoolsWithEnoughSpace = suitableStoragePools.stream().filter(pool -> storageMgr.storagePoolHasEnoughSpaceForResize(pool, 0L, newSizeFinal)).collect(Collectors.toList());
+ if (CollectionUtils.isEmpty(suitableStoragePoolsWithEnoughSpace)) {
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, String.format("Volume change offering operation failed for volume ID: %s as no suitable pool(s) with enough space found for volume migration.", volume.getUuid()));
+ }
+ Collections.shuffle(suitableStoragePoolsWithEnoughSpace);
+ MigrateVolumeCmd migrateVolumeCmd = new MigrateVolumeCmd(volume.getId(), suitableStoragePoolsWithEnoughSpace.get(0).getId(), newDiskOffering.getId(), true);
try {
- volume = (VolumeVO) migrateVolume(migrateVolumeCmd);
+ Volume result = migrateVolume(migrateVolumeCmd);
+ volume = (result != null) ? _volsDao.findById(result.getId()) : null;
if (volume == null) {
throw new CloudRuntimeException(String.format("Volume change offering operation failed for volume ID: %s migration failed to storage pool %s", volume.getUuid(), suitableStoragePools.get(0).getId()));
}
diff --git a/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java b/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java
index fcbae4f339c..98a6e203ed7 100644
--- a/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java
+++ b/server/src/test/java/com/cloud/storage/StorageManagerImplTest.java
@@ -16,6 +16,7 @@
// under the License.
package com.cloud.storage;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -42,6 +43,7 @@ import com.cloud.vm.dao.VMInstanceDao;
import org.apache.cloudstack.api.ApiConstants;
import org.apache.cloudstack.framework.config.ConfigDepot;
+import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.resourcedetail.dao.DiskOfferingDetailsDao;
import org.apache.cloudstack.storage.command.CheckDataStoreStoragePolicyComplainceCommand;
@@ -756,4 +758,81 @@ public class StorageManagerImplTest {
String failureReason = storageManagerImpl.getStoragePoolMountFailureReason(error);
Assert.assertEquals(failureReason, "An incorrect mount option was specified");
}
+
+ private void overrideDefaultConfigValue(final ConfigKey configKey, final String name, final Object o) throws IllegalAccessException, NoSuchFieldException {
+ Field f = ConfigKey.class.getDeclaredField(name);
+ f.setAccessible(true);
+ f.set(configKey, o);
+ }
+
+ private Long testCheckPoolforSpaceForResizeSetup(StoragePoolVO pool, Long allocatedSizeWithTemplate) {
+ Long poolId = 10L;
+ Long zoneId = 2L;
+
+ Long capacityBytes = (long) (allocatedSizeWithTemplate / Double.valueOf(CapacityManager.StorageAllocatedCapacityDisableThreshold.defaultValue())
+ / Double.valueOf(CapacityManager.StorageOverprovisioningFactor.defaultValue()));
+ Long maxAllocatedSizeForResize = (long) (capacityBytes * Double.valueOf(CapacityManager.StorageOverprovisioningFactor.defaultValue())
+ * Double.valueOf(CapacityManager.StorageAllocatedCapacityDisableThresholdForVolumeSize.defaultValue()));
+
+ System.out.println("maxAllocatedSizeForResize = " + maxAllocatedSizeForResize);
+ System.out.println("allocatedSizeWithTemplate = " + allocatedSizeWithTemplate);
+
+ Mockito.when(pool.getId()).thenReturn(poolId);
+ Mockito.when(pool.getCapacityBytes()).thenReturn(capacityBytes);
+ Mockito.when(pool.getDataCenterId()).thenReturn(zoneId);
+ Mockito.when(storagePoolDao.findById(poolId)).thenReturn(pool);
+ Mockito.when(pool.getPoolType()).thenReturn(Storage.StoragePoolType.NetworkFilesystem);
+
+ return maxAllocatedSizeForResize - allocatedSizeWithTemplate;
+ }
+
+ @Test
+ public void testCheckPoolforSpaceForResize1() {
+ StoragePoolVO pool = Mockito.mock(StoragePoolVO.class);
+ Long allocatedSizeWithTemplate = 100L * 1024 * 1024 * 1024;
+
+ Long maxAskingSize = testCheckPoolforSpaceForResizeSetup(pool, allocatedSizeWithTemplate);
+ Long totalAskingSize = maxAskingSize / 2;
+
+ boolean result = storageManagerImpl.checkPoolforSpace(pool, allocatedSizeWithTemplate, totalAskingSize, false);
+ Assert.assertFalse(result);
+ }
+
+ @Test
+ public void testCheckPoolforSpaceForResize2() {
+ StoragePoolVO pool = Mockito.mock(StoragePoolVO.class);
+ Long allocatedSizeWithTemplate = 100L * 1024 * 1024 * 1024;
+
+ Long maxAskingSize = testCheckPoolforSpaceForResizeSetup(pool, allocatedSizeWithTemplate);
+ Long totalAskingSize = maxAskingSize / 2;
+
+ boolean result = storageManagerImpl.checkPoolforSpace(pool, allocatedSizeWithTemplate, totalAskingSize, true);
+ Assert.assertFalse(result);
+ }
+
+ @Test
+ public void testCheckPoolforSpaceForResize3() throws NoSuchFieldException, IllegalAccessException {
+ StoragePoolVO pool = Mockito.mock(StoragePoolVO.class);
+ Long allocatedSizeWithTemplate = 100L * 1024 * 1024 * 1024;
+
+ Long maxAskingSize = testCheckPoolforSpaceForResizeSetup(pool, allocatedSizeWithTemplate);
+ Long totalAskingSize = maxAskingSize + 1;
+ overrideDefaultConfigValue(StorageManagerImpl.AllowVolumeReSizeBeyondAllocation, "_defaultValue", "true");
+
+ boolean result = storageManagerImpl.checkPoolforSpace(pool, allocatedSizeWithTemplate, totalAskingSize, true);
+ Assert.assertFalse(result);
+ }
+
+ @Test
+ public void testCheckPoolforSpaceForResize4() throws NoSuchFieldException, IllegalAccessException {
+ StoragePoolVO pool = Mockito.mock(StoragePoolVO.class);
+ Long allocatedSizeWithTemplate = 100L * 1024 * 1024 * 1024;
+
+ Long maxAskingSize = testCheckPoolforSpaceForResizeSetup(pool, allocatedSizeWithTemplate);
+ Long totalAskingSize = maxAskingSize / 2;
+ overrideDefaultConfigValue(StorageManagerImpl.AllowVolumeReSizeBeyondAllocation, "_defaultValue", "true");
+
+ boolean result = storageManagerImpl.checkPoolforSpace(pool, allocatedSizeWithTemplate, totalAskingSize, true);
+ Assert.assertTrue(result);
+ }
}
diff --git a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java
index 80835891327..666324d4ed2 100644
--- a/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java
+++ b/server/src/test/java/com/cloud/storage/VolumeApiServiceImplTest.java
@@ -19,10 +19,13 @@ package com.cloud.storage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.ArgumentMatchers.nullable;
import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
@@ -38,14 +41,17 @@ import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutionException;
+import com.cloud.server.ManagementService;
import org.apache.cloudstack.acl.ControlledEntity;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.command.user.volume.CheckAndRepairVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.CreateVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd;
import org.apache.cloudstack.api.command.user.volume.MigrateVolumeCmd;
+import org.apache.cloudstack.api.command.user.volume.ResizeVolumeCmd;
import org.apache.cloudstack.backup.dao.BackupDao;
import org.apache.cloudstack.context.CallContext;
+import org.apache.cloudstack.engine.orchestration.service.VolumeOrchestrationService;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStore;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.engine.subsystem.api.storage.PrimaryDataStore;
@@ -85,6 +91,7 @@ import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import com.cloud.api.query.dao.ServiceOfferingJoinDao;
+import com.cloud.configuration.ConfigurationManager;
import com.cloud.configuration.Resource;
import com.cloud.configuration.Resource.ResourceType;
import com.cloud.dc.DataCenterVO;
@@ -210,6 +217,8 @@ public class VolumeApiServiceImplTest {
private StoragePool storagePoolMock;
private long storagePoolMockId = 1;
@Mock
+ private DiskOfferingVO diskOfferingMock;
+ @Mock
private DiskOfferingVO newDiskOfferingMock;
@Mock
@@ -238,10 +247,20 @@ public class VolumeApiServiceImplTest {
@Mock
private StorageManager storageMgr;
+ @Mock
+ private ConfigurationManager _configMgr;
+
+ @Mock
+ private VolumeOrchestrationService _volumeMgr;
+
+ @Mock
+ private ManagementService managementService;
+
private long accountMockId = 456l;
private long volumeMockId = 12313l;
private long vmInstanceMockId = 1123l;
private long volumeSizeMock = 456789921939l;
+ private long newVolumeSizeMock = 456789930000l;
private static long imageStoreId = 10L;
private String projectMockUuid = "projectUuid";
@@ -250,6 +269,7 @@ public class VolumeApiServiceImplTest {
private long projectMockAccountId = 132329390L;
private long diskOfferingMockId = 100203L;
+ private long newDiskOfferingMockId = 100204L;
private long offeringMockId = 31902L;
@@ -1820,4 +1840,92 @@ public class VolumeApiServiceImplTest {
volumeApiServiceImpl.validationsForCheckVolumeOperation(volume);
}
+
+ private void testResizeVolumeSetup() throws ExecutionException, InterruptedException {
+ Long poolId = 11L;
+
+ when(volumeDaoMock.findById(volumeMockId)).thenReturn(volumeVoMock);
+ when(volumeVoMock.getId()).thenReturn(volumeMockId);
+ when(volumeDaoMock.getHypervisorType(volumeMockId)).thenReturn(HypervisorType.KVM);
+ when(volumeVoMock.getState()).thenReturn(Volume.State.Ready);
+ when(volumeVoMock.getDiskOfferingId()).thenReturn(diskOfferingMockId);
+ when(_diskOfferingDao.findById(diskOfferingMockId)).thenReturn(diskOfferingMock);
+ when(_diskOfferingDao.findById(newDiskOfferingMockId)).thenReturn(newDiskOfferingMock);
+ when(newDiskOfferingMock.getRemoved()).thenReturn(null);
+ when(diskOfferingMock.getDiskSizeStrictness()).thenReturn(false);
+ when(newDiskOfferingMock.getDiskSizeStrictness()).thenReturn(false);
+ when(volumeVoMock.getInstanceId()).thenReturn(null);
+ when(volumeVoMock.getVolumeType()).thenReturn(Type.DATADISK);
+ when(newDiskOfferingMock.getDiskSize()).thenReturn(newVolumeSizeMock);
+
+ VolumeInfo volInfo = Mockito.mock(VolumeInfo.class);
+ when(volumeDataFactoryMock.getVolume(volumeMockId)).thenReturn(volInfo);
+ DataStore dataStore = Mockito.mock(DataStore.class);
+ when((volInfo.getDataStore())).thenReturn(dataStore);
+
+ when(volumeVoMock.getPoolId()).thenReturn(poolId);
+ StoragePoolVO storagePool = Mockito.mock(StoragePoolVO.class);
+ when(primaryDataStoreDaoMock.findById(poolId)).thenReturn(storagePool);
+
+ Mockito.lenient().doReturn(asyncCallFutureVolumeapiResultMock).when(volumeServiceMock).resize(any(VolumeInfo.class));
+ Mockito.doReturn(Mockito.mock(VolumeApiResult.class)).when(asyncCallFutureVolumeapiResultMock).get();
+ }
+
+ @Test
+ public void testResizeVolumeWithEnoughCapacity() throws ResourceAllocationException, ExecutionException, InterruptedException {
+ ResizeVolumeCmd cmd = new ResizeVolumeCmd();
+ ReflectionTestUtils.setField(cmd, "id", volumeMockId);
+ ReflectionTestUtils.setField(cmd, "newDiskOfferingId", newDiskOfferingMockId);
+
+ testResizeVolumeSetup();
+
+ when(storageMgr.storagePoolHasEnoughSpaceForResize(any(), nullable(Long.class), nullable(Long.class))).thenReturn(true);
+
+ try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) {
+ volumeApiServiceImpl.resizeVolume(cmd);
+
+ verify(volumeServiceMock).resize(any(VolumeInfo.class));
+ }
+ }
+
+ @Test
+ public void testResizeVolumeWithoutEnoughCapacity() throws ResourceAllocationException, ExecutionException, InterruptedException {
+ ResizeVolumeCmd cmd = new ResizeVolumeCmd();
+ ReflectionTestUtils.setField(cmd, "id", volumeMockId);
+ ReflectionTestUtils.setField(cmd, "newDiskOfferingId", newDiskOfferingMockId);
+ ReflectionTestUtils.setField(cmd, "autoMigrate", true);
+
+ testResizeVolumeSetup();
+
+ when(storageMgr.storagePoolHasEnoughSpaceForResize(any(), nullable(Long.class), nullable(Long.class))).thenReturn(false).thenReturn(true);
+ StoragePoolVO suitableStoragePool = Mockito.mock(StoragePoolVO.class);
+ Pair, List extends StoragePool>> poolsPair = new Pair<>(Arrays.asList(suitableStoragePool), Arrays.asList(suitableStoragePool));
+ when(managementService.listStoragePoolsForSystemMigrationOfVolume(anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyBoolean(), anyBoolean())).thenReturn(poolsPair);
+ doReturn(volumeInfoMock).when(volumeApiServiceImpl).migrateVolume(any());
+ when(volumeInfoMock.getId()).thenReturn(volumeMockId);
+
+ try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) {
+ volumeApiServiceImpl.resizeVolume(cmd);
+
+ verify(volumeApiServiceImpl).migrateVolume(any());
+ verify(volumeServiceMock).resize(any(VolumeInfo.class));
+ }
+ }
+
+ @Test
+ public void testResizeVolumeInAllocateState() throws ResourceAllocationException, ExecutionException, InterruptedException {
+ ResizeVolumeCmd cmd = new ResizeVolumeCmd();
+ ReflectionTestUtils.setField(cmd, "id", volumeMockId);
+ ReflectionTestUtils.setField(cmd, "newDiskOfferingId", newDiskOfferingMockId);
+
+ testResizeVolumeSetup();
+
+ when(volumeVoMock.getState()).thenReturn(Volume.State.Allocated);
+
+ try (MockedStatic ignored = Mockito.mockStatic(UsageEventUtils.class)) {
+ volumeApiServiceImpl.resizeVolume(cmd);
+
+ verify(volumeServiceMock, times(0)).resize(any(VolumeInfo.class));
+ }
+ }
}
diff --git a/ui/src/views/storage/ResizeVolume.vue b/ui/src/views/storage/ResizeVolume.vue
index 38a7ea5cb23..8d5bd28917a 100644
--- a/ui/src/views/storage/ResizeVolume.vue
+++ b/ui/src/views/storage/ResizeVolume.vue
@@ -47,6 +47,15 @@
:checked="shrinkOk"
@change="val => { shrinkOk = val }"/>
+
+
+
+
+ { autoMigrate = val }"/>
+
-