This is an automated email from the ASF dual-hosted git repository.
winterhazel pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/cloudstack.git
The following commit(s) were added to refs/heads/main by this push:
new 96ca1b2a7c1 Add option to control MAC address reuse for VR public NICs
(#13001)
96ca1b2a7c1 is described below
commit 96ca1b2a7c1c80bc5b284e008e9c96d49a276b4a
Author: Bernardo De Marco Gonçalves <[email protected]>
AuthorDate: Wed May 6 13:41:11 2026 -0300
Add option to control MAC address reuse for VR public NICs (#13001)
---
api/src/main/java/com/cloud/network/Network.java | 2 +
.../java/com/cloud/network/NetworkProfile.java | 5 +
api/src/main/java/com/cloud/network/vpc/Vpc.java | 2 +
.../java/com/cloud/network/vpc/VpcService.java | 4 +-
.../org/apache/cloudstack/api/ApiConstants.java | 7 +
.../api/command/user/network/CreateNetworkCmd.java | 9 +
.../api/command/user/network/UpdateNetworkCmd.java | 9 +
.../api/command/user/vpc/CreateVPCCmd.java | 9 +
.../api/command/user/vpc/UpdateVPCCmd.java | 9 +
.../cloudstack/api/response/NetworkResponse.java | 8 +
.../cloudstack/api/response/VpcResponse.java | 8 +
.../api/command/user/vpc/UpdateVPCCmdTest.java | 2 +-
.../service/NetworkOrchestrationService.java | 5 +
.../engine/orchestration/NetworkOrchestrator.java | 24 +-
.../main/java/com/cloud/network/dao/NetworkVO.java | 13 +
.../src/main/java/com/cloud/network/vpc/VpcVO.java | 12 +
.../resources/META-INF/db/schema-42210to42300.sql | 4 +
.../main/java/com/cloud/api/ApiResponseHelper.java | 8 +
.../cloud/network/NetworkMigrationManagerImpl.java | 2 +-
.../java/com/cloud/network/NetworkServiceImpl.java | 31 ++-
.../cloud/network/router/NetworkHelperImpl.java | 122 ++++++---
.../java/com/cloud/network/vpc/VpcManagerImpl.java | 16 +-
.../deployment/RouterDeploymentDefinition.java | 6 +-
.../deployment/VpcRouterDeploymentDefinition.java | 7 +-
.../com/cloud/network/NetworkServiceImplTest.java | 50 +++-
.../network/router/NetworkHelperImplTest.java | 288 ++++++++++++++++++++-
.../com/cloud/network/vpc/VpcManagerImplTest.java | 10 +-
.../java/com/cloud/vpc/MockNetworkManagerImpl.java | 10 +-
ui/public/locales/en.json | 1 +
ui/public/locales/pt_BR.json | 1 +
ui/src/config/section/network.js | 30 ++-
ui/src/views/AutogenView.vue | 5 +
ui/src/views/network/CreateIsolatedNetworkForm.vue | 22 +-
ui/src/views/network/CreateVpc.vue | 20 +-
ui/src/views/network/UpdateNetwork.vue | 28 +-
35 files changed, 691 insertions(+), 98 deletions(-)
diff --git a/api/src/main/java/com/cloud/network/Network.java
b/api/src/main/java/com/cloud/network/Network.java
index 0846306f70f..e41eb880ffd 100644
--- a/api/src/main/java/com/cloud/network/Network.java
+++ b/api/src/main/java/com/cloud/network/Network.java
@@ -510,4 +510,6 @@ public interface Network extends ControlledEntity,
StateObject<Network.State>, I
Integer getPrivateMtu();
Integer getNetworkCidrSize();
+
+ boolean getKeepMacAddressOnPublicNic();
}
diff --git a/api/src/main/java/com/cloud/network/NetworkProfile.java
b/api/src/main/java/com/cloud/network/NetworkProfile.java
index 2e8efb48930..d690344a0e3 100644
--- a/api/src/main/java/com/cloud/network/NetworkProfile.java
+++ b/api/src/main/java/com/cloud/network/NetworkProfile.java
@@ -385,6 +385,11 @@ public class NetworkProfile implements Network {
return networkCidrSize;
}
+ @Override
+ public boolean getKeepMacAddressOnPublicNic() {
+ return true;
+ }
+
@Override
public String toString() {
return String.format("NetworkProfile %s",
diff --git a/api/src/main/java/com/cloud/network/vpc/Vpc.java
b/api/src/main/java/com/cloud/network/vpc/Vpc.java
index b94089d2d43..a0686e2bf7d 100644
--- a/api/src/main/java/com/cloud/network/vpc/Vpc.java
+++ b/api/src/main/java/com/cloud/network/vpc/Vpc.java
@@ -107,4 +107,6 @@ public interface Vpc extends ControlledEntity, Identity,
InternalIdentity {
String getIp6Dns2();
boolean useRouterIpAsResolver();
+
+ boolean getKeepMacAddressOnPublicNic();
}
diff --git a/api/src/main/java/com/cloud/network/vpc/VpcService.java
b/api/src/main/java/com/cloud/network/vpc/VpcService.java
index c1546609d2b..3d0ba43263f 100644
--- a/api/src/main/java/com/cloud/network/vpc/VpcService.java
+++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java
@@ -58,7 +58,7 @@ public interface VpcService {
*/
Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName,
String displayText, String cidr, String networkDomain,
String ip4Dns1, String ip4Dns2, String ip6Dns1, String
ip6Dns2, Boolean displayVpc, Integer publicMtu, Integer cidrSize,
- Long asNumber, List<Long> bgpPeerIds, Boolean
useVrIpResolver) throws ResourceAllocationException;
+ Long asNumber, List<Long> bgpPeerIds, Boolean
useVrIpResolver, boolean keepMacAddressOnPublicNic) throws
ResourceAllocationException;
/**
* Persists VPC record in the database
@@ -104,7 +104,7 @@ public interface VpcService {
* @throws ResourceUnavailableException if during restart some resources
may not be available
* @throws InsufficientCapacityException if for instance no address space,
compute or storage is sufficiently available
*/
- Vpc updateVpc(long vpcId, String vpcName, String displayText, String
customId, Boolean displayVpc, Integer mtu, String sourceNatIp) throws
ResourceUnavailableException, InsufficientCapacityException;
+ Vpc updateVpc(long vpcId, String vpcName, String displayText, String
customId, Boolean displayVpc, Integer mtu, String sourceNatIp, Boolean
keepMacAddressOnPublicNic) throws ResourceUnavailableException,
InsufficientCapacityException;
/**
* Lists VPC(s) based on the parameters passed to the API call
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 6fb7cb0612c..4d4ead277e5 100644
--- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
+++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java
@@ -1356,6 +1356,13 @@ public class ApiConstants {
public static final String OBJECT_STORAGE_LIMIT = "objectstoragelimit";
public static final String OBJECT_STORAGE_TOTAL = "objectstoragetotal";
+ public static final String KEEP_MAC_ADDRESS_ON_PUBLIC_NIC =
"keepmacaddressonpublicnic";
+
+ public static final String
PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC =
+ "Indicates whether to use the same MAC address for the public NIC
of VRs on the same network. If \"true\", when creating redundant routers or
recreating" +
+ " a VR, CloudStack will use the same MAC address for the
public NIC of all VRs. Otherwise, if \"false\", new public NICs will always
have " +
+ " a new MAC address.";
+
public static final String PARAMETER_DESCRIPTION_ACTIVATION_RULE = "Quota
tariff's activation rule. It can receive a JS script that results in either " +
"a boolean or a numeric value: if it results in a boolean value,
the tariff value will be applied according to the result; if it results in a
numeric value, the " +
"numeric value will be applied; if the result is neither a boolean
nor a numeric value, the tariff will not be applied. If the rule is not
informed, the tariff " +
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java
index cbf6df081b3..ee5b8568e83 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/user/network/CreateNetworkCmd.java
@@ -199,6 +199,11 @@ public class CreateNetworkCmd extends BaseCmd implements
UserCmd {
@Parameter(name=ApiConstants.AS_NUMBER, type=CommandType.LONG, since =
"4.20.0", description="the AS Number of the network")
private Long asNumber;
+ @Parameter(name = ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC,
+ description =
ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC,
+ type = CommandType.BOOLEAN, since = "4.23.0", authorized =
{RoleType.Admin})
+ private Boolean keepMacAddressOnPublicNic;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -286,6 +291,10 @@ public class CreateNetworkCmd extends BaseCmd implements
UserCmd {
return sourceNatIP;
}
+ public Boolean getKeepMacAddressOnPublicNic() {
+ return keepMacAddressOnPublicNic;
+ }
+
@Override
public boolean isDisplay() {
if(displayNetwork == null)
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java
index 2e638f1e2f7..7b6841d6097 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/user/network/UpdateNetworkCmd.java
@@ -105,6 +105,11 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd
implements UserCmd {
@Parameter(name = ApiConstants.SOURCE_NAT_IP, type = CommandType.STRING,
description = "IPV4 address to be assigned to the public interface of the
network router. This address must already be acquired for this network", since
= "4.19")
private String sourceNatIP;
+ @Parameter(name = ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC,
+ description =
ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC,
+ type = CommandType.BOOLEAN, since = "4.23.0", authorized =
{RoleType.Admin})
+ private Boolean keepMacAddressOnPublicNic;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -186,6 +191,10 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd
implements UserCmd {
return sourceNatIP;
}
+ public Boolean getKeepMacAddressOnPublicNic() {
+ return keepMacAddressOnPublicNic;
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java
index 2adbbd66408..86309d7f28a 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/CreateVPCCmd.java
@@ -130,6 +130,11 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd
implements UserCmd {
description="(optional) for NSX based VPCs: when set to true, use
the VR IP as nameserver, otherwise use DNS1 and DNS2")
private Boolean useVrIpResolver;
+ @Parameter(name = ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC,
+ description =
ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC,
+ type = CommandType.BOOLEAN, since = "4.23.0", authorized =
{RoleType.Admin})
+ private boolean keepMacAddressOnPublicNic = true;
+
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
// ///////////////////////////////////////////////////
@@ -214,6 +219,10 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd
implements UserCmd {
return BooleanUtils.toBoolean(useVrIpResolver);
}
+ public boolean getKeepMacAddressOnPublicNic() {
+ return keepMacAddressOnPublicNic;
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
diff --git
a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java
b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java
index f2327c9073f..6cfdb895977 100644
---
a/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java
+++
b/api/src/main/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmd.java
@@ -69,6 +69,11 @@ public class UpdateVPCCmd extends BaseAsyncCustomIdCmd
implements UserCmd {
since = "4.19")
private String sourceNatIP;
+ @Parameter(name = ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC,
+ description =
ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC,
+ type = CommandType.BOOLEAN, since = "4.23.0", authorized =
{RoleType.Admin})
+ private Boolean keepMacAddressOnPublicNic;
+
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@@ -97,6 +102,10 @@ public class UpdateVPCCmd extends BaseAsyncCustomIdCmd
implements UserCmd {
return sourceNatIP;
}
+ public Boolean getKeepMacAddressOnPublicNic() {
+ return keepMacAddressOnPublicNic;
+ }
+
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
diff --git
a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java
b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java
index 7c4b733a80f..3a3663af255 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkResponse.java
@@ -331,6 +331,10 @@ public class NetworkResponse extends
BaseResponseWithAssociatedNetwork implement
@Param(description = "The BGP peers for the network", since = "4.20.0")
private Set<BgpPeerResponse> bgpPeers;
+ @SerializedName(ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC)
+ @Param(description =
ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, since =
"4.23.0")
+ private Boolean keepMacAddressOnPublicNic;
+
public NetworkResponse() {}
public Boolean getDisplayNetwork() {
@@ -702,4 +706,8 @@ public class NetworkResponse extends
BaseResponseWithAssociatedNetwork implement
public void setIpv6Dns2(String ipv6Dns2) {
this.ipv6Dns2 = ipv6Dns2;
}
+
+ public void setKeepMacAddressOnPublicNic(Boolean
keepMacAddressOnPublicNic) {
+ this.keepMacAddressOnPublicNic = keepMacAddressOnPublicNic;
+ }
}
diff --git
a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java
b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java
index acfabb11350..34d50d5b9f9 100644
--- a/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java
+++ b/api/src/main/java/org/apache/cloudstack/api/response/VpcResponse.java
@@ -185,6 +185,10 @@ public class VpcResponse extends
BaseResponseWithAnnotations implements Controll
@Param(description = "The BGP peers for the VPC", since = "4.20.0")
private Set<BgpPeerResponse> bgpPeers;
+ @SerializedName(ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC)
+ @Param(description =
ApiConstants.PARAMETER_DESCRIPTION_KEEP_MAC_ADDRESS_ON_PUBLIC_NIC, since =
"4.23.0")
+ private Boolean keepMacAddressOnPublicNic;
+
public void setId(final String id) {
this.id = id;
}
@@ -366,4 +370,8 @@ public class VpcResponse extends
BaseResponseWithAnnotations implements Controll
}
this.bgpPeers.add(bgpPeer);
}
+
+ public void setKeepMacAddressOnPublicNic(Boolean
keepMacAddressOnPublicNic) {
+ this.keepMacAddressOnPublicNic = keepMacAddressOnPublicNic;
+ }
}
diff --git
a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java
b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java
index acb2dc68597..9d56cffd671 100644
---
a/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java
+++
b/api/src/test/java/org/apache/cloudstack/api/command/user/vpc/UpdateVPCCmdTest.java
@@ -93,7 +93,7 @@ public class UpdateVPCCmdTest extends TestCase {
responseGenerator = Mockito.mock(ResponseGenerator.class);
cmd._responseGenerator = responseGenerator;
Mockito.verify(_vpcService,
Mockito.times(0)).updateVpc(Mockito.anyLong(), Mockito.anyString(),
Mockito.anyString(),
- Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt(),
Mockito.anyString());
+ Mockito.anyString(), Mockito.anyBoolean(), Mockito.anyInt(),
Mockito.anyString(), Mockito.anyBoolean());
}
}
diff --git
a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
index b7b548fb940..030c1277efe 100644
---
a/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
+++
b/engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
@@ -220,6 +220,11 @@ public interface NetworkOrchestrationService {
Boolean displayNetworkEnabled, String
isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String
routerIp, String routerIpv6,
String ip4Dns1, String ip4Dns2, String ip6Dns1,
String ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs, Integer networkCidrSize)
throws ConcurrentOperationException, InsufficientCapacityException,
ResourceAllocationException;
+ Network createGuestNetwork(long networkOfferingId, String name, String
displayText, String gateway, String cidr, String vlanId, boolean
bypassVlanOverlapCheck, String networkDomain, Account owner,
+ Long domainId, PhysicalNetwork physicalNetwork,
long zoneId, ACLType aclType, Boolean subdomainAccess, Long vpcId, String
ip6Gateway, String ip6Cidr,
+ Boolean displayNetworkEnabled, String
isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String
routerIp, String routerIpv6,
+ String ip4Dns1, String ip4Dns2, String ip6Dns1,
String ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs, Integer networkCidrSize,
boolean keepMacAddressOnPublicNic) throws ConcurrentOperationException,
InsufficientCapacityException, ResourceAllocationException;
+
UserDataServiceProvider getPasswordResetProvider(Network network);
UserDataServiceProvider getSSHKeyResetProvider(Network network);
diff --git
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index 7d455e7d6dc..4262ee701aa 100644
---
a/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++
b/engine/orchestration/src/main/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -863,6 +863,7 @@ public class NetworkOrchestrator extends ManagerBase
implements NetworkOrchestra
vpcId, offering.isRedundantRouter(),
predefined.getExternalId());
vo.setDisplayNetwork(isDisplayNetworkEnabled == null ||
isDisplayNetworkEnabled);
vo.setStrechedL2Network(offering.isSupportingStrechedL2());
+
vo.setKeepMacAddressOnPublicNic(predefined.getKeepMacAddressOnPublicNic());
return vo;
}
@@ -2724,7 +2725,7 @@ public class NetworkOrchestrator extends ManagerBase
implements NetworkOrchestra
return createGuestNetwork(networkOfferingId, name, displayText,
gateway, cidr, vlanId,
bypassVlanOverlapCheck, null, owner, null, pNtwk,
pNtwk.getDataCenterId(), ACLType.Account, null,
vpcId, null, null, true, null, null, null, true, null, null,
- null, null, null, null, null, null);
+ null, null, null, null, null, null, true);
}
@Override
@@ -2735,10 +2736,25 @@ public class NetworkOrchestrator extends ManagerBase
implements NetworkOrchestra
final Boolean isDisplayNetworkEnabled,
final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String
externalId,
String routerIp, String routerIpv6,
String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2,
Pair<Integer, Integer> vrIfaceMTUs,
Integer networkCidrSize) throws ConcurrentOperationException,
InsufficientCapacityException, ResourceAllocationException {
+ return createGuestNetwork(networkOfferingId, name, displayText,
gateway, cidr, vlanId, bypassVlanOverlapCheck,
+ networkDomain, owner, domainId, pNtwk, zoneId, aclType,
subdomainAccess, vpcId, ip6Gateway, ip6Cidr,
+ isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType,
externalId, false, routerIp, routerIpv6,
+ ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs,
networkCidrSize, true);
+ }
+
+ @Override
+ @DB
+ public Network createGuestNetwork(final long networkOfferingId, final
String name, final String displayText, final String gateway, final String cidr,
String vlanId,
+ boolean bypassVlanOverlapCheck, String
networkDomain, final Account owner, final Long domainId, final PhysicalNetwork
pNtwk,
+ final long zoneId, final ACLType
aclType, Boolean subdomainAccess, final Long vpcId, final String ip6Gateway,
final String ip6Cidr,
+ final Boolean isDisplayNetworkEnabled,
final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String
externalId,
+ String routerIp, String routerIpv6,
String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2,
+ Pair<Integer, Integer> vrIfaceMTUs,
Integer networkCidrSize, boolean keepMacAddressOnPublicNic) throws
ConcurrentOperationException, InsufficientCapacityException,
ResourceAllocationException {
// create Isolated/Shared/L2 network
return createGuestNetwork(networkOfferingId, name, displayText,
gateway, cidr, vlanId, bypassVlanOverlapCheck,
networkDomain, owner, domainId, pNtwk, zoneId, aclType,
subdomainAccess, vpcId, ip6Gateway, ip6Cidr,
- isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType,
externalId, false, routerIp, routerIpv6, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2,
vrIfaceMTUs, networkCidrSize);
+ isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType,
externalId, false, routerIp, routerIpv6,
+ ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, vrIfaceMTUs,
networkCidrSize, keepMacAddressOnPublicNic);
}
@DB
@@ -2747,7 +2763,8 @@ public class NetworkOrchestrator extends ManagerBase
implements NetworkOrchestra
final long zoneId, final ACLType
aclType, Boolean subdomainAccess, final Long vpcId, final String ip6Gateway,
final String ip6Cidr,
final Boolean isDisplayNetworkEnabled,
final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String
externalId,
final Boolean isPrivateNetwork, String
routerIp, String routerIpv6, final String ip4Dns1, final String ip4Dns2,
- final String ip6Dns1, final String
ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs, Integer networkCidrSize) throws
ConcurrentOperationException, InsufficientCapacityException,
ResourceAllocationException {
+ final String ip6Dns1, final String
ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs, Integer networkCidrSize,
+ boolean keepMacAddressOnPublicNic)
throws ConcurrentOperationException, InsufficientCapacityException,
ResourceAllocationException {
final NetworkOfferingVO ntwkOff =
_networkOfferingDao.findById(networkOfferingId);
final DataCenterVO zone = _dcDao.findById(zoneId);
@@ -3095,6 +3112,7 @@ public class NetworkOrchestrator extends ManagerBase
implements NetworkOrchestra
}
}
userNetwork.setNetworkCidrSize(networkCidrSize);
+
userNetwork.setKeepMacAddressOnPublicNic(keepMacAddressOnPublicNic);
final List<? extends Network> networks = setupNetwork(owner,
ntwkOff, userNetwork, plan, name, displayText, true, domainId, aclType,
subdomainAccessFinal, vpcId,
isDisplayNetworkEnabled);
Network network;
diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java
b/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java
index 02abaacd854..f2572ba91c2 100644
--- a/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java
+++ b/engine/schema/src/main/java/com/cloud/network/dao/NetworkVO.java
@@ -203,9 +203,13 @@ public class NetworkVO implements Network {
@Column(name = "private_mtu")
Integer privateMtu;
+ @Column(name = "keep_mac_address_on_public_nic")
+ private boolean keepMacAddressOnPublicNic = true;
+
@Transient
Integer networkCidrSize;
+
public NetworkVO() {
uuid = UUID.randomUUID().toString();
}
@@ -773,4 +777,13 @@ public class NetworkVO implements Network {
public void setNetworkCidrSize(Integer networkCidrSize) {
this.networkCidrSize = networkCidrSize;
}
+
+ @Override
+ public boolean getKeepMacAddressOnPublicNic() {
+ return keepMacAddressOnPublicNic;
+ }
+
+ public void setKeepMacAddressOnPublicNic(boolean
keepMacAddressOnPublicNic) {
+ this.keepMacAddressOnPublicNic = keepMacAddressOnPublicNic;
+ }
}
diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java
b/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java
index e942eadb8ff..742d3f2f82e 100644
--- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java
+++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcVO.java
@@ -108,6 +108,9 @@ public class VpcVO implements Vpc {
@Column(name = "use_router_ip_resolver")
boolean useRouterIpResolver = false;
+ @Column(name = "keep_mac_address_on_public_nic")
+ private boolean keepMacAddressOnPublicNic = true;
+
@Transient
boolean rollingRestart = false;
@@ -321,4 +324,13 @@ public class VpcVO implements Vpc {
public void setUseRouterIpResolver(boolean useRouterIpResolver) {
this.useRouterIpResolver = useRouterIpResolver;
}
+
+ @Override
+ public boolean getKeepMacAddressOnPublicNic() {
+ return keepMacAddressOnPublicNic;
+ }
+
+ public void setKeepMacAddressOnPublicNic(boolean
keepMacAddressOnPublicNic) {
+ this.keepMacAddressOnPublicNic = keepMacAddressOnPublicNic;
+ }
}
diff --git
a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
index c0feb06e76a..c99f798d3d5 100644
--- a/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
+++ b/engine/schema/src/main/resources/META-INF/db/schema-42210to42300.sql
@@ -127,3 +127,7 @@ CREATE TABLE IF NOT EXISTS
`cloud_usage`.`quota_tariff_usage` (
PRIMARY KEY (`id`),
CONSTRAINT `fk_quota_tariff_usage__tariff_id` FOREIGN KEY (`tariff_id`)
REFERENCES `cloud_usage`.`quota_tariff` (`id`),
CONSTRAINT `fk_quota_tariff_usage__quota_usage_id` FOREIGN KEY
(`quota_usage_id`) REFERENCES `cloud_usage`.`quota_usage` (`id`));
+
+-- Add the 'keep_mac_address_on_public_nic' column to the 'cloud.networks' and
'cloud.vpc' tables
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.networks',
'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1');
+CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc',
'keep_mac_address_on_public_nic', 'TINYINT(1) NOT NULL DEFAULT 1');
diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
index 11ac3d639e9..a8551b4c669 100644
--- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java
+++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java
@@ -2890,6 +2890,11 @@ public class ApiResponseHelper implements
ResponseGenerator, ResourceIdSupport {
}
}
+ if (CallContext.current().getCallingAccount().getType() ==
Account.Type.ADMIN &&
+ network.getVpcId() == null && network.getGuestType() ==
Network.GuestType.Isolated) {
+
response.setKeepMacAddressOnPublicNic(network.getKeepMacAddressOnPublicNic());
+ }
+
response.setObjectName("network");
return response;
}
@@ -3653,6 +3658,9 @@ public class ApiResponseHelper implements
ResponseGenerator, ResourceIdSupport {
}
}
+ if (CallContext.current().getCallingAccount().getType() ==
Account.Type.ADMIN) {
+
response.setKeepMacAddressOnPublicNic(vpc.getKeepMacAddressOnPublicNic());
+ }
response.setObjectName("vpc");
return response;
}
diff --git
a/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java
b/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java
index 450f08c46e3..a09867b8ffc 100644
--- a/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java
@@ -301,7 +301,7 @@ public class NetworkMigrationManagerImpl implements
NetworkMigrationManager {
copyOfVpc = _vpcService.createVpc(vpc.getZoneId(), vpcOfferingId,
vpc.getAccountId(), vpc.getName(),
vpc.getDisplayText(), vpc.getCidr(),
vpc.getNetworkDomain(), vpc.getIp4Dns1(), vpc.getIp4Dns2(),
- vpc.getIp6Dns1(), vpc.getIp6Dns2(), vpc.isDisplay(),
vpc.getPublicMtu(), null, null, null, vpc.useRouterIpAsResolver());
+ vpc.getIp6Dns1(), vpc.getIp6Dns2(), vpc.isDisplay(),
vpc.getPublicMtu(), null, null, null, vpc.useRouterIpAsResolver(),
vpc.getKeepMacAddressOnPublicNic());
copyOfVpcId = copyOfVpc.getId();
//on resume of migration the uuid will be swapped already. So the
copy will have the value of the original vpcid.
diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
index c9884f8c469..d18fd043f69 100644
--- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
+++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java
@@ -1549,6 +1549,8 @@ public class NetworkServiceImpl extends ManagerBase
implements NetworkService, C
DataCenter zone = getAndValidateZone(cmd, pNtwk);
+ boolean keepMacAddressOnPublicNic =
getAndValidateSupportForKeepMacAddressOnPublicNicParameter(cmd.getKeepMacAddressOnPublicNic(),
ntwkOff);
+
_accountMgr.checkAccess(owner, ntwkOff, zone);
validateZoneAvailability(caller, zone);
@@ -1834,7 +1836,7 @@ public class NetworkServiceImpl extends ManagerBase
implements NetworkService, C
Network network = commitNetwork(networkOfferingId, gateway, startIP,
endIP, netmask, networkDomain, vlanId, bypassVlanOverlapCheck, name,
displayText, caller, physicalNetworkId, zone.getId(),
domainId, isDomainSpecific, subdomainAccess, vpcId, startIPv6,
endIPv6, ip6Gateway, ip6Cidr, displayNetwork, aclId, secondaryVlanId,
privateVlanType, ntwkOff, pNtwk, aclType, owner, cidr, createVlan,
- externalId, routerIPv4, routerIPv6, associatedNetwork,
ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, interfaceMTUs, networkCidrSize);
+ externalId, routerIPv4, routerIPv6, associatedNetwork,
ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2, interfaceMTUs, networkCidrSize,
keepMacAddressOnPublicNic);
// retrieve, acquire and associate the correct IP addresses
checkAndSetRouterSourceNatIp(owner, cmd, network);
@@ -1879,6 +1881,24 @@ public class NetworkServiceImpl extends ManagerBase
implements NetworkService, C
}
}
+ protected boolean
getAndValidateSupportForKeepMacAddressOnPublicNicParameter(Boolean
keepMacAddressOnPublicNic, NetworkOffering networkOffering) {
+ if (networkOffering.isForVpc() && keepMacAddressOnPublicNic != null) {
+ throw new InvalidParameterValueException(
+ String.format("The [%s] parameter cannot be specified on
the creation of VPC tiers.", ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC)
+ );
+ }
+
+ GuestType guestType = networkOffering.getGuestType();
+ if (guestType != GuestType.Isolated && keepMacAddressOnPublicNic !=
null) {
+ throw new InvalidParameterValueException(String.format(
+ "The [%s] parameter can only be specified on the creation
of [%s] networks.",
+ ApiConstants.KEEP_MAC_ADDRESS_ON_PUBLIC_NIC,
GuestType.Isolated
+ ));
+ }
+
+ return keepMacAddressOnPublicNic == null || keepMacAddressOnPublicNic;
+ }
+
@Override
@DB
@ActionEvent(eventType = EventTypes.EVENT_NETWORK_CREATE, eventDescription
= "creating network")
@@ -2282,7 +2302,7 @@ public class NetworkServiceImpl extends ManagerBase
implements NetworkService, C
final Boolean displayNetwork, final Long
aclId, final String isolatedPvlan, final PVlanType isolatedPvlanType, final
NetworkOffering ntwkOff, final PhysicalNetwork pNtwk, final ACLType aclType,
final Account ownerFinal,
final String cidr, final boolean createVlan,
final String externalId, String routerIp, String routerIpv6,
final Network associatedNetwork, final
String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String
ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs,
- final Integer networkCidrSize) throws
InsufficientCapacityException, ResourceAllocationException {
+ final Integer networkCidrSize, final boolean
keepMacAddressOnPublicNic) throws InsufficientCapacityException,
ResourceAllocationException {
try {
Network network = Transaction.execute(new
TransactionCallbackWithException<Network, Exception>() {
@Override
@@ -2348,7 +2368,7 @@ public class NetworkServiceImpl extends ManagerBase
implements NetworkService, C
}
network =
_networkMgr.createGuestNetwork(networkOfferingId, name, displayText, gateway,
cidr, vlanId, bypassVlanOverlapCheck, networkDomain, owner, sharedDomainId,
pNtwk,
zoneId, aclType, subdomainAccess, vpcId,
ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan, isolatedPvlanType,
externalId, routerIp, routerIpv6, ip4Dns1, ip4Dns2,
- ip6Dns1, ip6Dns2, vrIfaceMTUs,
networkCidrSize);
+ ip6Dns1, ip6Dns2, vrIfaceMTUs,
networkCidrSize, keepMacAddressOnPublicNic);
}
if (createVlan && network != null) {
@@ -3156,6 +3176,7 @@ public class NetworkServiceImpl extends ManagerBase
implements NetworkService, C
String ip4Dns2 = cmd.getIp4Dns2();
String ip6Dns1 = cmd.getIp6Dns1();
String ip6Dns2 = cmd.getIp6Dns2();
+ Boolean keepMacAddressOnPublicNic = cmd.getKeepMacAddressOnPublicNic();
boolean restartNetwork = false;
@@ -3214,6 +3235,10 @@ public class NetworkServiceImpl extends ManagerBase
implements NetworkService, C
network.setUuid(customId);
}
+ if (keepMacAddressOnPublicNic != null) {
+
network.setKeepMacAddressOnPublicNic(getAndValidateSupportForKeepMacAddressOnPublicNicParameter(keepMacAddressOnPublicNic,
offering));
+ }
+
// display flag is not null and has changed
if (displayNetwork != null && displayNetwork !=
network.getDisplayNetwork()) {
// Update resource count if it needs to be updated
diff --git
a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
index 684f196ce9f..56534a8ee7d 100644
--- a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
+++ b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java
@@ -737,55 +737,91 @@ public class NetworkHelperImpl implements NetworkHelper {
}
protected LinkedHashMap<Network, List<? extends NicProfile>>
configurePublicNic(final RouterDeploymentDefinition routerDeploymentDefinition,
final boolean hasGuestNic) throws InsufficientAddressCapacityException {
- final LinkedHashMap<Network, List<? extends NicProfile>> publicConfig
= new LinkedHashMap<Network, List<? extends NicProfile>>(3);
-
- if (routerDeploymentDefinition.isPublicNetwork()) {
- logger.debug("Adding NIC for Virtual Router in Public network ");
- // if source nat service is supported by the network, get the
source
- // nat ip address
- final NicProfile defaultNic = new NicProfile();
- defaultNic.setDefaultNic(true);
- final PublicIp sourceNatIp =
routerDeploymentDefinition.getSourceNatIP();
- defaultNic.setIPv4Address(sourceNatIp.getAddress().addr());
- defaultNic.setIPv4Gateway(sourceNatIp.getGateway());
- defaultNic.setIPv4Netmask(sourceNatIp.getNetmask());
- defaultNic.setMacAddress(sourceNatIp.getMacAddress());
- // get broadcast from public network
- final Network pubNet =
_networkDao.findById(sourceNatIp.getNetworkId());
- if (pubNet.getBroadcastDomainType() == BroadcastDomainType.Vxlan) {
- defaultNic.setBroadcastType(BroadcastDomainType.Vxlan);
-
defaultNic.setBroadcastUri(BroadcastDomainType.Vxlan.toUri(sourceNatIp.getVlanTag()));
-
defaultNic.setIsolationUri(BroadcastDomainType.Vxlan.toUri(sourceNatIp.getVlanTag()));
- } else {
- defaultNic.setBroadcastType(BroadcastDomainType.Vlan);
- defaultNic.setBroadcastUri(sourceNatIp.getVlanTag() != null ?
BroadcastDomainType.Vlan.toUri(sourceNatIp.getVlanTag()) : null);
- defaultNic.setIsolationUri(sourceNatIp.getVlanTag() != null ?
IsolationType.Vlan.toUri(sourceNatIp.getVlanTag()) : null);
- }
-
- //If guest nic has already been added we will have 2 devices in
the list.
- if (hasGuestNic) {
- defaultNic.setDeviceId(2);
- }
+ final LinkedHashMap<Network, List<? extends NicProfile>> publicConfig
= new LinkedHashMap<>(3);
+ if (!routerDeploymentDefinition.isPublicNetwork()) {
+ return publicConfig;
+ }
- final NetworkOffering publicOffering =
_networkModel.getSystemAccountNetworkOfferings(NetworkOffering.SystemPublicNetwork).get(0);
- final List<? extends Network> publicNetworks =
_networkMgr.setupNetwork(s_systemAccount, publicOffering,
routerDeploymentDefinition.getPlan(), null, null, false);
- final String publicIp = defaultNic.getIPv4Address();
- // We want to use the identical MAC address for RvR on public
- // interface if possible
- final NicVO peerNic =
_nicDao.findByIp4AddressAndNetworkId(publicIp, publicNetworks.get(0).getId());
- if (peerNic != null) {
- logger.info("Use same MAC as previous RvR, the MAC is " +
peerNic.getMacAddress());
- defaultNic.setMacAddress(peerNic.getMacAddress());
- }
- if (routerDeploymentDefinition.getGuestNetwork() != null) {
- ipv6Service.updateNicIpv6(defaultNic,
routerDeploymentDefinition.getDest().getDataCenter(),
routerDeploymentDefinition.getGuestNetwork());
- }
- publicConfig.put(publicNetworks.get(0), new
ArrayList<NicProfile>(Arrays.asList(defaultNic)));
+ final PublicIp sourceNatIp =
routerDeploymentDefinition.getSourceNatIP();
+ final NicProfile defaultNic = new NicProfile();
+ configurePublicVrNicBasedOnSourceNatIp(defaultNic, sourceNatIp);
+ if (hasGuestNic) {
+ logger.debug("Guest NIC has already been configured, therefore
setting device ID of new VR (with source NAT [{}]) public NIC to [2].",
sourceNatIp);
+ defaultNic.setDeviceId(2);
}
+ final NetworkOffering publicOffering =
_networkModel.getSystemAccountNetworkOfferings(NetworkOffering.SystemPublicNetwork).get(0);
+ final List<? extends Network> publicNetworks =
_networkMgr.setupNetwork(s_systemAccount, publicOffering,
routerDeploymentDefinition.getPlan(), null, null, false);
+
+ setPublicNicMacAddressSameAsPeerNic(defaultNic, publicNetworks.get(0),
routerDeploymentDefinition);
+
+ if (routerDeploymentDefinition.getGuestNetwork() != null) {
+ ipv6Service.updateNicIpv6(defaultNic,
routerDeploymentDefinition.getDest().getDataCenter(),
routerDeploymentDefinition.getGuestNetwork());
+ }
+ publicConfig.put(publicNetworks.get(0), List.of(defaultNic));
return publicConfig;
}
+ /**
+ * Configures the public NIC of a Virtual Router based on the provided
source NAT IP.
+ * @param nic Virtual Router public NIC to be configured.
+ * @param sourceNatIp Source NAT IP which should be used to configure the
Virtual Router's public NIC.
+ */
+ protected void configurePublicVrNicBasedOnSourceNatIp(NicProfile nic,
PublicIp sourceNatIp) {
+ logger.debug("Configuring public NIC of VR with source NAT IP equal to
[{}]", sourceNatIp.getAddress().addr());
+ nic.setDefaultNic(true);
+ nic.setIPv4Address(sourceNatIp.getAddress().addr());
+ nic.setIPv4Gateway(sourceNatIp.getGateway());
+ nic.setIPv4Netmask(sourceNatIp.getNetmask());
+ nic.setMacAddress(sourceNatIp.getMacAddress());
+
+ Network publicNetwork =
_networkDao.findById(sourceNatIp.getNetworkId());
+ if (publicNetwork.getBroadcastDomainType() ==
BroadcastDomainType.Vxlan) {
+ nic.setBroadcastType(BroadcastDomainType.Vxlan);
+
nic.setBroadcastUri(BroadcastDomainType.Vxlan.toUri(sourceNatIp.getVlanTag()));
+
nic.setIsolationUri(BroadcastDomainType.Vxlan.toUri(sourceNatIp.getVlanTag()));
+ } else {
+ nic.setBroadcastType(BroadcastDomainType.Vlan);
+ nic.setBroadcastUri(sourceNatIp.getVlanTag() != null ?
BroadcastDomainType.Vlan.toUri(sourceNatIp.getVlanTag()) : null);
+ nic.setIsolationUri(sourceNatIp.getVlanTag() != null ?
IsolationType.Vlan.toUri(sourceNatIp.getVlanTag()) : null);
+ }
+ }
+
+ /**
+ * Sets the MAC address of the new VR's public NIC the same as the
previous VR's public NIC MAC address if
+ * {@link RouterDeploymentDefinition#getKeepMacAddressOnPublicNic()} is
set to {@code true} and a peer NIC is found; otherwise,
+ * does nothing.
+ */
+ protected void setPublicNicMacAddressSameAsPeerNic(NicProfile defaultNic,
Network publicNetwork, RouterDeploymentDefinition routerDeploymentDefinition)
throws InsufficientAddressCapacityException {
+ String publicIp = defaultNic.getIPv4Address();
+ logger.info("Verifying if we will use same MAC address for public NIC
of new VR (with source NAT [{}]).", publicIp);
+
+ logger.debug("Searching for peer NIC for public IP [{}] and network
[{}].", publicIp, publicNetwork.getUuid());
+ NicVO peerNic = _nicDao.findByIp4AddressAndNetworkId(publicIp,
publicNetwork.getId());
+ if (peerNic == null) {
+ logger.info("We will not use the same MAC address for public NIC
of new VR as we have not found a peer NIC for public IP [{}] and network [{}].",
+ publicIp, publicNetwork.getUuid());
+ return;
+ }
+
+ logger.info("Found peer NIC [{}] for public IP [{}] and network
[{}].", peerNic.getUuid(), publicIp, publicNetwork.getUuid());
+ boolean keepMacAddressOnPublicNic =
routerDeploymentDefinition.getKeepMacAddressOnPublicNic();
+ String macAddressLog = String.format("same MAC address for public NIC
of new VR (with source NAT [%s]) as the [keep_mac_address_on_public_nic]
property is configured as [%s].", publicIp, keepMacAddressOnPublicNic);
+ if (keepMacAddressOnPublicNic) {
+ logger.info("Using {}", macAddressLog);
+ defaultNic.setMacAddress(peerNic.getMacAddress());
+ return;
+ }
+
+ logger.info("Not using {}", macAddressLog);
+ boolean fetchNewMacAddress =
peerNic.getMacAddress().equals(defaultNic.getMacAddress());
+ if (fetchNewMacAddress) {
+ logger.debug("Fetching new MAC address for public NIC of new VR,
since the MAC address of the peer NIC [UUID: {}, MAC address: {}] is equal to
the predefined MAC address of the current NIC.", peerNic.getUuid(),
peerNic.getMacAddress());
+ routerDeploymentDefinition.findSourceNatIP();
+ configurePublicVrNicBasedOnSourceNatIp(defaultNic,
routerDeploymentDefinition.getSourceNatIP());
+ }
+ }
+
@Override
public LinkedHashMap<Network, List<? extends NicProfile>>
configureDefaultNics(final RouterDeploymentDefinition
routerDeploymentDefinition) throws ConcurrentOperationException,
InsufficientAddressCapacityException {
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 5b4ab908ca4..c5f36e85655 100644
--- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
+++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java
@@ -1568,7 +1568,7 @@ public class VpcManagerImpl extends ManagerBase
implements VpcManager, VpcProvis
@ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription =
"creating vpc", create = true)
public Vpc createVpc(final long zoneId, final long vpcOffId, final long
vpcOwnerId, final String vpcName, final String displayText, final String cidr,
String networkDomain,
final String ip4Dns1, final String ip4Dns2, final
String ip6Dns1, final String ip6Dns2, final Boolean displayVpc, Integer
publicMtu,
- final Integer cidrSize, final Long asNumber, final
List<Long> bgpPeerIds, Boolean useVrIpResolver) throws
ResourceAllocationException {
+ final Integer cidrSize, final Long asNumber, final
List<Long> bgpPeerIds, Boolean useVrIpResolver, boolean
keepMacAddressOnPublicNic) throws ResourceAllocationException {
final Account caller = CallContext.current().getCallingAccount();
final Account owner = _accountMgr.getAccount(vpcOwnerId);
@@ -1668,6 +1668,7 @@ public class VpcManagerImpl extends ManagerBase
implements VpcManager, VpcProvis
vpc.setPublicMtu(publicMtu);
vpc.setDisplay(Boolean.TRUE.equals(displayVpc));
vpc.setUseRouterIpResolver(Boolean.TRUE.equals(useVrIpResolver));
+ vpc.setKeepMacAddressOnPublicNic(keepMacAddressOnPublicNic);
try (CheckedReservation vpcReservation = new CheckedReservation(owner,
ResourceType.vpc, null, null, 1L, reservationDao, _resourceLimitMgr)) {
if (vpc.getCidr() == null && cidrSize != null) {
@@ -1728,7 +1729,8 @@ public class VpcManagerImpl extends ManagerBase
implements VpcManager, VpcProvis
List<Long> bgpPeerIds = (cmd instanceof CreateVPCCmdByAdmin) ?
((CreateVPCCmdByAdmin)cmd).getBgpPeerIds() : null;
Vpc vpc = createVpc(cmd.getZoneId(), cmd.getVpcOffering(),
cmd.getEntityOwnerId(), cmd.getVpcName(), cmd.getDisplayText(),
cmd.getCidr(), cmd.getNetworkDomain(), cmd.getIp4Dns1(),
cmd.getIp4Dns2(), cmd.getIp6Dns1(),
- cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu(),
cmd.getCidrSize(), cmd.getAsNumber(), bgpPeerIds, cmd.getUseVrIpResolver());
+ cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu(),
cmd.getCidrSize(), cmd.getAsNumber(), bgpPeerIds,
+ cmd.getUseVrIpResolver(), cmd.getKeepMacAddressOnPublicNic());
String sourceNatIP = cmd.getSourceNatIP();
boolean forNsx = isVpcForProvider(Provider.Nsx, vpc);
@@ -1940,12 +1942,14 @@ public class VpcManagerImpl extends ManagerBase
implements VpcManager, VpcProvis
@Override
public Vpc updateVpc(UpdateVPCCmd cmd) throws
ResourceUnavailableException, InsufficientCapacityException {
- return updateVpc(cmd.getId(), cmd.getVpcName(), cmd.getDisplayText(),
cmd.getCustomId(), cmd.isDisplayVpc(), cmd.getPublicMtu(),
cmd.getSourceNatIP());
+ return updateVpc(cmd.getId(), cmd.getVpcName(), cmd.getDisplayText(),
cmd.getCustomId(),
+ cmd.isDisplayVpc(), cmd.getPublicMtu(), cmd.getSourceNatIP(),
cmd.getKeepMacAddressOnPublicNic());
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_VPC_UPDATE, eventDescription =
"updating vpc")
- public Vpc updateVpc(final long vpcId, final String vpcName, final String
displayText, final String customId, final Boolean displayVpc, Integer mtu,
String sourceNatIp) throws ResourceUnavailableException,
InsufficientCapacityException {
+ public Vpc updateVpc(final long vpcId, final String vpcName, final String
displayText, final String customId,
+ final Boolean displayVpc, Integer mtu, String
sourceNatIp, Boolean keepMacAddressOnPublicNic) throws
ResourceUnavailableException, InsufficientCapacityException {
final Account caller = CallContext.current().getCallingAccount();
// Verify input parameters
@@ -1977,6 +1981,10 @@ public class VpcManagerImpl extends ManagerBase
implements VpcManager, VpcProvis
vpc.setDisplay(displayVpc);
}
+ if (keepMacAddressOnPublicNic != null) {
+ vpc.setKeepMacAddressOnPublicNic(keepMacAddressOnPublicNic);
+ }
+
mtu = validateMtu(vpcToUpdate, mtu);
if (mtu != null) {
updateMtuOfVpcNetwork(vpcToUpdate, vpc, mtu);
diff --git
a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java
b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java
index 4187586736d..77cd7890e41 100644
---
a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java
+++
b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java
@@ -394,7 +394,7 @@ public class RouterDeploymentDefinition {
}
}
- protected void findSourceNatIP() throws
InsufficientAddressCapacityException, ConcurrentOperationException {
+ public void findSourceNatIP() throws InsufficientAddressCapacityException,
ConcurrentOperationException {
sourceNatIp = null;
DataCenter zone = dest.getDataCenter();
Long zoneId = null;
@@ -548,4 +548,8 @@ public class RouterDeploymentDefinition {
return needReset;
}
+
+ public boolean getKeepMacAddressOnPublicNic() {
+ return guestNetwork == null ||
guestNetwork.getKeepMacAddressOnPublicNic();
+ }
}
diff --git
a/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java
b/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java
index 063565ebf46..9e432265f2a 100644
---
a/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java
+++
b/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java
@@ -117,7 +117,7 @@ public class VpcRouterDeploymentDefinition extends
RouterDeploymentDefinition {
}
@Override
- protected void findSourceNatIP() throws
InsufficientAddressCapacityException, ConcurrentOperationException {
+ public void findSourceNatIP() throws InsufficientAddressCapacityException,
ConcurrentOperationException {
sourceNatIp = null;
DataCenter zone = dest.getDataCenter();
Long zoneId = null;
@@ -219,4 +219,9 @@ public class VpcRouterDeploymentDefinition extends
RouterDeploymentDefinition {
public boolean isRollingRestart() {
return vpc.isRollingRestart();
}
+
+ @Override
+ public boolean getKeepMacAddressOnPublicNic() {
+ return vpc == null || vpc.getKeepMacAddressOnPublicNic();
+ }
}
diff --git a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java
b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java
index 51b2dad3dec..cd7d40d6895 100644
--- a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java
+++ b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java
@@ -460,7 +460,7 @@ public class NetworkServiceImplTest {
null, null, false, null, accountMock, null, phyNet,
1L, null, null, null, null, null,
true, null, null, null, null, null,
- null, null, null, null, new Pair<>(1500, privateMtu), null);
+ null, null, null, null, new Pair<>(1500, privateMtu), null,
true);
}
@Test
public void testValidateMtuConfigWhenMtusExceedThreshold() {
@@ -1330,4 +1330,52 @@ public class NetworkServiceImplTest {
Mockito.verify(accountJoin).addAnd("type", SearchCriteria.Op.EQ,
Account.Type.PROJECT);
Mockito.verify(sc).addAnd("id", SearchCriteria.Op.SC, accountJoin);
}
+
+ @Test(expected = InvalidParameterValueException.class)
+ public void
getAndValidateSupportForKeepMacAddressOnPublicNicParameterTestThrowExceptionWhenParamIsSpecifiedOnTiersCreation()
{
+ networkOfferingVO = Mockito.mock(NetworkOfferingVO.class);
+ Mockito.when(networkOfferingVO.isForVpc()).thenReturn(true);
+
+
service.getAndValidateSupportForKeepMacAddressOnPublicNicParameter(true,
networkOfferingVO);
+ }
+
+ @Test
+ public void
getAndValidateSupportForKeepMacAddressOnPublicNicParameterTestReturnTrueByDefaultOnTiersCreation()
{
+ networkOfferingVO = Mockito.mock(NetworkOfferingVO.class);
+ Mockito.when(networkOfferingVO.isForVpc()).thenReturn(true);
+
+
Assert.assertTrue(service.getAndValidateSupportForKeepMacAddressOnPublicNicParameter(null,
networkOfferingVO));
+ }
+
+ @Test(expected = InvalidParameterValueException.class)
+ public void
getAndValidateSupportForKeepMacAddressOnPublicNicParameterTestThrowExceptionWhenParamIsSpecifiedOnNonIsolatedNetworksCreation()
{
+ networkOfferingVO = Mockito.mock(NetworkOfferingVO.class);
+
Mockito.when(networkOfferingVO.getGuestType()).thenReturn(Network.GuestType.Shared);
+
+
service.getAndValidateSupportForKeepMacAddressOnPublicNicParameter(true,
networkOfferingVO);
+ }
+
+ @Test
+ public void
getAndValidateSupportForKeepMacAddressOnPublicNicParameterTestReturnTrueByDefaultOnNonIsolatedNetworksCreation()
{
+ networkOfferingVO = Mockito.mock(NetworkOfferingVO.class);
+
Mockito.when(networkOfferingVO.getGuestType()).thenReturn(Network.GuestType.L2);
+
+
Assert.assertTrue(service.getAndValidateSupportForKeepMacAddressOnPublicNicParameter(null,
networkOfferingVO));
+ }
+
+ @Test
+ public void
getAndValidateSupportForKeepMacAddressOnPublicNicParameterTestReturnTrueByDefaultOnIsolatedNetworksCreation()
{
+ networkOfferingVO = Mockito.mock(NetworkOfferingVO.class);
+
Mockito.when(networkOfferingVO.getGuestType()).thenReturn(Network.GuestType.Isolated);
+
+
Assert.assertTrue(service.getAndValidateSupportForKeepMacAddressOnPublicNicParameter(null,
networkOfferingVO));
+ }
+
+ @Test
+ public void
getAndValidateSupportForKeepMacAddressOnPublicNicParameterTestReturnSpecifiedValueOnIsolatedNetworksCreation()
{
+ networkOfferingVO = Mockito.mock(NetworkOfferingVO.class);
+
Mockito.when(networkOfferingVO.getGuestType()).thenReturn(Network.GuestType.Isolated);
+
+
Assert.assertFalse(service.getAndValidateSupportForKeepMacAddressOnPublicNicParameter(false,
networkOfferingVO));
+ }
}
diff --git
a/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java
b/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java
index 4237ef7f6f6..e4773d01de9 100644
--- a/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java
+++ b/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java
@@ -24,18 +24,31 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import com.cloud.deploy.DeployDestination;
+import com.cloud.exception.InsufficientAddressCapacityException;
+import com.cloud.network.Ipv6Service;
+import com.cloud.network.Network;
+import com.cloud.network.Networks;
+import com.cloud.network.addr.PublicIp;
+import com.cloud.network.dao.NetworkVO;
+import com.cloud.offering.NetworkOffering;
+import com.cloud.utils.net.Ip;
+import com.cloud.vm.NicProfile;
+import com.cloud.vm.NicVO;
import
org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import
org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition;
+import org.junit.Assert;
import org.junit.Before;
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 com.cloud.agent.AgentManager;
@@ -57,6 +70,9 @@ import com.cloud.vm.VirtualMachineName;
import com.cloud.vm.dao.DomainRouterDao;
import com.cloud.vm.dao.NicDao;
+import java.util.List;
+import java.util.Map;
+
@RunWith(MockitoJUnitRunner.class)
public class NetworkHelperImplTest {
@@ -69,32 +85,59 @@ public class NetworkHelperImplTest {
@Mock
DomainRouterDao routerDao;
+ @Spy
@InjectMocks
- protected NetworkHelperImpl nwHelper = new NetworkHelperImpl();
+ protected NetworkHelperImpl networkHelperSpy = new NetworkHelperImpl();
+
@Mock
NetworkOrchestrationService networkOrchestrationService;
+
@Mock
NetworkDao networkDao;
+
@Mock
NetworkModel networkModel;
+
@Mock
- NicDao nicDao;
+ private NicDao nicDaoMock;
@Mock
private RouterDeploymentDefinition routerDeploymentDefinition;
+
@Mock
private VirtualRouterProvider virtualProvider;
+
@Mock
private Account owner;
+
@Mock
private ServiceOfferingVO routerOffering;
+
@Mock
private VMTemplateVO template;
+ @Mock
+ private PublicIp publicIpMock;
+
+ @Mock
+ private RouterDeploymentDefinition routerDeploymentDefinitionMock;
+
+ @Mock
+ private Ipv6Service ipv6ServiceMock;
+
+ @Mock
+ private NicVO nicVoMock;
+
+ @Mock
+ private Network networkMock;
+
+ private NicProfile nicProfile = new NicProfile();
+
@Before
public void setUp() {
- nwHelper._networkDao = networkDao;
- nwHelper._networkModel = networkModel;
+ networkHelperSpy._networkDao = networkDao;
+ networkHelperSpy._networkModel = networkModel;
+
when(template.getId()).thenReturn(1L);
when(template.isDynamicallyScalable()).thenReturn(true);
when(virtualProvider.getId()).thenReturn(1L);
@@ -107,7 +150,7 @@ public class NetworkHelperImplTest {
public void testSendCommandsToRouterWrongRouterVersion()
throws AgentUnavailableException, OperationTimedoutException,
ResourceUnavailableException {
// Prepare
- NetworkHelperImpl nwHelperUT = spy(this.nwHelper);
+ NetworkHelperImpl nwHelperUT = networkHelperSpy;
VirtualRouter vr = mock(VirtualRouter.class);
doReturn(false).when(nwHelperUT).checkRouterVersion(vr);
@@ -122,7 +165,7 @@ public class NetworkHelperImplTest {
public void testSendCommandsToRouter()
throws AgentUnavailableException, OperationTimedoutException,
ResourceUnavailableException {
// Prepare
- NetworkHelperImpl nwHelperUT = spy(this.nwHelper);
+ NetworkHelperImpl nwHelperUT = networkHelperSpy;
VirtualRouter vr = mock(VirtualRouter.class);
when(vr.getHostId()).thenReturn(HOST_ID);
doReturn(true).when(nwHelperUT).checkRouterVersion(vr);
@@ -160,7 +203,7 @@ public class NetworkHelperImplTest {
public void testSendCommandsToRouterWithTrueResult()
throws AgentUnavailableException, OperationTimedoutException,
ResourceUnavailableException {
// Prepare
- NetworkHelperImpl nwHelperUT = spy(this.nwHelper);
+ NetworkHelperImpl nwHelperUT = networkHelperSpy;
VirtualRouter vr = mock(VirtualRouter.class);
when(vr.getHostId()).thenReturn(HOST_ID);
doReturn(true).when(nwHelperUT).checkRouterVersion(vr);
@@ -198,7 +241,7 @@ public class NetworkHelperImplTest {
public void testSendCommandsToRouterWithNoAnswers()
throws AgentUnavailableException, OperationTimedoutException,
ResourceUnavailableException {
// Prepare
- NetworkHelperImpl nwHelperUT = spy(this.nwHelper);
+ NetworkHelperImpl nwHelperUT = networkHelperSpy;
VirtualRouter vr = mock(VirtualRouter.class);
when(vr.getHostId()).thenReturn(HOST_ID);
doReturn(true).when(nwHelperUT).checkRouterVersion(vr);
@@ -227,7 +270,7 @@ public class NetworkHelperImplTest {
boolean offerHA = false;
Long vpcId = 900L;
when(routerDao.persist(any(DomainRouterVO.class))).thenAnswer(invocation ->
invocation.getArgument(0));
- DomainRouterVO result = nwHelper.createOrUpdateDomainRouter(
+ DomainRouterVO result = networkHelperSpy.createOrUpdateDomainRouter(
null, id, routerDeploymentDefinition, owner, userId,
routerOffering, offerHA, vpcId, template);
assertNotNull(result);
assertEquals(id, result.getId());
@@ -261,11 +304,234 @@ public class NetworkHelperImplTest {
owner.getDomainId(), owner.getId(), userId,
routerDeploymentDefinition.isRedundant(), VirtualRouter.RedundantState.UNKNOWN,
offerHA, false, vpcId);
existing.setDynamicallyScalable(false);
- DomainRouterVO result = nwHelper.createOrUpdateDomainRouter(
+ DomainRouterVO result = networkHelperSpy.createOrUpdateDomainRouter(
existing, id, routerDeploymentDefinition, owner, userId,
routerOffering, offerHA, vpcId, template);
verify(routerDao).update(existing.getId(), existing);
assertEquals(template.getId(), result.getTemplateId());
assertEquals(Hypervisor.HypervisorType.KVM,
result.getHypervisorType());
assertTrue(result.isDynamicallyScalable());
}
+
+
+ private NicProfile getExpectedNicProfile(boolean vxlan, String vlanTag) {
+ NicProfile nic = new NicProfile();
+ nic.setDefaultNic(true);
+ nic.setIPv4Address("192.168.0.10");
+ nic.setIPv4Gateway("192.168.0.1");
+ nic.setIPv4Netmask("255.255.255.0");
+ nic.setMacAddress("ff-ff-ff-ff-ff-ff");
+
+ if (vxlan) {
+ nic.setBroadcastType(Networks.BroadcastDomainType.Vxlan);
+
nic.setBroadcastUri(Networks.BroadcastDomainType.Vxlan.toUri(vlanTag));
+
nic.setIsolationUri(Networks.BroadcastDomainType.Vxlan.toUri(vlanTag));
+ } else {
+ nic.setBroadcastType(Networks.BroadcastDomainType.Vlan);
+ nic.setBroadcastUri(vlanTag != null ?
Networks.BroadcastDomainType.Vlan.toUri(vlanTag) : null);
+ nic.setIsolationUri(vlanTag != null ?
Networks.IsolationType.Vlan.toUri(vlanTag) : null);
+ }
+
+ return nic;
+ }
+
+ @Test
+ public void configurePublicVrNicBasedOnSourceNatIpTestConfigureVxLanNic() {
+ String vlanTag = "200";
+ NicProfile expected = getExpectedNicProfile(true, vlanTag);
+
+ Ip ipMock = Mockito.mock(Ip.class);
+ NetworkVO publicNetworkMock = Mockito.mock(NetworkVO.class);
+ long networkId = 1L;
+ Mockito.when(publicIpMock.getAddress()).thenReturn(ipMock);
+ Mockito.when(ipMock.addr()).thenReturn(expected.getIPv4Address());
+
Mockito.when(publicIpMock.getGateway()).thenReturn(expected.getIPv4Gateway());
+
Mockito.when(publicIpMock.getNetmask()).thenReturn(expected.getIPv4Netmask());
+
Mockito.when(publicIpMock.getMacAddress()).thenReturn(expected.getMacAddress());
+ Mockito.when(publicIpMock.getNetworkId()).thenReturn(networkId);
+
Mockito.when(networkDao.findById(networkId)).thenReturn(publicNetworkMock);
+
Mockito.when(publicNetworkMock.getBroadcastDomainType()).thenReturn(Networks.BroadcastDomainType.Vxlan);
+ Mockito.when(publicIpMock.getVlanTag()).thenReturn(vlanTag);
+
+ networkHelperSpy.configurePublicVrNicBasedOnSourceNatIp(nicProfile,
publicIpMock);
+
+ Assert.assertTrue(nicProfile.isDefaultNic());
+ Assert.assertEquals(expected.getIPv4Address(),
nicProfile.getIPv4Address());
+ Assert.assertEquals(expected.getIPv4Gateway(),
nicProfile.getIPv4Gateway());
+ Assert.assertEquals(expected.getIPv4Netmask(),
nicProfile.getIPv4Netmask());
+ Assert.assertEquals(expected.getMacAddress(),
nicProfile.getMacAddress());
+ Assert.assertEquals(expected.getBroadcastType(),
nicProfile.getBroadcastType());
+ Assert.assertEquals(expected.getBroadCastUri(),
nicProfile.getBroadCastUri());
+ Assert.assertEquals(expected.getIsolationUri(),
nicProfile.getIsolationUri());
+ }
+
+ @Test
+ public void
configurePublicVrNicBasedOnSourceNatIpTestConfigureVlanNicWithVlanTag() {
+ String vlanTag = "200";
+ NicProfile expected = getExpectedNicProfile(false, vlanTag);
+
+ Ip ipMock = Mockito.mock(Ip.class);
+ NetworkVO publicNetworkMock = Mockito.mock(NetworkVO.class);
+ long networkId = 1L;
+ Mockito.when(publicIpMock.getAddress()).thenReturn(ipMock);
+ Mockito.when(ipMock.addr()).thenReturn(expected.getIPv4Address());
+
Mockito.when(publicIpMock.getGateway()).thenReturn(expected.getIPv4Gateway());
+
Mockito.when(publicIpMock.getNetmask()).thenReturn(expected.getIPv4Netmask());
+
Mockito.when(publicIpMock.getMacAddress()).thenReturn(expected.getMacAddress());
+ Mockito.when(publicIpMock.getNetworkId()).thenReturn(networkId);
+
Mockito.when(networkDao.findById(networkId)).thenReturn(publicNetworkMock);
+
Mockito.when(publicNetworkMock.getBroadcastDomainType()).thenReturn(Networks.BroadcastDomainType.Vlan);
+ Mockito.when(publicIpMock.getVlanTag()).thenReturn(vlanTag);
+
+ networkHelperSpy.configurePublicVrNicBasedOnSourceNatIp(nicProfile,
publicIpMock);
+
+ Assert.assertTrue(nicProfile.isDefaultNic());
+ Assert.assertEquals(expected.getIPv4Address(),
nicProfile.getIPv4Address());
+ Assert.assertEquals(expected.getIPv4Gateway(),
nicProfile.getIPv4Gateway());
+ Assert.assertEquals(expected.getIPv4Netmask(),
nicProfile.getIPv4Netmask());
+ Assert.assertEquals(expected.getMacAddress(),
nicProfile.getMacAddress());
+ Assert.assertEquals(expected.getBroadcastType(),
nicProfile.getBroadcastType());
+ Assert.assertEquals(expected.getBroadCastUri(),
nicProfile.getBroadCastUri());
+ Assert.assertEquals(expected.getIsolationUri(),
nicProfile.getIsolationUri());
+ }
+
+ @Test
+ public void
configurePublicVrNicBasedOnSourceNatIpTestConfigureVlanNicWithNoVlanTag() {
+ String vlanTag = null;
+ NicProfile expected = getExpectedNicProfile(false, vlanTag);
+
+ Ip ipMock = Mockito.mock(Ip.class);
+ NetworkVO publicNetworkMock = Mockito.mock(NetworkVO.class);
+ long networkId = 1L;
+ Mockito.when(publicIpMock.getAddress()).thenReturn(ipMock);
+ Mockito.when(ipMock.addr()).thenReturn(expected.getIPv4Address());
+
Mockito.when(publicIpMock.getGateway()).thenReturn(expected.getIPv4Gateway());
+
Mockito.when(publicIpMock.getNetmask()).thenReturn(expected.getIPv4Netmask());
+
Mockito.when(publicIpMock.getMacAddress()).thenReturn(expected.getMacAddress());
+ Mockito.when(publicIpMock.getNetworkId()).thenReturn(networkId);
+
Mockito.when(networkDao.findById(networkId)).thenReturn(publicNetworkMock);
+
Mockito.when(publicNetworkMock.getBroadcastDomainType()).thenReturn(Networks.BroadcastDomainType.Vlan);
+ Mockito.when(publicIpMock.getVlanTag()).thenReturn(vlanTag);
+
+ networkHelperSpy.configurePublicVrNicBasedOnSourceNatIp(nicProfile,
publicIpMock);
+
+ Assert.assertTrue(nicProfile.isDefaultNic());
+ Assert.assertEquals(expected.getIPv4Address(),
nicProfile.getIPv4Address());
+ Assert.assertEquals(expected.getIPv4Gateway(),
nicProfile.getIPv4Gateway());
+ Assert.assertEquals(expected.getIPv4Netmask(),
nicProfile.getIPv4Netmask());
+ Assert.assertEquals(expected.getMacAddress(),
nicProfile.getMacAddress());
+ Assert.assertEquals(expected.getBroadcastType(),
nicProfile.getBroadcastType());
+ Assert.assertEquals(expected.getBroadCastUri(),
nicProfile.getBroadCastUri());
+ Assert.assertEquals(expected.getIsolationUri(),
nicProfile.getIsolationUri());
+ }
+
+ @Test
+ public void
setPublicNicMacAddressSameAsPeerNicTestDoNothingWhenThereIsNoPeer() throws
InsufficientAddressCapacityException {
+ String newMacAddress = "ff-ff-ff-ff-ff-ff";
+ nicProfile.setIPv4Address("10.0.0.1");
+ nicProfile.setMacAddress(newMacAddress);
+
Mockito.when(nicDaoMock.findByIp4AddressAndNetworkId(Mockito.anyString(),
Mockito.anyLong())).thenReturn(null);
+
+ networkHelperSpy.setPublicNicMacAddressSameAsPeerNic(nicProfile,
networkMock, routerDeploymentDefinitionMock);
+
+ Assert.assertEquals(newMacAddress, nicProfile.getMacAddress());
+ }
+
+ @Test
+ public void setPublicNicMacAddressSameAsPeerNicTestKeepMacAddress() throws
InsufficientAddressCapacityException {
+ String peerMacAddress = "ff-ff-ff-ff-ff-ff";
+ nicProfile.setIPv4Address("10.0.0.1");
+ nicProfile.setMacAddress("ff-ff-ff-ff-ff-f1");
+
+
Mockito.when(nicDaoMock.findByIp4AddressAndNetworkId(Mockito.anyString(),
Mockito.anyLong())).thenReturn(nicVoMock);
+ Mockito.when(nicVoMock.getMacAddress()).thenReturn(peerMacAddress);
+
Mockito.when(routerDeploymentDefinitionMock.getKeepMacAddressOnPublicNic()).thenReturn(true);
+
+ networkHelperSpy.setPublicNicMacAddressSameAsPeerNic(nicProfile,
networkMock, routerDeploymentDefinitionMock);
+
+ Assert.assertEquals(peerMacAddress, nicProfile.getMacAddress());
+ }
+
+ @Test
+ public void
setPublicNicMacAddressSameAsPeerNicTestDifferentMacAddressFetchingNewSourceNatIp()
throws InsufficientAddressCapacityException {
+ String macAddress = "ff-ff-ff-ff-ff-f1";
+ nicProfile.setIPv4Address("10.0.0.1");
+ nicProfile.setMacAddress(macAddress);
+ PublicIp publicIpMock = Mockito.mock(PublicIp.class);
+
+
Mockito.when(nicDaoMock.findByIp4AddressAndNetworkId(Mockito.anyString(),
Mockito.anyLong())).thenReturn(nicVoMock);
+ Mockito.when(nicVoMock.getMacAddress()).thenReturn(macAddress);
+
Mockito.when(routerDeploymentDefinitionMock.getKeepMacAddressOnPublicNic()).thenReturn(false);
+
Mockito.when(routerDeploymentDefinitionMock.getSourceNatIP()).thenReturn(publicIpMock);
+
Mockito.doNothing().when(networkHelperSpy).configurePublicVrNicBasedOnSourceNatIp(nicProfile,
publicIpMock);
+
+ networkHelperSpy.setPublicNicMacAddressSameAsPeerNic(nicProfile,
networkMock, routerDeploymentDefinitionMock);
+
+ Mockito.verify(routerDeploymentDefinitionMock).findSourceNatIP();
+
Mockito.verify(networkHelperSpy).configurePublicVrNicBasedOnSourceNatIp(nicProfile,
publicIpMock);
+ }
+
+ @Test
+ public void
setPublicNicMacAddressSameAsPeerNicTestDifferentMacAddressNotFetchingNewSourceNatIp()
throws InsufficientAddressCapacityException {
+ String newMacAddress = "ff-ff-ff-ff-ff-ff";
+ nicProfile.setIPv4Address("10.0.0.1");
+ nicProfile.setMacAddress(newMacAddress);
+
+
Mockito.when(nicDaoMock.findByIp4AddressAndNetworkId(Mockito.anyString(),
Mockito.anyLong())).thenReturn(nicVoMock);
+
Mockito.when(nicVoMock.getMacAddress()).thenReturn("ff-ff-ff-ff-ff-f1");
+
Mockito.when(routerDeploymentDefinitionMock.getKeepMacAddressOnPublicNic()).thenReturn(false);
+
+ networkHelperSpy.setPublicNicMacAddressSameAsPeerNic(nicProfile,
networkMock, routerDeploymentDefinitionMock);
+
+ Mockito.verify(routerDeploymentDefinitionMock,
Mockito.never()).findSourceNatIP();
+ Mockito.verify(networkHelperSpy,
Mockito.never()).configurePublicVrNicBasedOnSourceNatIp(Mockito.any(),
Mockito.any());
+ Assert.assertEquals(newMacAddress, nicProfile.getMacAddress());
+ }
+
+ @Test
+ public void configurePublicNicTestReturnEmptyMapWhenNetworkIsNotPublic()
throws InsufficientAddressCapacityException {
+
Mockito.when(routerDeploymentDefinitionMock.isPublicNetwork()).thenReturn(false);
+
+ Map<Network, List<? extends NicProfile>> nic =
networkHelperSpy.configurePublicNic(routerDeploymentDefinitionMock, false);
+ Assert.assertTrue(nic.isEmpty());
+ }
+
+ @Test
+ public void configurePublicNicTestConfigureDeviceId() throws
InsufficientAddressCapacityException {
+ PublicIp publicIpMock = Mockito.mock(PublicIp.class);
+ NetworkOffering networkOfferingMock =
Mockito.mock(NetworkOffering.class);
+
+
Mockito.when(routerDeploymentDefinitionMock.isPublicNetwork()).thenReturn(true);
+
Mockito.when(routerDeploymentDefinitionMock.getSourceNatIP()).thenReturn(publicIpMock);
+
Mockito.doNothing().when(networkHelperSpy).configurePublicVrNicBasedOnSourceNatIp(Mockito.any(),
Mockito.any());
+
Mockito.doReturn(List.of(networkOfferingMock)).when(networkModel).getSystemAccountNetworkOfferings(Mockito.any());
+
Mockito.doReturn(List.of(networkMock)).when(networkOrchestrationService).setupNetwork(
+ Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.anyBoolean()
+ );
+
Mockito.doNothing().when(networkHelperSpy).setPublicNicMacAddressSameAsPeerNic(Mockito.any(),
Mockito.any(), Mockito.any());
+
+ Map<Network, List<? extends NicProfile>> nic =
networkHelperSpy.configurePublicNic(routerDeploymentDefinitionMock, true);
+ Integer nicDeviceId = nic.get(networkMock).get(0).getDeviceId();
+ Assert.assertEquals(2, nicDeviceId.intValue());
+ }
+
+ @Test
+ public void configurePublicNicTestUpdateGuestNetworksIpv6Nic() throws
InsufficientAddressCapacityException {
+ PublicIp publicIpMock = Mockito.mock(PublicIp.class);
+ NetworkOffering networkOfferingMock =
Mockito.mock(NetworkOffering.class);
+ DeployDestination deployDestinationMock =
Mockito.mock(DeployDestination.class);
+
+
Mockito.when(routerDeploymentDefinitionMock.isPublicNetwork()).thenReturn(true);
+
Mockito.when(routerDeploymentDefinitionMock.getSourceNatIP()).thenReturn(publicIpMock);
+
Mockito.doNothing().when(networkHelperSpy).configurePublicVrNicBasedOnSourceNatIp(Mockito.any(),
Mockito.any());
+
Mockito.doReturn(List.of(networkOfferingMock)).when(networkModel).getSystemAccountNetworkOfferings(Mockito.any());
+
Mockito.doReturn(List.of(networkMock)).when(networkOrchestrationService).setupNetwork(
+ Mockito.any(), Mockito.any(), Mockito.any(), Mockito.any(),
Mockito.any(), Mockito.anyBoolean()
+ );
+
Mockito.doNothing().when(networkHelperSpy).setPublicNicMacAddressSameAsPeerNic(Mockito.any(),
Mockito.any(), Mockito.any());
+
Mockito.when(routerDeploymentDefinitionMock.getGuestNetwork()).thenReturn(networkMock);
+
Mockito.when(routerDeploymentDefinitionMock.getDest()).thenReturn(deployDestinationMock);
+
+ Map<Network, List<? extends NicProfile>> nic =
networkHelperSpy.configurePublicNic(routerDeploymentDefinitionMock, false);
+
Mockito.verify(ipv6ServiceMock).updateNicIpv6(Mockito.eq(nic.get(networkMock).get(0)),
Mockito.any(), Mockito.eq(networkMock));
+ }
}
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 9569769b016..8b8259421cd 100644
--- a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java
+++ b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java
@@ -492,7 +492,7 @@ public class VpcManagerImplTest {
mockVpcDnsResources(false, false);
try {
manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName,
vpcName, ip4Cidr, vpcDomain,
- ip4Dns[0], null, null, null, true, 1500, null, null, null,
false);
+ ip4Dns[0], null, null, null, true, 1500, null, null, null,
false, true);
} catch (ResourceAllocationException e) {
Assert.fail(String.format("failure with exception: %s",
e.getMessage()));
}
@@ -503,7 +503,7 @@ public class VpcManagerImplTest {
mockVpcDnsResources(true, false);
try {
manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName,
vpcName, ip4Cidr, vpcDomain,
- ip4Dns[0], ip4Dns[1], ip6Dns[0], null, true, 1500, null,
null, null, false);
+ ip4Dns[0], ip4Dns[1], ip6Dns[0], null, true, 1500, null,
null, null, false, true);
} catch (ResourceAllocationException e) {
Assert.fail(String.format("failure with exception: %s",
e.getMessage()));
}
@@ -517,7 +517,7 @@ public class VpcManagerImplTest {
Mockito.when(vpc.getUuid()).thenReturn("uuid");
try (MockedConstruction<CheckedReservation> mockCheckedReservation =
Mockito.mockConstruction(CheckedReservation.class)) {
manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName,
vpcName, ip4Cidr, vpcDomain,
- ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null,
null, false);
+ ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null,
null, false, true);
} catch (ResourceAllocationException e) {
Assert.fail(String.format("failure with exception: %s",
e.getMessage()));
}
@@ -533,7 +533,7 @@ public class VpcManagerImplTest {
doNothing().when(routedIpv4Manager).getOrCreateIpv4SubnetForVpc(any(),
anyString());
try (MockedConstruction<CheckedReservation> mockCheckedReservation =
Mockito.mockConstruction(CheckedReservation.class)) {
manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName,
vpcName, ip4Cidr, vpcDomain,
- ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null,
null, false);
+ ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null,
null, false, true);
} catch (ResourceAllocationException e) {
Assert.fail(String.format("failure with exception: %s",
e.getMessage()));
}
@@ -556,7 +556,7 @@ public class VpcManagerImplTest {
try (MockedConstruction<CheckedReservation> mockCheckedReservation =
Mockito.mockConstruction(CheckedReservation.class)) {
manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName,
vpcName, null, vpcDomain,
- ip4Dns[0], ip4Dns[1], null, null, true, 1500, 24, null,
bgpPeerIds, false);
+ ip4Dns[0], ip4Dns[1], null, null, true, 1500, 24, null,
bgpPeerIds, false, true);
} catch (ResourceAllocationException e) {
Assert.fail(String.format("failure with exception: %s",
e.getMessage()));
}
diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java
b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java
index 3b120b88db4..e4bc2096b9d 100644
--- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java
+++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java
@@ -704,11 +704,19 @@ public class MockNetworkManagerImpl extends ManagerBase
implements NetworkOrches
public Network createGuestNetwork(long networkOfferingId, String name,
String displayText, String gateway, String cidr, String vlanId, boolean
bypassVlanOverlapCheck, String networkDomain,
Account owner, Long domainId,
PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean
subdomainAccess, Long vpcId, String gatewayv6,
String cidrv6, Boolean
displayNetworkEnabled, String isolatedPvlan, Network.PVlanType
isolatedPvlanType, String externalId, String routerIp, String routerIpv6,
- String ip4Dns1, String ip4Dns2, String
ip6Dns1, String ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs, Integer
networkCidrSize) throws ConcurrentOperationException,
ResourceAllocationException {
+ String ip4Dns1, String ip4Dns2, String
ip6Dns1, String ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs, Integer
networkCidrSize) throws ConcurrentOperationException {
// TODO Auto-generated method stub
return null;
}
+ @Override
+ public Network createGuestNetwork(long networkOfferingId, String name,
String displayText, String gateway, String cidr, String vlanId, boolean
bypassVlanOverlapCheck, String networkDomain,
+ Account owner, Long domainId,
PhysicalNetwork physicalNetwork, long zoneId, ACLType aclType, Boolean
subdomainAccess, Long vpcId, String gatewayv6,
+ String cidrv6, Boolean
displayNetworkEnabled, String isolatedPvlan, Network.PVlanType
isolatedPvlanType, String externalId, String routerIp, String routerIpv6,
+ String ip4Dns1, String ip4Dns2, String
ip6Dns1, String ip6Dns2, Pair<Integer, Integer> vrIfaceMTUs, Integer
networkCidrSize, boolean keepMacAddressOnPublicNic) throws
ConcurrentOperationException {
+ return null;
+ }
+
/* (non-Javadoc)
* @see
com.cloud.network.NetworkManager#getPasswordResetProvider(com.cloud.network.Network)
*/
diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json
index e2045eba8df..f0a6ad3c79b 100644
--- a/ui/public/locales/en.json
+++ b/ui/public/locales/en.json
@@ -1433,6 +1433,7 @@
"label.javadistribution": "Java Runtime Distribution",
"label.javaversion": "Java Runtime Version",
"label.keep": "Keep",
+"label.keep.mac.address.on.public.nic": "Use same MAC address for public NIC
of VRs",
"label.kernelversion": "Kernel Version",
"label.key": "Key",
"label.keyboard": "Keyboard language",
diff --git a/ui/public/locales/pt_BR.json b/ui/public/locales/pt_BR.json
index c7ca36c1278..d2910a3a2b9 100644
--- a/ui/public/locales/pt_BR.json
+++ b/ui/public/locales/pt_BR.json
@@ -904,6 +904,7 @@
"label.items.selected": "item(ns) selecionados",
"label.japanese.keyboard": "Teclado japon\u00eas",
"label.keep": "Manter",
+"label.keep.mac.address.on.public.nic": "Utilizar o mesmo endere\u00e7o MAC
para a NIC p\u00fablica dos VRs",
"label.key": "Chave",
"label.keyboard": "Linguagem do teclado",
"label.keyboardtype": "Tipo de teclado",
diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js
index 37cdd0c8b98..50c2ff4250b 100644
--- a/ui/src/config/section/network.js
+++ b/ui/src/config/section/network.js
@@ -49,9 +49,14 @@ export default {
return fields
},
details: () => {
- var fields = ['name', 'id', 'description', 'type', 'traffictype',
'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway',
'asnumber', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange',
'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename',
'account', 'domainpath', 'associatednetwork', 'associatednetworkid',
'ip4routing', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2',
'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu']
- if (!isAdmin()) {
- fields = fields.filter(function (e) { return e !== 'broadcasturi' })
+ const fields = ['name', 'id', 'description', 'type', 'traffictype',
'vpcid', 'vlan', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'asnumber',
'aclname', 'ispersistent', 'restartrequired', 'reservediprange',
'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename',
'account', 'domainpath', 'associatednetwork', 'associatednetworkid',
'ip4routing', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2',
'ip6dns1', 'ip6dns2', 'publicmtu', 'privatemtu']
+ if (isAdmin()) {
+ const vlanIndex = fields.findIndex(detail => detail === 'vlan')
+ fields.splice(vlanIndex + 1, 0, 'broadcasturi')
+ fields.push({
+ field: 'keepmacaddressonpublicnic',
+ customTitle: 'keep.mac.address.on.public.nic'
+ })
}
return fields
},
@@ -233,7 +238,16 @@ export default {
fields.push(...['domain', 'zonename'])
return fields
},
- details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain',
'ip4routing', 'ip4routes', 'ip6routes', 'ispersistent', 'redundantvpcrouter',
'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1',
'ip6dns2', 'publicmtu'],
+ details: () => {
+ const fields = ['name', 'id', 'displaytext', 'cidr', 'networkdomain',
'ip4routing', 'ip4routes', 'ip6routes', 'ispersistent', 'redundantvpcrouter',
'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1',
'ip6dns2', 'publicmtu']
+ if (isAdmin()) {
+ fields.push({
+ field: 'keepmacaddressonpublicnic',
+ customTitle: 'keep.mac.address.on.public.nic'
+ })
+ }
+ return fields
+ },
searchFilters: ['name', 'zoneid', 'domainid', 'account',
'restartrequired', 'tags'],
related: [{
name: 'vm',
@@ -268,7 +282,13 @@ export default {
icon: 'edit-outlined',
label: 'label.edit',
dataView: true,
- args: ['name', 'displaytext', 'publicmtu', 'sourcenatipaddress']
+ args: () => {
+ const fields = ['name', 'displaytext', 'publicmtu',
'sourcenatipaddress']
+ if (isAdmin()) {
+ fields.push('keepmacaddressonpublicnic')
+ }
+ return fields
+ }
},
{
api: 'restartVPC',
diff --git a/ui/src/views/AutogenView.vue b/ui/src/views/AutogenView.vue
index f4aa842d8f2..066226980c5 100644
--- a/ui/src/views/AutogenView.vue
+++ b/ui/src/views/AutogenView.vue
@@ -294,6 +294,11 @@
:title="$t('label.default.network.' + field.name +
'.isolated.network')"
:tooltip="field.description"
/>
+ <tooltip-label
+ v-else-if="field.name === 'keepmacaddressonpublicnic' &&
currentAction.api === 'updateVPC'"
+ :title="$t('label.keep.mac.address.on.public.nic')"
+ :tooltip="field.description"
+ />
<tooltip-label
v-else
:title="$t('label.' + field.name)"
diff --git a/ui/src/views/network/CreateIsolatedNetworkForm.vue
b/ui/src/views/network/CreateIsolatedNetworkForm.vue
index 7dc4dbe75e3..8d6fed9cf12 100644
--- a/ui/src/views/network/CreateIsolatedNetworkForm.vue
+++ b/ui/src/views/network/CreateIsolatedNetworkForm.vue
@@ -294,6 +294,15 @@
v-model:value="form.sourcenatipaddress"
:placeholder="apiParams.sourcenatipaddress?.description"/>
</a-form-item>
+ <a-form-item name="keepMacAddressOnPublicNic"
ref="keepMacAddressOnPublicNic" v-if="isAdmin() &&
!selectedNetworkOffering?.forvpc">
+ <template #label>
+ <tooltip-label
+ :title="$t('label.keep.mac.address.on.public.nic')"
+ :tooltip="apiParams.keepmacaddressonpublicnic?.description"
+ />
+ </template>
+ <a-switch v-model:checked="form.keepMacAddressOnPublicNic" />
+ </a-form-item>
<div :span="24" class="action-button">
<a-button
:loading="actionLoading"
@@ -408,7 +417,9 @@ export default {
methods: {
initForm () {
this.formRef = ref()
- this.form = reactive({})
+ this.form = reactive({
+ keepMacAddressOnPublicNic: true
+ })
this.rules = reactive({
name: [{ required: true, message: this.$t('message.error.name') }],
zoneid: [{ type: 'number', required: true, message:
this.$t('message.error.select') }],
@@ -600,14 +611,15 @@ export default {
const formRaw = toRaw(this.form)
const values = this.handleRemoveFields(formRaw)
this.actionLoading = true
- var params = {
+ const params = {
zoneId: this.selectedZone.id,
name: values.name,
displayText: values.displaytext,
- networkOfferingId: this.selectedNetworkOffering.id
+ networkOfferingId: this.selectedNetworkOffering.id,
+ keepmacaddressonpublicnic: values.keepMacAddressOnPublicNic
}
- var usefulFields = ['gateway', 'netmask', 'cidrsize', 'startip',
'startipv4', 'endip', 'endipv4', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2',
'sourcenatipaddress', 'externalid', 'vpcid', 'vlan', 'networkdomain']
- for (var field of usefulFields) {
+ const usefulFields = ['gateway', 'netmask', 'cidrsize', 'startip',
'startipv4', 'endip', 'endipv4', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2',
'sourcenatipaddress', 'externalid', 'vpcid', 'vlan', 'networkdomain']
+ for (const field of usefulFields) {
if (this.isValidTextValueForKey(values, field)) {
params[field] = values[field]
}
diff --git a/ui/src/views/network/CreateVpc.vue
b/ui/src/views/network/CreateVpc.vue
index 2218662076e..2b66f79b387 100644
--- a/ui/src/views/network/CreateVpc.vue
+++ b/ui/src/views/network/CreateVpc.vue
@@ -202,6 +202,15 @@
v-model:value="form.sourcenatipaddress"
:placeholder="apiParams.sourcenatipaddress?.description"/>
</a-form-item>
+ <a-form-item name="keepMacAddressOnPublicNic"
ref="keepMacAddressOnPublicNic" v-if="isAdmin()">
+ <template #label>
+ <tooltip-label
+ :title="$t('label.keep.mac.address.on.public.nic')"
+ :tooltip="apiParams.keepmacaddressonpublicnic?.description"
+ />
+ </template>
+ <a-switch v-model:checked="form.keepMacAddressOnPublicNic" />
+ </a-form-item>
<a-form-item name="start" ref="start">
<template #label>
<tooltip-label :title="$t('label.start')"
:tooltip="apiParams.start.description"/>
@@ -287,7 +296,8 @@ export default {
initForm () {
this.formRef = ref()
this.form = reactive({
- start: true
+ start: true,
+ keepMacAddressOnPublicNic: true
})
this.rules = reactive({
name: [{ required: true, message:
this.$t('message.error.required.input') }],
@@ -447,7 +457,7 @@ export default {
if (this.loading) return
this.formRef.value.validate().then(() => {
const values = toRaw(this.form)
- var params = {}
+ const params = {}
if (this.owner?.account) {
params.account = this.owner.account
params.domainid = this.owner.domainid
@@ -460,7 +470,11 @@ export default {
if (input === '' || input === null || input === undefined) {
continue
}
- params[key] = input
+ if (key === 'keepMacAddressOnPublicNic') {
+ params.keepmacaddressonpublicnic = input
+ } else {
+ params[key] = input
+ }
}
if (this.selectedVpcOffering.networkmode === 'ROUTED') {
if ((values.cidr === undefined || values.cidr === '') &&
(values.cidrsize === undefined || values.cidrsize === '')) {
diff --git a/ui/src/views/network/UpdateNetwork.vue
b/ui/src/views/network/UpdateNetwork.vue
index 4a434bb15ae..2299d842a71 100644
--- a/ui/src/views/network/UpdateNetwork.vue
+++ b/ui/src/views/network/UpdateNetwork.vue
@@ -208,6 +208,15 @@
</template>
<a-switch v-model:checked="form.displaynetwork" />
</a-form-item>
+ <a-form-item name="keepMacAddressOnPublicNic"
ref="keepMacAddressOnPublicNic" v-if="isAdmin() && isUpdatingIsolatedNetwork &&
!resource?.vpcid">
+ <template #label>
+ <tooltip-label
+ :title="$t('label.keep.mac.address.on.public.nic')"
+ :tooltip="apiParams.keepmacaddressonpublicnic?.description"
+ />
+ </template>
+ <a-switch v-model:checked="form.keepMacAddressOnPublicNic" />
+ </a-form-item>
<a-form-item name="forced" ref="forced" v-if="isAdmin()">
<template #label>
<tooltip-label :title="$t('label.forced')"
:tooltip="apiParams.forced.description"/>
@@ -310,7 +319,8 @@ export default {
this.form = reactive({
displaynetwork: this.resource.displaynetwork,
privatemtu: this.resource.privatemtu,
- publicmtu: this.resource.publicmtu
+ publicmtu: this.resource.publicmtu,
+ keepMacAddressOnPublicNic: this.resource.keepmacaddressonpublicnic
})
this.rules = reactive({
name: [{ required: true, message:
this.$t('message.error.required.input') }],
@@ -393,18 +403,20 @@ export default {
const formRaw = toRaw(this.form)
const values = this.handleRemoveFields(formRaw)
this.loading = true
- var manualFields = ['name', 'networkofferingid']
+ const manualFields = ['name', 'networkofferingid']
const params = {
id: this.resource.id,
name: values.name
}
- for (var field in values) {
+ for (const field in values) {
if (manualFields.includes(field)) continue
- var fieldValue = values[field]
- if (fieldValue !== undefined &&
- fieldValue !== null &&
- (!(field in this.resourceValues) || this.resourceValues[field] !==
fieldValue)) {
- params[field] = fieldValue
+ const fieldValue = values[field]
+ if (fieldValue !== undefined && fieldValue !== null && (!(field in
this.resourceValues) || this.resourceValues[field] !== fieldValue)) {
+ if (field === 'keepMacAddressOnPublicNic') {
+ params.keepmacaddressonpublicnic = fieldValue
+ } else {
+ params[field] = fieldValue
+ }
}
}
if (values.networkofferingid !== undefined &&