From 78b68fd7e6ec9eefd6c5275fab05ec7697f8dd2f Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Sat, 10 Sep 2022 13:05:40 +0530 Subject: [PATCH] api,server: custom dns for guest network (#6425) Adds option to provide custom DNS servers for isolated network, shared network and VPC tier. New API parameters added in createNetwork API along with the corresponding response parameters. Doc PR: apache/cloudstack-documentation#276 --- .../com/cloud/agent/api/to/NetworkTO.java | 10 + .../main/java/com/cloud/network/Network.java | 8 + .../java/com/cloud/network/NetworkModel.java | 16 +- .../com/cloud/network/NetworkProfile.java | 22 + .../main/java/com/cloud/network/vpc/Vpc.java | 12 +- .../com/cloud/network/vpc/VpcService.java | 5 +- .../user/network/CreateNetworkCmd.java | 28 + .../user/network/UpdateNetworkCmd.java | 29 + .../api/command/user/vpc/CreateVPCCmd.java | 30 +- .../api/response/NetworkResponse.java | 20 +- .../cloudstack/api/response/VpcResponse.java | 32 + .../service/NetworkOrchestrationService.java | 3 +- .../com/cloud/network/vpc/VpcManager.java | 3 +- .../orchestration/NetworkOrchestrator.java | 64 +- .../NetworkOrchestratorTest.java | 156 +++- .../main/java/com/cloud/dc/DataCenterVO.java | 10 +- .../java/com/cloud/network/dao/NetworkVO.java | 45 +- .../java/com/cloud/network/vpc/VpcVO.java | 41 +- .../vpc/dao/VpcOfferingServiceMapDao.java | 2 +- .../vpc/dao/VpcOfferingServiceMapDaoImpl.java | 4 +- .../META-INF/db/schema-41710to41800.sql | 12 +- .../networkservice/BaremetaNetworkGuru.java | 14 +- .../cluster/KubernetesClusterManagerImpl.java | 106 +-- .../java/com/cloud/api/ApiResponseHelper.java | 6 + .../cloud/hypervisor/HypervisorGuruBase.java | 2 + .../cloud/network/IpAddressManagerImpl.java | 13 +- .../cloud/network/Ipv6AddressManagerImpl.java | 6 +- .../com/cloud/network/Ipv6ServiceImpl.java | 5 +- .../network/NetworkMigrationManagerImpl.java | 5 +- .../com/cloud/network/NetworkModelImpl.java | 46 ++ .../com/cloud/network/NetworkServiceImpl.java | 90 ++- .../cloud/network/guru/DirectNetworkGuru.java | 40 +- .../guru/DirectPodBasedNetworkGuru.java | 11 +- .../guru/ExternalGuestNetworkGuru.java | 6 +- .../cloud/network/guru/GuestNetworkGuru.java | 36 +- .../network/guru/PrivateNetworkGuru.java | 26 +- .../cloud/network/guru/PublicNetworkGuru.java | 26 +- .../network/router/CommandSetupHelper.java | 22 +- .../VirtualNetworkApplianceManagerImpl.java | 4 +- ...VpcVirtualNetworkApplianceManagerImpl.java | 10 + .../com/cloud/network/vpc/VpcManagerImpl.java | 47 +- .../java/com/cloud/vm/UserVmManagerImpl.java | 4 +- .../network/CreatePrivateNetworkTest.java | 2 +- .../cloud/network/Ipv6AddressManagerTest.java | 8 +- .../cloud/network/Ipv6ServiceImplTest.java | 5 +- .../cloud/network/MockNetworkModelImpl.java | 17 + .../cloud/network/NetworkModelImplTest.java | 144 ++++ .../cloud/network/NetworkServiceImplTest.java | 300 ++++++- .../network/guru/DirectNetworkGuruTest.java | 86 +- .../guru/ExternalGuestNetworkGuruTest.java | 123 +++ .../cloud/network/vpc/VpcManagerImplTest.java | 101 ++- .../com/cloud/vpc/MockNetworkManagerImpl.java | 4 +- .../com/cloud/vpc/MockNetworkModelImpl.java | 17 + .../java/com/cloud/vpc/VpcApiUnitTest.java | 2 +- .../com/cloud/vpc/dao/MockVpcDaoImpl.java | 4 +- .../dao/MockVpcOfferingServiceMapDaoImpl.java | 2 +- .../debian/opt/cloud/bin/setup/vpcrouter.sh | 12 + .../component/test_network_vpc_custom_dns.py | 732 ++++++++++++++++++ tools/marvin/marvin/lib/base.py | 8 + ui/src/config/section/network.js | 4 +- .../network/CreateIsolatedNetworkForm.vue | 100 ++- .../views/network/CreateSharedNetworkForm.vue | 296 ++++--- ui/src/views/network/CreateVpc.vue | 73 +- ui/src/views/network/UpdateNetwork.vue | 64 +- 64 files changed, 2829 insertions(+), 352 deletions(-) create mode 100644 server/src/test/java/com/cloud/network/NetworkModelImplTest.java create mode 100644 server/src/test/java/com/cloud/network/guru/ExternalGuestNetworkGuruTest.java create mode 100644 test/integration/component/test_network_vpc_custom_dns.py diff --git a/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java b/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java index b106418f068..bd08ce81101 100644 --- a/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/NetworkTO.java @@ -41,6 +41,8 @@ public class NetworkTO { protected String ip6address; protected String ip6gateway; protected String ip6cidr; + protected String ip6Dns1; + protected String ip6Dns2; public NetworkTO() { } @@ -221,4 +223,12 @@ public class NetworkTO { public boolean isSecurityGroupEnabled() { return this.isSecurityGroupEnabled; } + + public void setIp6Dns1(String ip6Dns1) { + this.ip6Dns1 = ip6Dns1; + } + + public void setIp6Dns2(String ip6Dns2) { + this.ip6Dns2 = ip6Dns2; + } } diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java index 49408b9110d..0fee5c04d25 100644 --- a/api/src/main/java/com/cloud/network/Network.java +++ b/api/src/main/java/com/cloud/network/Network.java @@ -483,5 +483,13 @@ public interface Network extends ControlledEntity, StateObject, I String getRouterIpv6(); + String getDns1(); + + String getDns2(); + + String getIp6Dns1(); + + String getIp6Dns2(); + Date getCreated(); } diff --git a/api/src/main/java/com/cloud/network/NetworkModel.java b/api/src/main/java/com/cloud/network/NetworkModel.java index 971bb308b1d..c27290f902d 100644 --- a/api/src/main/java/com/cloud/network/NetworkModel.java +++ b/api/src/main/java/com/cloud/network/NetworkModel.java @@ -17,13 +17,14 @@ package com.cloud.network; -import com.google.common.collect.ImmutableMap; - import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; +import org.apache.cloudstack.framework.config.ConfigKey; + +import com.cloud.dc.DataCenter; import com.cloud.dc.Vlan; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InvalidParameterValueException; @@ -39,10 +40,11 @@ import com.cloud.network.router.VirtualRouter; import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; import com.cloud.user.Account; +import com.cloud.utils.Pair; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; import com.cloud.vm.VirtualMachine; -import org.apache.cloudstack.framework.config.ConfigKey; +import com.google.common.collect.ImmutableMap; /** * The NetworkModel presents a read-only view into the Network data such as L2 networks, @@ -323,4 +325,12 @@ public interface NetworkModel { String getValidNetworkCidr(Network guestNetwork); + Pair getNetworkIp4Dns(final Network network, final DataCenter zone); + + Pair getNetworkIp6Dns(final Network network, final DataCenter zone); + + void verifyIp4DnsPair(final String ip4Dns1, final String ip4Dns2); + + void verifyIp6DnsPair(final String ip6Dns1, final String ip6Dns2); + } diff --git a/api/src/main/java/com/cloud/network/NetworkProfile.java b/api/src/main/java/com/cloud/network/NetworkProfile.java index f3c178767e8..5289bda6299 100644 --- a/api/src/main/java/com/cloud/network/NetworkProfile.java +++ b/api/src/main/java/com/cloud/network/NetworkProfile.java @@ -31,6 +31,8 @@ public class NetworkProfile implements Network { private final long domainId; private String dns1; private String dns2; + private String ip6Dns1; + private String ip6Dns2; private URI broadcastUri; private final State state; private boolean isRedundant; @@ -98,10 +100,12 @@ public class NetworkProfile implements Network { externalId = network.getExternalId(); } + @Override public String getDns1() { return dns1; } + @Override public String getDns2() { return dns2; } @@ -114,6 +118,24 @@ public class NetworkProfile implements Network { this.dns2 = dns2; } + @Override + public String getIp6Dns1() { + return ip6Dns1; + } + + @Override + public String getIp6Dns2() { + return ip6Dns2; + } + + public void setIp6Dns1(String ip6Dns1) { + this.ip6Dns1 = ip6Dns1; + } + + public void setIp6Dns2(String ip6Dns2) { + this.ip6Dns2 = ip6Dns2; + } + @Override public String getGuruName() { return guruName; 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 432c8839ad8..664b6eb7e95 100644 --- a/api/src/main/java/com/cloud/network/vpc/Vpc.java +++ b/api/src/main/java/com/cloud/network/vpc/Vpc.java @@ -16,12 +16,12 @@ // under the License. package com.cloud.network.vpc; +import java.util.Date; + import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.api.Identity; import org.apache.cloudstack.api.InternalIdentity; -import java.util.Date; - public interface Vpc extends ControlledEntity, Identity, InternalIdentity { public enum State { @@ -95,4 +95,12 @@ public interface Vpc extends ControlledEntity, Identity, InternalIdentity { void setRollingRestart(boolean rollingRestart); Date getCreated(); + + String getIp4Dns1(); + + String getIp4Dns2(); + + String getIp6Dns1(); + + String getIp6Dns2(); } 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 088239708f1..3829e5dbd7a 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcService.java @@ -20,6 +20,7 @@ import java.util.List; import java.util.Map; import org.apache.cloudstack.api.command.user.vpc.CreatePrivateGatewayCmd; +import org.apache.cloudstack.api.command.user.vpc.CreateVPCCmd; import org.apache.cloudstack.api.command.user.vpc.ListPrivateGatewaysCmd; import org.apache.cloudstack.api.command.user.vpc.ListStaticRoutesCmd; import org.apache.cloudstack.api.command.user.vpc.RestartVPCCmd; @@ -36,6 +37,7 @@ import com.cloud.utils.Pair; public interface VpcService { + public Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException; /** * Persists VPC record in the database * @@ -50,7 +52,8 @@ public interface VpcService { * @return * @throws ResourceAllocationException TODO */ - public Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain, Boolean displayVpc) + public Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain, + String dns1, String dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc) throws ResourceAllocationException; /** 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 903793b6e94..9973af2db9e 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 @@ -157,6 +157,18 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { description = "The network this network is associated to. only available if create a Shared network") private Long associatedNetworkId; + @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "the first IPv4 DNS for the network", since = "4.18.0") + private String ip4Dns1; + + @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "the second IPv4 DNS for the network", since = "4.18.0") + private String ip4Dns2; + + @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "the first IPv6 DNS for the network", since = "4.18.0") + private String ip6Dns1; + + @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the network", since = "4.18.0") + private String ip6Dns2; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -326,6 +338,22 @@ public class CreateNetworkCmd extends BaseCmd implements UserCmd { return aclId; } + public String getIp4Dns1() { + return ip4Dns1; + } + + public String getIp4Dns2() { + return ip4Dns2; + } + + public String getIp6Dns1() { + return ip6Dns1; + } + + public String getIp6Dns2() { + return ip6Dns2; + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// 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 bb27579992f..49a23a145f4 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 @@ -84,6 +84,18 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd implements UserCmd { @Parameter(name= ApiConstants.FORCED, type = CommandType.BOOLEAN, description = "Setting this to true will cause a forced network update,", authorized = {RoleType.Admin}) private Boolean forced; + @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "the first IPv4 DNS for the network. Empty string will update the first IPv4 DNS with the value from the zone", since = "4.18.0") + private String ip4Dns1; + + @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "the second IPv4 DNS for the network. Empty string will update the second IPv4 DNS with the value from the zone", since = "4.18.0") + private String ip4Dns2; + + @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "the first IPv6 DNS for the network. Empty string will update the first IPv6 DNS with the value from the zone", since = "4.18.0") + private String ip6Dns1; + + @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the network. Empty string will update the second IPv6 DNS with the value from the zone", since = "4.18.0") + private String ip6Dns2; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -136,6 +148,23 @@ public class UpdateNetworkCmd extends BaseAsyncCustomIdCmd implements UserCmd { } return forced; } + + public String getIp4Dns1() { + return ip4Dns1; + } + + public String getIp4Dns2() { + return ip4Dns2; + } + + public String getIp6Dns1() { + return ip6Dns1; + } + + public String getIp6Dns2() { + return ip6Dns2; + } + ///////////////////////////////////////////////////// /////////////// 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 8f6568fbe59..4df741f1d23 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 @@ -95,6 +95,18 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "an optional field, whether to the display the vpc to the end user or not", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.DNS1, type = CommandType.STRING, description = "the first IPv4 DNS for the VPC", since = "4.18.0") + private String ip4Dns1; + + @Parameter(name = ApiConstants.DNS2, type = CommandType.STRING, description = "the second IPv4 DNS for the VPC", since = "4.18.0") + private String ip4Dns2; + + @Parameter(name = ApiConstants.IP6_DNS1, type = CommandType.STRING, description = "the first IPv6 DNS for the VPC", since = "4.18.0") + private String ip6Dns1; + + @Parameter(name = ApiConstants.IP6_DNS2, type = CommandType.STRING, description = "the second IPv6 DNS for the VPC", since = "4.18.0") + private String ip6Dns2; + // /////////////////////////////////////////////////// // ///////////////// Accessors /////////////////////// // /////////////////////////////////////////////////// @@ -131,6 +143,22 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { return networkDomain; } + public String getIp4Dns1() { + return ip4Dns1; + } + + public String getIp4Dns2() { + return ip4Dns2; + } + + public String getIp6Dns1() { + return ip6Dns1; + } + + public String getIp6Dns2() { + return ip6Dns2; + } + public boolean isStart() { if (start != null) { return start; @@ -144,7 +172,7 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd { @Override public void create() throws ResourceAllocationException { - Vpc vpc = _vpcService.createVpc(getZoneId(), getVpcOffering(), getEntityOwnerId(), getVpcName(), getDisplayText(), getCidr(), getNetworkDomain(), getDisplayVpc()); + Vpc vpc = _vpcService.createVpc(this); if (vpc != null) { setEntityId(vpc.getId()); setEntityUuid(vpc.getUuid()); 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 7bed68f99b4..19edc1b3944 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 @@ -120,11 +120,11 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement private String broadcastUri; @SerializedName(ApiConstants.DNS1) - @Param(description = "the first DNS for the network") + @Param(description = "the first IPv4 DNS for the network") private String dns1; @SerializedName(ApiConstants.DNS2) - @Param(description = "the second DNS for the network") + @Param(description = "the second IPv4 DNS for the network") private String dns2; @SerializedName(ApiConstants.TYPE) @@ -287,6 +287,14 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement @Param(description = "The routes for the network to ease adding route in upstream router", since = "4.17.0") private Set ipv6Routes; + @SerializedName(ApiConstants.IP6_DNS1) + @Param(description = "the first IPv6 DNS for the network", since = "4.18.0") + private String ipv6Dns1; + + @SerializedName(ApiConstants.IP6_DNS2) + @Param(description = "the second IPv6 DNS for the network", since = "4.18.0") + private String ipv6Dns2; + public NetworkResponse() {} public Boolean getDisplayNetwork() { @@ -586,4 +594,12 @@ public class NetworkResponse extends BaseResponseWithAssociatedNetwork implement public void addIpv6Route(Ipv6RouteResponse ipv6Route) { this.ipv6Routes.add(ipv6Route); } + + public void setIpv6Dns1(String ipv6Dns1) { + this.ipv6Dns1 = ipv6Dns1; + } + + public void setIpv6Dns2(String ipv6Dns2) { + this.ipv6Dns2 = ipv6Dns2; + } } 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 3b5661f8a80..4ca594870ac 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 @@ -136,6 +136,22 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll @Param(description = "The routes for the network to ease adding route in upstream router", since = "4.17.0") private Set ipv6Routes; + @SerializedName(ApiConstants.DNS1) + @Param(description = "the first IPv4 DNS for the VPC") + private String dns1; + + @SerializedName(ApiConstants.DNS2) + @Param(description = "the second IPv4 DNS for the VPC") + private String dns2; + + @SerializedName(ApiConstants.IP6_DNS1) + @Param(description = "the first IPv6 DNS for the VPC", since = "4.18.0") + private String ipv6Dns1; + + @SerializedName(ApiConstants.IP6_DNS2) + @Param(description = "the second IPv6 DNS for the VPC", since = "4.18.0") + private String ipv6Dns2; + public void setId(final String id) { this.id = id; } @@ -257,4 +273,20 @@ public class VpcResponse extends BaseResponseWithAnnotations implements Controll public Set getIpv6Routes() { return ipv6Routes; } + + public void setDns1(String dns1) { + this.dns1 = dns1; + } + + public void setDns2(String dns2) { + this.dns2 = dns2; + } + + public void setIpv6Dns1(String ipv6Dns1) { + this.ipv6Dns1 = ipv6Dns1; + } + + public void setIpv6Dns2(String ipv6Dns2) { + this.ipv6Dns2 = ipv6Dns2; + } } 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 b79cce5f604..cde00df026e 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 @@ -187,7 +187,8 @@ public interface NetworkOrchestrationService { 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) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; + Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String routerIp, String routerIpv6, + String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; UserDataServiceProvider getPasswordResetProvider(Network network); diff --git a/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java b/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java index 56e70ca56f1..4974345d4e2 100644 --- a/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java +++ b/engine/components-api/src/main/java/com/cloud/network/vpc/VpcManager.java @@ -109,8 +109,7 @@ public interface VpcManager { Network createVpcGuestNetwork(long ntwkOffId, String name, String displayText, String gateway, String cidr, String vlanId, String networkDomain, Account owner, Long domainId, PhysicalNetwork pNtwk, long zoneId, ACLType aclType, Boolean subdomainAccess, long vpcId, Long aclId, Account caller, - Boolean displayNetworkEnabled, String externalId, String ip6Gateway, String ip6Cidr) - + Boolean displayNetworkEnabled, String externalId, String ip6Gateway, String ip6Cidr, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException; /** 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 3284511e01a..a8464f39c3e 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 @@ -38,7 +38,6 @@ import java.util.stream.Collectors; import javax.inject.Inject; import javax.naming.ConfigurationException; -import com.cloud.server.ManagementServer; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -55,6 +54,7 @@ import org.apache.cloudstack.framework.messagebus.MessageBus; import org.apache.cloudstack.framework.messagebus.PublishScope; import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.network.dao.NetworkPermissionDao; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; @@ -197,6 +197,7 @@ import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.offerings.dao.NetworkOfferingDetailsDao; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.resource.ResourceManager; +import com.cloud.server.ManagementServer; import com.cloud.user.Account; import com.cloud.user.ResourceLimitService; import com.cloud.user.User; @@ -422,6 +423,38 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra HashMap _lastNetworkIdsToFree = new HashMap(); + private void updateRouterDefaultDns(final VirtualMachineProfile vmProfile, final NicProfile nicProfile) { + if (!Type.DomainRouter.equals(vmProfile.getType()) || !nicProfile.isDefaultNic()) { + return; + } + DomainRouterVO router = routerDao.findById(vmProfile.getId()); + if (router != null && router.getVpcId() != null) { + final Vpc vpc = _vpcMgr.getActiveVpc(router.getVpcId()); + if (StringUtils.isNotBlank(vpc.getIp4Dns1())) { + nicProfile.setIPv4Dns1(vpc.getIp4Dns1()); + nicProfile.setIPv4Dns2(vpc.getIp4Dns2()); + } + if (StringUtils.isNotBlank(vpc.getIp6Dns1())) { + nicProfile.setIPv6Dns1(vpc.getIp6Dns1()); + nicProfile.setIPv6Dns2(vpc.getIp6Dns2()); + } + return; + } + List networkIds = routerNetworkDao.getRouterNetworks(vmProfile.getId()); + if (CollectionUtils.isEmpty(networkIds) || networkIds.size() > 1) { + return; + } + final NetworkVO routerNetwork = _networksDao.findById(networkIds.get(0)); + if (StringUtils.isNotBlank(routerNetwork.getDns1())) { + nicProfile.setIPv4Dns1(routerNetwork.getDns1()); + nicProfile.setIPv4Dns2(routerNetwork.getDns2()); + } + if (StringUtils.isNotBlank(routerNetwork.getIp6Dns1())) { + nicProfile.setIPv6Dns1(routerNetwork.getIp6Dns1()); + nicProfile.setIPv6Dns2(routerNetwork.getIp6Dns2()); + } + } + @Override @DB public boolean configure(final String name, final Map params) throws ConfigurationException { @@ -1951,7 +1984,7 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra profile.setSecurityGroupEnabled(_networkModel.isSecurityGroupSupportedInNetwork(network)); guru.updateNicProfile(profile, network); - + updateRouterDefaultDns(vmProfile, profile); configureExtraDhcpOptions(network, nicId); return profile; } @@ -2430,7 +2463,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra // create network for private gateway 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); + vpcId, null, null, true, null, null, null, true, null, null, + null, null, null, null); } @Override @@ -2438,18 +2472,21 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra 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) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { + final Boolean isDisplayNetworkEnabled, final String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, + String routerIp, String routerIpv6, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2) 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); + isDisplayNetworkEnabled, isolatedPvlan, isolatedPvlanType, externalId, false, routerIp, routerIpv6, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); } @DB private 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, final Boolean isPrivateNetwork, String routerIp, String routerIpv6) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { + 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) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { final NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); final DataCenterVO zone = _dcDao.findById(zoneId); @@ -2724,6 +2761,21 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra userNetwork.setRouterIpv6(routerIpv6); } + if (!GuestType.L2.equals(userNetwork.getGuestType())) { + if (StringUtils.isNotBlank(ip4Dns1)) { + userNetwork.setDns1(ip4Dns1); + } + if (StringUtils.isNotBlank(ip4Dns2)) { + userNetwork.setDns2(ip4Dns2); + } + if (StringUtils.isNotBlank(ip6Dns1)) { + userNetwork.setIp6Dns1(ip6Dns1); + } + if (StringUtils.isNotBlank(ip6Dns2)) { + userNetwork.setIp6Dns2(ip6Dns2); + } + } + if (vlanIdFinal != null) { if (isolatedPvlan == null) { URI uri = null; diff --git a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java index ff0a6faae2d..f9adc9c643d 100644 --- a/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java +++ b/engine/orchestration/src/test/java/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java @@ -29,8 +29,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import com.cloud.network.dao.PhysicalNetworkVO; -import com.cloud.utils.exception.CloudRuntimeException; import org.apache.log4j.Logger; import org.junit.Assert; import org.junit.Before; @@ -43,30 +41,44 @@ import org.mockito.Mockito; import com.cloud.dc.Vlan; import com.cloud.dc.VlanVO; import com.cloud.dc.dao.VlanDao; +import com.cloud.deploy.DeployDestination; import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.IpAddress.State; import com.cloud.network.Network; import com.cloud.network.Network.GuestType; import com.cloud.network.Network.Service; import com.cloud.network.NetworkModel; -import com.cloud.network.IpAddress.State; import com.cloud.network.Networks.TrafficType; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkServiceMapDao; import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.dao.RouterNetworkDao; import com.cloud.network.element.DhcpServiceProvider; +import com.cloud.network.guru.GuestNetworkGuru; import com.cloud.network.guru.NetworkGuru; +import com.cloud.network.vpc.VpcManager; +import com.cloud.network.vpc.VpcVO; import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.Ip; +import com.cloud.vm.DomainRouterVO; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.NicDao; +import com.cloud.vm.dao.NicExtraDhcpOptionDao; import com.cloud.vm.dao.NicIpAliasDao; import com.cloud.vm.dao.NicSecondaryIpDao; @@ -89,6 +101,11 @@ public class NetworkOrchestratorTest extends TestCase { private static final long networkOfferingId = 1l; + final String[] ip4Dns = {"5.5.5.5", "6.6.6.6"}; + final String[] ip6Dns = {"2001:4860:4860::5555", "2001:4860:4860::6666"}; + final String[] ip4AltDns = {"7.7.7.7", "8.8.8.8"}; + final String[] ip6AltDns = {"2001:4860:4860::7777", "2001:4860:4860::8888"}; + @Override @Before public void setUp() { @@ -101,6 +118,11 @@ public class NetworkOrchestratorTest extends TestCase { testOrchastrator._nicIpAliasDao = mock(NicIpAliasDao.class); testOrchastrator._ipAddressDao = mock(IPAddressDao.class); testOrchastrator._vlanDao = mock(VlanDao.class); + testOrchastrator._networkModel = mock(NetworkModel.class); + testOrchastrator._nicExtraDhcpOptionDao = mock(NicExtraDhcpOptionDao.class); + testOrchastrator.routerDao = mock(DomainRouterDao.class); + testOrchastrator.routerNetworkDao = mock(RouterNetworkDao.class); + testOrchastrator._vpcMgr = mock(VpcManager.class); DhcpServiceProvider provider = mock(DhcpServiceProvider.class); Map capabilities = new HashMap(); @@ -556,4 +578,132 @@ public class NetworkOrchestratorTest extends TestCase { Assert.assertEquals(expectedIsolation, resultUri.getScheme()); Assert.assertEquals(expectedUri, resultUri.toString()); } + + private NicProfile prepareMocksAndRunPrepareNic(VirtualMachine.Type vmType, boolean isDefaultNic, boolean isVpcRouter, boolean routerResourceHasCustomDns) { + Hypervisor.HypervisorType hypervisorType = Hypervisor.HypervisorType.KVM; + Long nicId = 1L; + Long vmId = 1L; + Long networkId = 1L; + Integer networkRate = 200; + Network network = Mockito.mock(Network.class); + Mockito.when(network.getGuruName()).thenReturn(GuestNetworkGuru.class.getSimpleName()); + Mockito.when(network.getDns1()).thenReturn(ip4Dns[0]); + Mockito.when(network.getDns2()).thenReturn(ip4Dns[1]); + Mockito.when(network.getIp6Dns1()).thenReturn(ip6Dns[0]); + Mockito.when(network.getIp6Dns2()).thenReturn(ip6Dns[1]); + Mockito.when(testOrchastrator._networkModel.getNetworkRate(networkId, vmId)).thenReturn(networkRate); + NicVO nicVO = Mockito.mock(NicVO.class); + Mockito.when(nicVO.isDefaultNic()).thenReturn(isDefaultNic); + Mockito.when(testOrchastrator._nicDao.findById(nicId)).thenReturn(nicVO); + Mockito.when(testOrchastrator._nicDao.update(nicId, nicVO)).thenReturn(true); + Mockito.when(testOrchastrator._networkModel.isSecurityGroupSupportedInNetwork(network)).thenReturn(false); + Mockito.when(testOrchastrator._networkModel.getNetworkTag(hypervisorType, network)).thenReturn(null); + Mockito.when(testOrchastrator._ntwkSrvcDao.getDistinctProviders(networkId)).thenReturn(new ArrayList<>()); + testOrchastrator.networkElements = new ArrayList<>(); + Mockito.when(testOrchastrator._nicExtraDhcpOptionDao.listByNicId(nicId)).thenReturn(new ArrayList<>()); + Mockito.when(testOrchastrator._ntwkSrvcDao.areServicesSupportedInNetwork(networkId, Service.Dhcp)).thenReturn(false); + VirtualMachineProfile virtualMachineProfile = Mockito.mock(VirtualMachineProfile.class); + Mockito.when(virtualMachineProfile.getType()).thenReturn(vmType); + Mockito.when(virtualMachineProfile.getId()).thenReturn(vmId); + DeployDestination deployDestination = Mockito.mock(DeployDestination.class); + ReservationContext reservationContext = Mockito.mock(ReservationContext.class); + Mockito.doAnswer((org.mockito.stubbing.Answer) invocation -> { + NicProfile nicProfile = (NicProfile) invocation.getArguments()[0]; + Network ntwk = (Network) invocation.getArguments()[1]; + nicProfile.setIPv4Dns1(ntwk.getDns1()); + nicProfile.setIPv4Dns2(ntwk.getDns2()); + nicProfile.setIPv6Dns1(ntwk.getIp6Dns1()); + nicProfile.setIPv6Dns2(ntwk.getIp6Dns2()); + return null; + }).when(guru).updateNicProfile(Mockito.any(NicProfile.class), Mockito.any(Network.class)); + DomainRouterVO routerVO = Mockito.mock(DomainRouterVO.class); + if (isVpcRouter) { + Long vpcId = 1L; + Mockito.when(routerVO.getVpcId()).thenReturn(vpcId); + VpcVO vpcVO = Mockito.mock(VpcVO.class); + if (routerResourceHasCustomDns) { + Mockito.when(vpcVO.getIp4Dns1()).thenReturn(ip4AltDns[0]); + Mockito.when(vpcVO.getIp4Dns2()).thenReturn(ip4AltDns[1]); + Mockito.when(vpcVO.getIp6Dns1()).thenReturn(ip6AltDns[0]); + Mockito.when(vpcVO.getIp6Dns2()).thenReturn(ip6AltDns[1]); + } else { + Mockito.when(vpcVO.getIp4Dns1()).thenReturn(null); + Mockito.when(vpcVO.getIp6Dns1()).thenReturn(null); + } + Mockito.when(testOrchastrator._vpcMgr.getActiveVpc(vpcId)).thenReturn(vpcVO); + } else { + Mockito.when(routerVO.getVpcId()).thenReturn(null); + Long routerNetworkId = 2L; + NetworkVO routerNetworkVO = Mockito.mock(NetworkVO.class); + if (routerResourceHasCustomDns) { + Mockito.when(routerNetworkVO.getDns1()).thenReturn(ip4AltDns[0]); + Mockito.when(routerNetworkVO.getDns2()).thenReturn(ip4AltDns[1]); + Mockito.when(routerNetworkVO.getIp6Dns1()).thenReturn(ip6AltDns[0]); + Mockito.when(routerNetworkVO.getIp6Dns2()).thenReturn(ip6AltDns[1]); + } else { + Mockito.when(routerNetworkVO.getDns1()).thenReturn(null); + Mockito.when(routerNetworkVO.getIp6Dns1()).thenReturn(null); + } + Mockito.when(testOrchastrator.routerNetworkDao.getRouterNetworks(vmId)).thenReturn(List.of(routerNetworkId)); + Mockito.when(testOrchastrator._networksDao.findById(routerNetworkId)).thenReturn(routerNetworkVO); + } + Mockito.when(testOrchastrator.routerDao.findById(vmId)).thenReturn(routerVO); + NicProfile profile = null; + try { + profile = testOrchastrator.prepareNic(virtualMachineProfile, deployDestination, reservationContext, nicId, network); + } catch (InsufficientCapacityException | ResourceUnavailableException e) { + Assert.fail(String.format("Failure with exception %s", e.getMessage())); + } + return profile; + } + + @Test + public void testPrepareNicUserVm() { + NicProfile profile = prepareMocksAndRunPrepareNic(Type.User, false, false, false); + Assert.assertNotNull(profile); + Assert.assertEquals(ip4Dns[0], profile.getIPv4Dns1()); + Assert.assertEquals(ip4Dns[1], profile.getIPv4Dns2()); + Assert.assertEquals(ip6Dns[0], profile.getIPv6Dns1()); + Assert.assertEquals(ip6Dns[1], profile.getIPv6Dns2()); + } + + @Test + public void testPrepareNicVpcRouterVm() { + NicProfile profile = prepareMocksAndRunPrepareNic(Type.DomainRouter, true, true, true); + Assert.assertNotNull(profile); + Assert.assertEquals(ip4AltDns[0], profile.getIPv4Dns1()); + Assert.assertEquals(ip4AltDns[1], profile.getIPv4Dns2()); + Assert.assertEquals(ip6AltDns[0], profile.getIPv6Dns1()); + Assert.assertEquals(ip6AltDns[1], profile.getIPv6Dns2()); + } + + @Test + public void testPrepareNicVpcRouterNoDnsVm() { + NicProfile profile = prepareMocksAndRunPrepareNic(Type.DomainRouter, true, true, false); + Assert.assertNotNull(profile); + Assert.assertEquals(ip4Dns[0], profile.getIPv4Dns1()); + Assert.assertEquals(ip4Dns[1], profile.getIPv4Dns2()); + Assert.assertEquals(ip6Dns[0], profile.getIPv6Dns1()); + Assert.assertEquals(ip6Dns[1], profile.getIPv6Dns2()); + } + + @Test + public void testPrepareNicNetworkRouterVm() { + NicProfile profile = prepareMocksAndRunPrepareNic(Type.DomainRouter, true, false, true); + Assert.assertNotNull(profile); + Assert.assertEquals(ip4AltDns[0], profile.getIPv4Dns1()); + Assert.assertEquals(ip4AltDns[1], profile.getIPv4Dns2()); + Assert.assertEquals(ip6AltDns[0], profile.getIPv6Dns1()); + Assert.assertEquals(ip6AltDns[1], profile.getIPv6Dns2()); + } + + @Test + public void testPrepareNicNetworkRouterNoDnsVm() { + NicProfile profile = prepareMocksAndRunPrepareNic(Type.DomainRouter, true, false, false); + Assert.assertNotNull(profile); + Assert.assertEquals(ip4Dns[0], profile.getIPv4Dns1()); + Assert.assertEquals(ip4Dns[1], profile.getIPv4Dns2()); + Assert.assertEquals(ip6Dns[0], profile.getIPv6Dns1()); + Assert.assertEquals(ip6Dns[1], profile.getIPv6Dns2()); + } } diff --git a/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java b/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java index d0f3192c60c..038daeaa10b 100644 --- a/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java +++ b/engine/schema/src/main/java/com/cloud/dc/DataCenterVO.java @@ -183,15 +183,15 @@ public class DataCenterVO implements DataCenter { this.firewallProvider = firewallProvider; } - public DataCenterVO(long id, String name, String description, String dns1, String dns2, String dns3, String dns4, String guestCidr, String domain, Long domainId, + public DataCenterVO(long id, String name, String description, String dns1, String dns2, String internalDns1, String internalDns2, String guestCidr, String domain, Long domainId, NetworkType zoneType, String zoneToken, String domainSuffix) { - this(name, description, dns1, dns2, dns3, dns4, guestCidr, domain, domainId, zoneType, zoneToken, domainSuffix, false, false, null, null); + this(name, description, dns1, dns2, internalDns1, internalDns2, guestCidr, domain, domainId, zoneType, zoneToken, domainSuffix, false, false, null, null); this.id = id; this.allocationState = Grouping.AllocationState.Enabled; this.uuid = UUID.randomUUID().toString(); } - public DataCenterVO(String name, String description, String dns1, String dns2, String dns3, String dns4, String guestCidr, String domain, Long domainId, + public DataCenterVO(String name, String description, String dns1, String dns2, String internalDns1, String internalDns2, String guestCidr, String domain, Long domainId, NetworkType zoneType, String zoneToken, String domainSuffix, boolean securityGroupEnabled, boolean localStorageEnabled, String ip6Dns1, String ip6Dns2) { this.name = name; this.description = description; @@ -199,8 +199,8 @@ public class DataCenterVO implements DataCenter { this.dns2 = dns2; this.ip6Dns1 = ip6Dns1; this.ip6Dns2 = ip6Dns2; - this.internalDns1 = dns3; - this.internalDns2 = dns4; + this.internalDns1 = internalDns1; + this.internalDns2 = internalDns2; this.guestNetworkCidr = guestCidr; this.domain = domain; this.domainId = domainId; 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 4dbcda670a7..e6869b4b264 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 @@ -30,6 +30,7 @@ import javax.persistence.TableGenerator; import javax.persistence.Transient; import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.commons.lang3.StringUtils; import com.cloud.network.Network; import com.cloud.network.Networks.BroadcastDomainType; @@ -106,9 +107,6 @@ public class NetworkVO implements Network { @Column(name = "redundant") boolean redundant; - @Column(name = "dns1") - String dns1; - @Column(name = "domain_id") long domainId; @@ -125,9 +123,18 @@ public class NetworkVO implements Network { @Column(name = "guru_data", length = 1024) String guruData; + @Column(name = "dns1") + String dns1; + @Column(name = "dns2") String dns2; + @Column(name = "ip6Dns1") + String ip6Dns1; + + @Column(name = "ip6Dns2") + String ip6Dns2; + @Column(name = "network_domain") String networkDomain; @@ -255,6 +262,18 @@ public class NetworkVO implements Network { uuid = UUID.randomUUID().toString(); ip6Gateway = that.getIp6Gateway(); ip6Cidr = that.getIp6Cidr(); + if (StringUtils.isNotBlank(that.getDns1())) { + this.dns1 = that.getDns1(); + } + if (StringUtils.isNotBlank(that.getDns2())) { + this.dns2 = that.getDns2(); + } + if (StringUtils.isNotBlank(that.getIp6Dns1())) { + this.ip6Dns1 = that.getIp6Dns1(); + } + if (StringUtils.isNotBlank(that.getIp6Dns2())) { + this.ip6Dns2 = that.getIp6Dns2(); + } this.externalId = externalId; } @@ -471,6 +490,7 @@ public class NetworkVO implements Network { return dataCenterId; } + @Override public String getDns1() { return dns1; } @@ -479,6 +499,7 @@ public class NetworkVO implements Network { dns1 = dns; } + @Override public String getDns2() { return dns2; } @@ -487,6 +508,24 @@ public class NetworkVO implements Network { dns2 = dns; } + @Override + public String getIp6Dns1() { + return ip6Dns1; + } + + public void setIp6Dns1(String ip6Dns1) { + this.ip6Dns1 = ip6Dns1; + } + + @Override + public String getIp6Dns2() { + return ip6Dns2; + } + + public void setIp6Dns2(String ip6Dns2) { + this.ip6Dns2 = ip6Dns2; + } + @Override public String getName() { return name; 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 83034b3fdbe..c2c57b11fbf 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 @@ -89,6 +89,18 @@ public class VpcVO implements Vpc { @Column(name = "region_level_vpc") boolean regionLevelVpc = false; + @Column(name = "dns1") + String ip4Dns1; + + @Column(name = "dns2") + String ip4Dns2; + + @Column(name = "ip6Dns1") + String ip6Dns1; + + @Column(name = "ip6Dns2") + String ip6Dns2; + @Transient boolean rollingRestart = false; @@ -97,8 +109,9 @@ public class VpcVO implements Vpc { } public VpcVO(final long zoneId, final String name, final String displayText, final long accountId, final long domainId, - final long vpcOffId, final String cidr, final String networkDomain, final boolean useDistributedRouter, - final boolean regionLevelVpc, final boolean isRedundant) { + final long vpcOffId, final String cidr, final String networkDomain, final boolean useDistributedRouter, + final boolean regionLevelVpc, final boolean isRedundant, final String ip4Dns1, final String ip4Dns2, + final String ip6Dns1, final String ip6Dns2) { this.zoneId = zoneId; this.name = name; this.displayText = displayText; @@ -112,6 +125,10 @@ public class VpcVO implements Vpc { usesDistributedRouter = useDistributedRouter; this.regionLevelVpc = regionLevelVpc; redundant = isRedundant; + this.ip4Dns1 = ip4Dns1; + this.ip4Dns2 = ip4Dns2; + this.ip6Dns1 = ip6Dns1; + this.ip6Dns2 = ip6Dns2; } @Override @@ -255,4 +272,24 @@ public class VpcVO implements Vpc { public boolean usesDistributedRouter() { return usesDistributedRouter; } + + @Override + public String getIp4Dns1() { + return ip4Dns1; + } + + @Override + public String getIp4Dns2() { + return ip4Dns2; + } + + @Override + public String getIp6Dns1() { + return ip6Dns1; + } + + @Override + public String getIp6Dns2() { + return ip6Dns2; + } } diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java index 56de31819d2..06cfd25e670 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDao.java @@ -31,7 +31,7 @@ public interface VpcOfferingServiceMapDao extends GenericDao listServicesForVpcOffering(long vpcOfferingId); diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java index 9e14bb5348f..c7400f6edfd 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/dao/VpcOfferingServiceMapDaoImpl.java @@ -65,9 +65,9 @@ public class VpcOfferingServiceMapDaoImpl extends GenericDaoBase sc = MultipleServicesSearch.create(); - sc.setParameters("vpcOffId", networkOfferingId); + sc.setParameters("vpcOffId", vpcOfferingId); if (services != null) { String[] servicesStr = new String[services.length]; diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41710to41800.sql b/engine/schema/src/main/resources/META-INF/db/schema-41710to41800.sql index 4ec812cc1c0..f57b75c7376 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41710to41800.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41710to41800.sql @@ -18,7 +18,6 @@ --; -- Schema upgrade from 4.17.1.0 to 4.18.0.0 --; - -- Enable CPU cap for default system offerings; UPDATE `cloud`.`service_offering` so SET so.limit_cpu_use = 1 @@ -27,3 +26,14 @@ WHERE so.default_use = 1 AND so.vm_type IN ('domainrouter', 'secondarystoragevm' -- Add cidr_list column to load_balancing_rules ALTER TABLE `cloud`.`load_balancing_rules` ADD cidr_list VARCHAR(4096); + +-- Alter networks table to add ip6dns1 and ip6dns2 +ALTER TABLE `cloud`.`networks` + ADD COLUMN `ip6dns1` varchar(255) DEFAULT NULL COMMENT 'first IPv6 DNS for the network' AFTER `dns2`, + ADD COLUMN `ip6dns2` varchar(255) DEFAULT NULL COMMENT 'second IPv6 DNS for the network' AFTER `ip6dns1`; +-- Alter vpc table to add dns1, dns2, ip6dns1 and ip6dns2 +ALTER TABLE `cloud`.`vpc` + ADD COLUMN `dns1` varchar(255) DEFAULT NULL COMMENT 'first IPv4 DNS for the vpc' AFTER `network_domain`, + ADD COLUMN `dns2` varchar(255) DEFAULT NULL COMMENT 'second IPv4 DNS for the vpc' AFTER `dns1`, + ADD COLUMN `ip6dns1` varchar(255) DEFAULT NULL COMMENT 'first IPv6 DNS for the vpc' AFTER `dns2`, + ADD COLUMN `ip6dns2` varchar(255) DEFAULT NULL COMMENT 'second IPv6 DNS for the vpc' AFTER `ip6dns1`; diff --git a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java index 68acee88abe..bf6932f05d4 100644 --- a/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java +++ b/plugins/hypervisors/baremetal/src/main/java/com/cloud/baremetal/networkservice/BaremetaNetworkGuru.java @@ -41,6 +41,7 @@ import com.cloud.host.dao.HostDao; import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; +import com.cloud.network.NetworkModel; import com.cloud.network.Networks.AddressFormat; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.IsolationType; @@ -49,6 +50,7 @@ import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.guru.DirectPodBasedNetworkGuru; import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.utils.Pair; import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallbackNoReturn; import com.cloud.utils.db.TransactionStatus; @@ -74,6 +76,8 @@ public class BaremetaNetworkGuru extends DirectPodBasedNetworkGuru { PodVlanMapDao _podVlanDao; @Inject IpAddressManager _ipAddrMgr; + @Inject + NetworkModel networkModel; @Override public void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) @@ -125,8 +129,9 @@ public class BaremetaNetworkGuru extends DirectPodBasedNetworkGuru { } DataCenter dc = _dcDao.findById(network.getDataCenterId()); - nic.setIPv4Dns1(dc.getDns1()); - nic.setIPv4Dns2(dc.getDns2()); + Pair dns = networkModel.getNetworkIp4Dns(network, dc); + nic.setIPv4Dns1(dns.first()); + nic.setIPv4Dns2(dns.second()); /* * Pod pod = dest.getPod(); Pair ip = @@ -167,7 +172,8 @@ public class BaremetaNetworkGuru extends DirectPodBasedNetworkGuru { nic.setReservationId(String.valueOf(ip.getVlanTag())); nic.setMacAddress(ip.getMacAddress()); } - nic.setIPv4Dns1(dc.getDns1()); - nic.setIPv4Dns2(dc.getDns2()); + Pair dns = networkModel.getNetworkIp4Dns(network, dc); + nic.setIPv4Dns1(dns.first()); + nic.setIPv4Dns2(dns.second()); } } diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java index fa96e8b24b7..e1c205e29cb 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterManagerImpl.java @@ -16,6 +16,58 @@ // under the License. package com.cloud.kubernetes.cluster; +import static com.cloud.utils.NumbersUtil.toHumanReadableSize; + +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; + +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.acl.SecurityChecker; +import org.apache.cloudstack.annotation.AnnotationService; +import org.apache.cloudstack.annotation.dao.AnnotationDao; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiConstants.VMDetails; +import org.apache.cloudstack.api.ResponseObject.ResponseView; +import org.apache.cloudstack.api.command.user.kubernetes.cluster.CreateKubernetesClusterCmd; +import org.apache.cloudstack.api.command.user.kubernetes.cluster.DeleteKubernetesClusterCmd; +import org.apache.cloudstack.api.command.user.kubernetes.cluster.GetKubernetesClusterConfigCmd; +import org.apache.cloudstack.api.command.user.kubernetes.cluster.ListKubernetesClustersCmd; +import org.apache.cloudstack.api.command.user.kubernetes.cluster.ScaleKubernetesClusterCmd; +import org.apache.cloudstack.api.command.user.kubernetes.cluster.StartKubernetesClusterCmd; +import org.apache.cloudstack.api.command.user.kubernetes.cluster.StopKubernetesClusterCmd; +import org.apache.cloudstack.api.command.user.kubernetes.cluster.UpgradeKubernetesClusterCmd; +import org.apache.cloudstack.api.response.KubernetesClusterConfigResponse; +import org.apache.cloudstack.api.response.KubernetesClusterResponse; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.cloudstack.config.ApiServiceConfiguration; +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.managed.context.ManagedContextRunnable; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.log4j.Level; +import org.apache.log4j.Logger; + import com.cloud.api.ApiDBUtils; import com.cloud.api.query.dao.NetworkOfferingJoinDao; import com.cloud.api.query.dao.TemplateJoinDao; @@ -117,56 +169,6 @@ import com.cloud.utils.net.NetUtils; import com.cloud.vm.VMInstanceVO; import com.cloud.vm.VirtualMachine; import com.cloud.vm.dao.VMInstanceDao; -import org.apache.cloudstack.acl.ControlledEntity; -import org.apache.cloudstack.acl.SecurityChecker; -import org.apache.cloudstack.annotation.AnnotationService; -import org.apache.cloudstack.annotation.dao.AnnotationDao; -import org.apache.cloudstack.api.ApiConstants; -import org.apache.cloudstack.api.ApiConstants.VMDetails; -import org.apache.cloudstack.api.ResponseObject.ResponseView; -import org.apache.cloudstack.api.command.user.kubernetes.cluster.CreateKubernetesClusterCmd; -import org.apache.cloudstack.api.command.user.kubernetes.cluster.DeleteKubernetesClusterCmd; -import org.apache.cloudstack.api.command.user.kubernetes.cluster.GetKubernetesClusterConfigCmd; -import org.apache.cloudstack.api.command.user.kubernetes.cluster.ListKubernetesClustersCmd; -import org.apache.cloudstack.api.command.user.kubernetes.cluster.ScaleKubernetesClusterCmd; -import org.apache.cloudstack.api.command.user.kubernetes.cluster.StartKubernetesClusterCmd; -import org.apache.cloudstack.api.command.user.kubernetes.cluster.StopKubernetesClusterCmd; -import org.apache.cloudstack.api.command.user.kubernetes.cluster.UpgradeKubernetesClusterCmd; -import org.apache.cloudstack.api.response.KubernetesClusterConfigResponse; -import org.apache.cloudstack.api.response.KubernetesClusterResponse; -import org.apache.cloudstack.api.response.ListResponse; -import org.apache.cloudstack.api.response.UserVmResponse; -import org.apache.cloudstack.config.ApiServiceConfiguration; -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.managed.context.ManagedContextRunnable; -import org.apache.commons.codec.binary.Base64; -import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.log4j.Level; -import org.apache.log4j.Logger; - -import javax.inject.Inject; -import javax.naming.ConfigurationException; -import java.math.BigInteger; -import java.net.MalformedURLException; -import java.net.URL; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; - -import static com.cloud.utils.NumbersUtil.toHumanReadableSize; public class KubernetesClusterManagerImpl extends ManagerBase implements KubernetesClusterService { @@ -761,7 +763,9 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements Kuberne try { network = networkMgr.createGuestNetwork(networkOffering.getId(), clusterName + "-network", owner.getAccountName() + "-network", - null, null, null, false, null, owner, null, physicalNetwork, zone.getId(), ControlledEntity.ACLType.Account, null, null, null, null, true, null, null, null, null, null); + null, null, null, false, null, owner, null, physicalNetwork, zone.getId(), + ControlledEntity.ACLType.Account, null, null, null, null, true, null, + null, null, null, null, null, null, null, null); } catch (ConcurrentOperationException | InsufficientCapacityException | ResourceAllocationException e) { logAndThrow(Level.ERROR, String.format("Unable to create network for the Kubernetes cluster: %s", clusterName)); } diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index 411d0e1c0a3..52502f39cf1 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -2374,6 +2374,8 @@ public class ApiResponseHelper implements ResponseGenerator { response.setDns1(profile.getDns1()); response.setDns2(profile.getDns2()); + response.setIpv6Dns1(profile.getIp6Dns1()); + response.setIpv6Dns2(profile.getIp6Dns2()); // populate capability Map> serviceCapabilitiesMap = ApiDBUtils.getNetworkCapabilities(network.getId(), network.getDataCenterId()); Map> serviceProviderMap = ApiDBUtils.listNetworkOfferingServices(network.getNetworkOfferingId()); @@ -3235,6 +3237,10 @@ public class ApiResponseHelper implements ResponseGenerator { response.setHasAnnotation(annotationDao.hasAnnotations(vpc.getUuid(), AnnotationService.EntityType.VPC.name(), _accountMgr.isRootAdmin(CallContext.current().getCallingAccount().getId()))); ipv6Service.updateIpv6RoutesForVpcResponse(vpc, response); + response.setDns1(vpc.getIp4Dns1()); + response.setDns2(vpc.getIp4Dns2()); + response.setIpv6Dns1(vpc.getIp6Dns1()); + response.setIpv6Dns2(vpc.getIp6Dns2()); response.setObjectName("vpc"); return response; } diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java index 20b7a2b67d6..3c8b6c58d9b 100644 --- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java @@ -143,6 +143,8 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis to.setIp6Address(profile.getIPv6Address()); to.setIp6Gateway(profile.getIPv6Gateway()); to.setIp6Cidr(profile.getIPv6Cidr()); + to.setIp6Dns1(profile.getIPv6Dns1()); + to.setIp6Dns2(profile.getIPv6Dns2()); NetworkVO network = networkDao.findById(profile.getNetworkId()); to.setNetworkUuid(network.getUuid()); diff --git a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java index 7ec4d0fd37e..010925b7006 100644 --- a/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java +++ b/server/src/main/java/com/cloud/network/IpAddressManagerImpl.java @@ -1802,7 +1802,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() + " as a part of createVlanIpRange process"); guestNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() - + "-network", null, null, null, false, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, null, null, null, null, null); + + "-network", null, null, null, false, null, owner, null, physicalNetwork, zoneId, ACLType.Account, null, null, null, null, true, null, null, null, null, null, + null, null, null, null); if (guestNetwork == null) { s_logger.warn("Failed to create default Virtual network for the account " + accountId + "in zone " + zoneId); throw new CloudRuntimeException("Failed to create a Guest Isolated Networks with SourceNAT " @@ -2210,8 +2211,9 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage nic.setMacAddress(ip.getMacAddress()); } } - nic.setIPv4Dns1(dc.getDns1()); - nic.setIPv4Dns2(dc.getDns2()); + Pair dns = _networkModel.getNetworkIp4Dns(network, dc); + nic.setIPv4Dns1(dns.first()); + nic.setIPv4Dns2(dns.second()); } _ipv6Mgr.setNicIp6Address(nic, dc, network); @@ -2260,8 +2262,9 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage nic.setMacAddress(_networkModel.getNextAvailableMacAddressInNetwork(network.getId())); } } - nic.setIPv4Dns1(dc.getDns1()); - nic.setIPv4Dns2(dc.getDns2()); + Pair dns = _networkModel.getNetworkIp4Dns(network, dc); + nic.setIPv4Dns1(dns.first()); + nic.setIPv4Dns2(dns.second()); } _ipv6Mgr.setNicIp6Address(nic, dc, network); diff --git a/server/src/main/java/com/cloud/network/Ipv6AddressManagerImpl.java b/server/src/main/java/com/cloud/network/Ipv6AddressManagerImpl.java index 2a53708fc59..0d683292cfa 100644 --- a/server/src/main/java/com/cloud/network/Ipv6AddressManagerImpl.java +++ b/server/src/main/java/com/cloud/network/Ipv6AddressManagerImpl.java @@ -42,6 +42,7 @@ import com.cloud.network.dao.NetworkDetailsDao; import com.cloud.network.dao.UserIpv6AddressDao; import com.cloud.user.Account; import com.cloud.utils.NumbersUtil; +import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.net.NetUtils; @@ -226,8 +227,9 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa IPv6Address.class.getName(), null); } } - nic.setIPv6Dns1(dc.getIp6Dns1()); - nic.setIPv6Dns2(dc.getIp6Dns2()); + Pair dns = _networkModel.getNetworkIp6Dns(network, dc); + nic.setIPv6Dns1(dns.first()); + nic.setIPv6Dns2(dns.second()); } } diff --git a/server/src/main/java/com/cloud/network/Ipv6ServiceImpl.java b/server/src/main/java/com/cloud/network/Ipv6ServiceImpl.java index dd0f6c49e37..47537a69d19 100644 --- a/server/src/main/java/com/cloud/network/Ipv6ServiceImpl.java +++ b/server/src/main/java/com/cloud/network/Ipv6ServiceImpl.java @@ -447,8 +447,9 @@ public class Ipv6ServiceImpl extends ComponentLifecycleBase implements Ipv6Servi } else { nic.setFormat(Networks.AddressFormat.Ip6); } - nic.setIPv6Dns1(dc.getIp6Dns1()); - nic.setIPv6Dns2(dc.getIp6Dns2()); + Pair dns = networkModel.getNetworkIp6Dns(network, dc); + nic.setIPv6Dns1(dns.first()); + nic.setIPv6Dns2(dns.second()); } } diff --git a/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java b/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java index 748ea5c68a6..1df42a54acd 100644 --- a/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java @@ -297,8 +297,9 @@ public class NetworkMigrationManagerImpl implements NetworkMigrationManager { Vpc copyOfVpc; long copyOfVpcId; try { - copyOfVpc = _vpcService.createVpc(vpc.getZoneId(), vpcOfferingId, vpc.getAccountId(), vpc.getName(), vpc.getDisplayText(), vpc.getCidr(), - vpc.getNetworkDomain(), vpc.isDisplay()); + 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()); copyOfVpcId = copyOfVpc.getId(); //on resume of migration the uuid will be swapped already. So the copy will have the value of the original vpcid. _resourceTagDao.persist(new ResourceTagVO(MIGRATION, Long.toString(vpcId), vpc.getAccountId(), vpc.getDomainId(), copyOfVpcId, ResourceTag.ResourceObjectType.Vpc, null, vpc.getUuid())); diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java b/server/src/main/java/com/cloud/network/NetworkModelImpl.java index c601c13e6ad..d183ba0dd92 100644 --- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java @@ -79,6 +79,7 @@ import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkAccountDao; import com.cloud.network.dao.NetworkAccountVO; import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkDetailsDao; import com.cloud.network.dao.NetworkDomainDao; import com.cloud.network.dao.NetworkDomainVO; import com.cloud.network.dao.NetworkServiceMapDao; @@ -119,6 +120,7 @@ import com.cloud.user.AccountVO; import com.cloud.user.DomainManager; import com.cloud.user.User; import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; import com.cloud.utils.StringUtils; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.component.ManagerBase; @@ -166,6 +168,8 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi @Inject NetworkDao _networksDao = null; @Inject + NetworkDetailsDao networkDetailsDao; + @Inject NicDao _nicDao = null; @Inject PodVlanMapDao _podVlanMapDao; @@ -2637,4 +2641,46 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi String networkCidr = guestNetwork.getNetworkCidr(); return networkCidr == null ? guestNetwork.getCidr() : networkCidr; } + + @Override + public Pair getNetworkIp4Dns(final Network network, final DataCenter zone) { + if (org.apache.commons.lang3.StringUtils.isNotBlank(network.getDns1())) { + return new Pair<>(network.getDns1(), network.getDns2()); + } + return new Pair<>(zone.getDns1(), zone.getDns2()); + } + + @Override + public Pair getNetworkIp6Dns(final Network network, final DataCenter zone) { + if (org.apache.commons.lang3.StringUtils.isNotBlank(network.getIp6Dns1())) { + return new Pair<>(network.getIp6Dns1(), network.getIp6Dns2()); + } + return new Pair<>(zone.getIp6Dns1(), zone.getIp6Dns2()); + } + + @Override + public void verifyIp4DnsPair(String ip4Dns1, String ip4Dns2) { + if (org.apache.commons.lang3.StringUtils.isEmpty(ip4Dns1) && org.apache.commons.lang3.StringUtils.isNotEmpty(ip4Dns2)) { + throw new InvalidParameterValueException("Second IPv4 DNS can be specified only with the first IPv4 DNS"); + } + if (org.apache.commons.lang3.StringUtils.isNotEmpty(ip4Dns1) && !NetUtils.isValidIp4(ip4Dns1)) { + throw new InvalidParameterValueException("Invalid IPv4 for DNS1"); + } + if (org.apache.commons.lang3.StringUtils.isNotEmpty(ip4Dns2) && !NetUtils.isValidIp4(ip4Dns2)) { + throw new InvalidParameterValueException("Invalid IPv4 for DNS2"); + } + } + + @Override + public void verifyIp6DnsPair(String ip6Dns1, String ip6Dns2) { + if (org.apache.commons.lang3.StringUtils.isEmpty(ip6Dns1) && org.apache.commons.lang3.StringUtils.isNotEmpty(ip6Dns2)) { + throw new InvalidParameterValueException("Second IPv6 DNS can be specified only with the first IPv6 DNS"); + } + if (org.apache.commons.lang3.StringUtils.isNotEmpty(ip6Dns1) && !NetUtils.isValidIp6(ip6Dns1)) { + throw new InvalidParameterValueException("Invalid IPv6 for IPv6 DNS1"); + } + if (org.apache.commons.lang3.StringUtils.isNotEmpty(ip6Dns2) && !NetUtils.isValidIp6(ip6Dns2)) { + throw new InvalidParameterValueException("Invalid IPv6 for IPv6 DNS2"); + } + } } diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index 4ad5b4a6b3c..fefe6c98cc6 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -74,6 +74,7 @@ import org.apache.cloudstack.network.dao.NetworkPermissionDao; import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; @@ -571,6 +572,70 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C return result; } + private void checkNetworkDns(boolean isIpv6, NetworkOffering networkOffering, Long vpcId, + String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2) { + if (ObjectUtils.anyNotNull(ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2)) { + if (GuestType.L2.equals(networkOffering.getGuestType())) { + throw new InvalidParameterValueException(String.format("DNS can not be specified %s networks", GuestType.L2)); + } + if (vpcId != null) { + throw new InvalidParameterValueException("DNS can not be specified for a VPC tier"); + } + if (!areServicesSupportedByNetworkOffering(networkOffering.getId(), Service.Dns)) { + throw new InvalidParameterValueException("DNS can not be specified for networks with network offering that do not support DNS service"); + } + } + if (!isIpv6 && !StringUtils.isAllEmpty(ip6Dns1, ip6Dns2)) { + throw new InvalidParameterValueException("IPv6 DNS cannot be specified for IPv4 only network"); + } + _networkModel.verifyIp4DnsPair(ip4Dns1, ip4Dns2); + _networkModel.verifyIp6DnsPair(ip6Dns1, ip6Dns2); + } + + protected boolean checkAndUpdateNetworkDns(NetworkVO network, NetworkOffering networkOffering, String newIp4Dns1, + String newIp4Dns2, String newIp6Dns1, String newIp6Dns2) { + String ip4Dns1 = network.getDns1(); + String ip4Dns2 = network.getDns2(); + String ip6Dns1 = network.getIp6Dns1(); + String ip6Dns2 = network.getIp6Dns2(); + if (ObjectUtils.allNull(newIp4Dns1, newIp4Dns2, newIp6Dns1, newIp6Dns2)) { + if (ObjectUtils.anyNotNull(ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2) && + !areServicesSupportedByNetworkOffering(networkOffering.getId(), Service.Dns)) { + network.setDns1(null); + network.setDns2(null); + network.setIp6Dns1(null); + network.setIp6Dns2(null); + return true; + } + return false; + } + if (StringUtils.equals(ip4Dns1, StringUtils.trimToNull(newIp4Dns1)) && StringUtils.equals(ip4Dns2, StringUtils.trimToNull(newIp4Dns2)) && + StringUtils.equals(ip6Dns1, StringUtils.trimToNull(newIp6Dns1)) && StringUtils.equals(ip6Dns2, StringUtils.trimToNull(newIp6Dns2))) { + return false; + } + boolean isIpv6 = (GuestType.Shared.equals(network.getGuestType()) && + StringUtils.isNotEmpty(network.getIp6Cidr())) || + _networkOfferingDao.isIpv6Supported(networkOffering.getId()); + if (newIp4Dns1 != null) { + ip4Dns1 = StringUtils.trimToNull(newIp4Dns1); + } + if (newIp4Dns2 != null) { + ip4Dns2 = StringUtils.trimToNull(newIp4Dns2); + } + if (newIp6Dns1 != null) { + ip6Dns1 = StringUtils.trimToNull(newIp6Dns1); + } + if (newIp6Dns2 != null) { + ip6Dns2 = StringUtils.trimToNull(newIp6Dns2); + } + checkNetworkDns(isIpv6, networkOffering, network.getVpcId(), ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); + network.setDns1(ip4Dns1); + network.setDns2(ip4Dns2); + network.setIp6Dns1(ip6Dns1); + network.setIp6Dns2(ip6Dns2); + return true; + } + @Override public List getIsolatedNetworksOwnedByAccountInZone(long zoneId, Account owner) { @@ -1273,6 +1338,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C String externalId = cmd.getExternalId(); String isolatedPvlanType = cmd.getIsolatedPvlanType(); Long associatedNetworkId = cmd.getAssociatedNetworkId(); + String ip4Dns1 = cmd.getIp4Dns1(); + String ip4Dns2 = cmd.getIp4Dns2(); + String ip6Dns1 = cmd.getIp6Dns1(); + String ip6Dns2 = cmd.getIp6Dns2(); // Validate network offering NetworkOfferingVO ntwkOff = _networkOfferingDao.findById(networkOfferingId); @@ -1486,7 +1555,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C throw new InvalidParameterValueException("Can only support create IPv6 network with advance shared network!"); } - if(StringUtils.isAllBlank(zone.getIp6Dns1(), zone.getIp6Dns2())) { + if(StringUtils.isAllBlank(ip6Dns1, ip6Dns2, zone.getIp6Dns1(), zone.getIp6Dns2())) { throw new InvalidParameterValueException("Can only create IPv6 network if the zone has IPv6 DNS! Please configure the zone IPv6 DNS1 and/or IPv6 DNS2."); } @@ -1609,9 +1678,11 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C cidr, startIP, endIP); } + checkNetworkDns(ipv6, ntwkOff, vpcId, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); + Network network = commitNetwork(networkOfferingId, gateway, startIP, endIP, netmask, networkDomain, vlanId, bypassVlanOverlapCheck, name, displayText, caller, physicalNetworkId, zoneId, domainId, isDomainSpecific, subdomainAccess, vpcId, startIPv6, endIPv6, ip6Gateway, ip6Cidr, displayNetwork, aclId, secondaryVlanId, privateVlanType, ntwkOff, pNtwk, aclType, owner, cidr, createVlan, - externalId, routerIp, routerIpv6, associatedNetwork); + externalId, routerIp, routerIpv6, associatedNetwork, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); if (hideIpAddressUsage) { _networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.hideIpAddressUsage, String.valueOf(hideIpAddressUsage), false)); @@ -1753,7 +1824,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C final boolean isDomainSpecific, final Boolean subdomainAccessFinal, final Long vpcId, final String startIPv6, final String endIPv6, final String ip6Gateway, final String ip6Cidr, final Boolean displayNetwork, final Long aclId, final String isolatedPvlan, final PVlanType isolatedPvlanType, final NetworkOfferingVO 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) throws InsufficientCapacityException, ResourceAllocationException { + final Network associatedNetwork, final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2) throws InsufficientCapacityException, ResourceAllocationException { try { Network network = Transaction.execute(new TransactionCallbackWithException() { @Override @@ -1812,7 +1883,7 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } } network = _vpcMgr.createVpcGuestNetwork(networkOfferingId, name, displayText, gateway, cidr, vlanId, networkDomain, owner, sharedDomainId, pNtwk, zoneId, aclType, - subdomainAccess, vpcId, aclId, caller, displayNetwork, externalId, ip6Gateway, ip6Cidr); + subdomainAccess, vpcId, aclId, caller, displayNetwork, externalId, ip6Gateway, ip6Cidr, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); } else { if (_configMgr.isOfferingForVpc(ntwkOff)) { throw new InvalidParameterValueException("Network offering can be used for VPC networks only"); @@ -1822,7 +1893,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); + zoneId, aclType, subdomainAccess, vpcId, ip6Gateway, ip6Cidr, displayNetwork, isolatedPvlan, isolatedPvlanType, externalId, routerIp, routerIpv6, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); } if (createVlan && network != null) { @@ -2587,6 +2658,10 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C String customId = cmd.getCustomId(); boolean updateInSequence = cmd.getUpdateInSequence(); boolean forced = cmd.getForced(); + String ip4Dns1 = cmd.getIp4Dns1(); + String ip4Dns2 = cmd.getIp4Dns2(); + String ip6Dns1 = cmd.getIp6Dns1(); + String ip6Dns2 = cmd.getIp6Dns2(); boolean restartNetwork = false; @@ -2715,6 +2790,11 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C } } + if (checkAndUpdateNetworkDns(network, networkOfferingChanged ? networkOffering : oldNtwkOff, ip4Dns1, ip4Dns2, + ip6Dns1, ip6Dns2)) { + restartNetwork = true; + } + final Map newSvcProviders = networkOfferingChanged ? _networkMgr.finalizeServicesAndProvidersForNetwork(_entityMgr.findById(NetworkOffering.class, networkOfferingId), network.getPhysicalNetworkId()) : new HashMap(); diff --git a/server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java index 5629eb2d1c1..7d7fb1564e8 100644 --- a/server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java @@ -20,9 +20,9 @@ import java.util.List; import javax.inject.Inject; -import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import com.cloud.dc.DataCenter; @@ -36,8 +36,6 @@ import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InsufficientVirtualNetworkCapacityException; import com.cloud.exception.InvalidParameterValueException; -import com.cloud.network.dao.PhysicalNetworkDao; -import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.IpAddressManager; import com.cloud.network.Network; import com.cloud.network.Network.GuestType; @@ -54,8 +52,12 @@ import com.cloud.network.PhysicalNetwork.IsolationMethod; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.user.Account; +import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; @@ -217,6 +219,19 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru { if (userSpecified.getRouterIpv6() != null) { config.setRouterIpv6(userSpecified.getRouterIpv6()); } + + if (StringUtils.isNotBlank(userSpecified.getDns1())) { + config.setDns1(userSpecified.getDns1()); + } + if (StringUtils.isNotBlank(userSpecified.getDns2())) { + config.setDns2(userSpecified.getDns2()); + } + if (StringUtils.isNotBlank(userSpecified.getIp6Dns1())) { + config.setIp6Dns1(userSpecified.getIp6Dns1()); + } + if (StringUtils.isNotBlank(userSpecified.getIp6Dns2())) { + config.setIp6Dns2(userSpecified.getIp6Dns2()); + } } boolean isSecurityGroupEnabled = _networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Service.SecurityGroup); @@ -240,11 +255,13 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru { @Override public void updateNicProfile(NicProfile profile, Network network) { DataCenter dc = _dcDao.findById(network.getDataCenterId()); + Pair ip4Dns = _networkModel.getNetworkIp4Dns(network, dc); + Pair ip6Dns = _networkModel.getNetworkIp6Dns(network, dc); if (profile != null) { - profile.setIPv4Dns1(dc.getDns1()); - profile.setIPv4Dns2(dc.getDns2()); - profile.setIPv6Dns1(dc.getIp6Dns1()); - profile.setIPv6Dns2(dc.getIp6Dns2()); + profile.setIPv4Dns1(ip4Dns.first()); + profile.setIPv4Dns2(ip4Dns.second()); + profile.setIPv6Dns1(ip6Dns.first()); + profile.setIPv6Dns2(ip6Dns.second()); } } @@ -413,7 +430,12 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru { @Override public void updateNetworkProfile(NetworkProfile networkProfile) { DataCenter dc = _dcDao.findById(networkProfile.getDataCenterId()); - networkProfile.setDns1(dc.getDns1()); - networkProfile.setDns2(dc.getDns2()); + Network network = _networkModel.getNetwork(networkProfile.getId()); + Pair dns = _networkModel.getNetworkIp4Dns(network, dc); + networkProfile.setDns1(dns.first()); + networkProfile.setDns2(dns.second()); + dns = _networkModel.getNetworkIp6Dns(network, dc); + networkProfile.setIp6Dns1(dns.first()); + networkProfile.setIp6Dns2(dns.second()); } } diff --git a/server/src/main/java/com/cloud/network/guru/DirectPodBasedNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/DirectPodBasedNetworkGuru.java index 01b33893b5c..f0ddc128b95 100644 --- a/server/src/main/java/com/cloud/network/guru/DirectPodBasedNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/DirectPodBasedNetworkGuru.java @@ -50,6 +50,7 @@ import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.utils.Pair; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; import com.cloud.utils.db.TransactionCallbackNoReturn; @@ -157,8 +158,9 @@ public class DirectPodBasedNetworkGuru extends DirectNetworkGuru { } DataCenter dc = _dcDao.findById(network.getDataCenterId()); - nic.setIPv4Dns1(dc.getDns1()); - nic.setIPv4Dns2(dc.getDns2()); + Pair dns = _networkModel.getNetworkIp4Dns(network, dc); + nic.setIPv4Dns1(dns.first()); + nic.setIPv4Dns2(dns.second()); } @DB @@ -240,8 +242,9 @@ public class DirectPodBasedNetworkGuru extends DirectNetworkGuru { } }); - nic.setIPv4Dns1(dc.getDns1()); - nic.setIPv4Dns2(dc.getDns2()); + Pair dns = _networkModel.getNetworkIp4Dns(network, dc); + nic.setIPv4Dns1(dns.first()); + nic.setIPv4Dns2(dns.second()); } } diff --git a/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java index 6524abe1e45..2f7051154c8 100644 --- a/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java @@ -59,6 +59,7 @@ import com.cloud.network.rules.PortForwardingRuleVO; import com.cloud.network.rules.dao.PortForwardingRulesDao; import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; +import com.cloud.utils.Pair; import com.cloud.utils.db.DB; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.Ip; @@ -321,8 +322,9 @@ public class ExternalGuestNetworkGuru extends GuestNetworkGuru { if (_networkModel.networkIsConfiguredForExternalNetworking(config.getDataCenterId(), config.getId())) { nic.setBroadcastUri(config.getBroadcastUri()); nic.setIsolationUri(config.getBroadcastUri()); - nic.setIPv4Dns1(dc.getDns1()); - nic.setIPv4Dns2(dc.getDns2()); + Pair dns = _networkModel.getNetworkIp4Dns(config, dc); + nic.setIPv4Dns1(dns.first()); + nic.setIPv4Dns2(dns.second()); nic.setIPv4Netmask(NetUtils.cidr2Netmask(config.getCidr())); long cidrAddress = NetUtils.ip2Long(config.getCidr().split("/")[0]); int cidrSize = getGloballyConfiguredCidrSize(); diff --git a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java index be3d57eb48d..47d90aa8816 100644 --- a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java @@ -28,6 +28,7 @@ import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationSe import org.apache.cloudstack.framework.config.ConfigKey; import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.commons.lang3.StringUtils; import org.apache.log4j.Logger; import com.cloud.configuration.Config; @@ -258,6 +259,19 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur network.setPvlanType(userSpecified.getPvlanType()); } } + + if (StringUtils.isNotBlank(userSpecified.getDns1())) { + network.setDns1(userSpecified.getDns1()); + } + if (StringUtils.isNotBlank(userSpecified.getDns2())) { + network.setDns2(userSpecified.getDns2()); + } + if (StringUtils.isNotBlank(userSpecified.getIp6Dns1())) { + network.setIp6Dns1(userSpecified.getIp6Dns1()); + } + if (StringUtils.isNotBlank(userSpecified.getIp6Dns2())) { + network.setIp6Dns2(userSpecified.getIp6Dns2()); + } } else { final String guestNetworkCidr = dc.getGuestNetworkCidr(); if (guestNetworkCidr == null && dc.getNetworkType() == NetworkType.Advanced) { @@ -437,8 +451,9 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur nic.setIPv4Netmask(NetUtils.cidr2Netmask(_networkModel.getValidNetworkCidr(network))); } - nic.setIPv4Dns1(dc.getDns1()); - nic.setIPv4Dns2(dc.getDns2()); + Pair dns = _networkModel.getNetworkIp4Dns(network, dc); + nic.setIPv4Dns1(dns.first()); + nic.setIPv4Dns2(dns.second()); nic.setFormat(AddressFormat.Ip4); } } @@ -458,9 +473,13 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur @Override public void updateNicProfile(final NicProfile profile, final Network network) { final DataCenter dc = _dcDao.findById(network.getDataCenterId()); + Pair dns = _networkModel.getNetworkIp4Dns(network, dc); + Pair ip6Dns = _networkModel.getNetworkIp6Dns(network, dc); if (profile != null) { - profile.setIPv4Dns1(dc.getDns1()); - profile.setIPv4Dns2(dc.getDns2()); + profile.setIPv4Dns1(dns.first()); + profile.setIPv4Dns2(dns.second()); + profile.setIPv6Dns1(ip6Dns.first()); + profile.setIPv6Dns2(ip6Dns.second()); } } @@ -506,8 +525,13 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur @Override public void updateNetworkProfile(final NetworkProfile networkProfile) { final DataCenter dc = _dcDao.findById(networkProfile.getDataCenterId()); - networkProfile.setDns1(dc.getDns1()); - networkProfile.setDns2(dc.getDns2()); + Network network = _networkModel.getNetwork(networkProfile.getId()); + Pair dns = _networkModel.getNetworkIp4Dns(network, dc); + networkProfile.setDns1(dns.first()); + networkProfile.setDns2(dns.second()); + dns = _networkModel.getNetworkIp6Dns(network, dc); + networkProfile.setIp6Dns1(dns.first()); + networkProfile.setIp6Dns2(dns.second()); } @Override diff --git a/server/src/main/java/com/cloud/network/guru/PrivateNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/PrivateNetworkGuru.java index 6e2f1db5f27..a5eac9aac1b 100644 --- a/server/src/main/java/com/cloud/network/guru/PrivateNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/PrivateNetworkGuru.java @@ -44,6 +44,7 @@ import com.cloud.network.vpc.PrivateIpVO; import com.cloud.network.vpc.dao.PrivateIpDao; import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; +import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.db.EntityManager; import com.cloud.utils.exception.CloudRuntimeException; @@ -60,7 +61,7 @@ public class PrivateNetworkGuru extends AdapterBase implements NetworkGuru { @Inject protected PrivateIpDao _privateIpDao; @Inject - protected NetworkModel _networkMgr; + protected NetworkModel networkModel; @Inject EntityManager _entityMgr; @@ -199,16 +200,22 @@ public class PrivateNetworkGuru extends AdapterBase implements NetworkGuru { nic.setMacAddress(ip.getMacAddress()); } - nic.setIPv4Dns1(dc.getDns1()); - nic.setIPv4Dns2(dc.getDns2()); + + Pair dns = networkModel.getNetworkIp4Dns(network, dc); + nic.setIPv4Dns1(dns.first()); + nic.setIPv4Dns2(dns.second()); } @Override public void updateNicProfile(NicProfile profile, Network network) { DataCenter dc = _entityMgr.findById(DataCenter.class, network.getDataCenterId()); + Pair dns = networkModel.getNetworkIp4Dns(network, dc); + Pair ip6Dns = networkModel.getNetworkIp6Dns(network, dc); if (profile != null) { - profile.setIPv4Dns1(dc.getDns1()); - profile.setIPv4Dns2(dc.getDns2()); + profile.setIPv4Dns1(dns.first()); + profile.setIPv4Dns2(dns.second()); + profile.setIPv6Dns1(ip6Dns.first()); + profile.setIPv6Dns2(ip6Dns.second()); } } @@ -239,7 +246,12 @@ public class PrivateNetworkGuru extends AdapterBase implements NetworkGuru { @Override public void updateNetworkProfile(NetworkProfile networkProfile) { DataCenter dc = _entityMgr.findById(DataCenter.class, networkProfile.getDataCenterId()); - networkProfile.setDns1(dc.getDns1()); - networkProfile.setDns2(dc.getDns2()); + Network network = networkModel.getNetwork(networkProfile.getId()); + Pair dns = networkModel.getNetworkIp4Dns(network, dc); + networkProfile.setDns1(dns.first()); + networkProfile.setDns2(dns.second()); + dns = networkModel.getNetworkIp6Dns(network, dc); + networkProfile.setIp6Dns1(dns.first()); + networkProfile.setIp6Dns2(dns.second()); } } diff --git a/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java index c3f7ee3d941..e8374b39f53 100644 --- a/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java @@ -34,6 +34,7 @@ import com.cloud.network.IpAddressManager; import com.cloud.network.Ipv6Service; import com.cloud.network.Network; import com.cloud.network.Network.State; +import com.cloud.network.NetworkModel; import com.cloud.network.NetworkProfile; import com.cloud.network.Networks.AddressFormat; import com.cloud.network.Networks.BroadcastDomainType; @@ -46,6 +47,7 @@ import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkVO; import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; +import com.cloud.utils.Pair; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.Transaction; @@ -73,6 +75,8 @@ public class PublicNetworkGuru extends AdapterBase implements NetworkGuru { IpAddressManager _ipAddrMgr; @Inject Ipv6Service ipv6Service; + @Inject + NetworkModel networkModel; private static final TrafficType[] TrafficTypes = {TrafficType.Public}; @@ -140,8 +144,9 @@ public class PublicNetworkGuru extends AdapterBase implements NetworkGuru { nic.setMacAddress(ip.getMacAddress()); } - nic.setIPv4Dns1(dc.getDns1()); - nic.setIPv4Dns2(dc.getDns2()); + Pair dns = networkModel.getNetworkIp4Dns(network, dc); + nic.setIPv4Dns1(dns.first()); + nic.setIPv4Dns2(dns.second()); ipv6Service.updateNicIpv6(nic, dc, network); } @@ -149,9 +154,13 @@ public class PublicNetworkGuru extends AdapterBase implements NetworkGuru { @Override public void updateNicProfile(NicProfile profile, Network network) { DataCenter dc = _dcDao.findById(network.getDataCenterId()); + Pair dns = networkModel.getNetworkIp4Dns(network, dc); + Pair ip6Dns = networkModel.getNetworkIp6Dns(network, dc); if (profile != null) { - profile.setIPv4Dns1(dc.getDns1()); - profile.setIPv4Dns2(dc.getDns2()); + profile.setIPv4Dns1(dns.first()); + profile.setIPv4Dns2(dns.second()); + profile.setIPv6Dns1(ip6Dns.first()); + profile.setIPv6Dns2(ip6Dns.second()); } } @@ -237,8 +246,13 @@ public class PublicNetworkGuru extends AdapterBase implements NetworkGuru { @Override public void updateNetworkProfile(NetworkProfile networkProfile) { DataCenter dc = _dcDao.findById(networkProfile.getDataCenterId()); - networkProfile.setDns1(dc.getDns1()); - networkProfile.setDns2(dc.getDns2()); + Network network = networkModel.getNetwork(networkProfile.getId()); + Pair dns = networkModel.getNetworkIp4Dns(network, dc); + networkProfile.setDns1(dns.first()); + networkProfile.setDns2(dns.second()); + dns = networkModel.getNetworkIp6Dns(network, dc); + networkProfile.setIp6Dns1(dns.first()); + networkProfile.setIp6Dns2(dns.second()); } } diff --git a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java index 6839a6ae135..517b65b83e1 100644 --- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java +++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java @@ -1116,18 +1116,20 @@ public class CommandSetupHelper { if (setupDns) { final DataCenterVO dcVo = _dcDao.findById(router.getDataCenterId()); - if (guestNic.getIPv4Dns1() != null) { - defaultDns1 = guestNic.getIPv4Dns1(); - } else { - defaultDns1 = dcVo.getDns1(); + defaultDns1 = guestNic.getIPv4Dns1(); + defaultDns2 = guestNic.getIPv4Dns2(); + if (org.apache.commons.lang3.StringUtils.isAllBlank(guestNic.getIPv4Dns1(), guestNic.getIPv4Dns2())) { + Pair dns = _networkModel.getNetworkIp4Dns(network, dcVo); + defaultDns1 = dns.first(); + defaultDns2 = dns.second(); } - if (guestNic.getIPv4Dns2() != null) { - defaultDns2 = guestNic.getIPv4Dns2(); - } else { - defaultDns2 = dcVo.getDns2(); + defaultIp6Dns1 = guestNic.getIPv6Dns1(); + defaultIp6Dns2 = guestNic.getIPv6Dns2(); + if (org.apache.commons.lang3.StringUtils.isAllBlank(guestNic.getIPv6Dns1(), guestNic.getIPv6Dns2())) { + Pair dns = _networkModel.getNetworkIp6Dns(network, dcVo); + defaultIp6Dns1 = dns.first(); + defaultIp6Dns2 = dns.second(); } - defaultIp6Dns1 = dcVo.getIp6Dns1(); - defaultIp6Dns2 = dcVo.getIp6Dns2(); } final Nic nic = _nicDao.findByNtwkIdAndInstanceId(network.getId(), router.getId()); 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 5ce43629087..c8daff60ba8 100644 --- a/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java +++ b/server/src/main/java/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java @@ -1984,8 +1984,8 @@ Configurable, StateListener params) throws ConfigurationException { @@ -788,7 +801,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } protected boolean areServicesSupportedByVpcOffering(final long vpcOffId, final Service... services) { - return _vpcOffSvcMapDao.areServicesSupportedByNetworkOffering(vpcOffId, services); + return _vpcOffSvcMapDao.areServicesSupportedByVpcOffering(vpcOffId, services); } @Override @@ -969,7 +982,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override @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 Boolean displayVpc) throws ResourceAllocationException { + final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, final Boolean displayVpc) throws ResourceAllocationException { final Account caller = CallContext.current().getCallingAccount(); final Account owner = _accountMgr.getAccount(vpcOwnerId); @@ -979,9 +992,15 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis // check resource limit _resourceLimitMgr.checkResourceLimit(owner, ResourceType.vpc); + // Validate zone + final DataCenter zone = _dcDao.findById(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Can't find zone by id specified"); + } + // Validate vpc offering final VpcOfferingVO vpcOff = _vpcOffDao.findById(vpcOffId); - _accountMgr.checkAccess(owner, vpcOff, _dcDao.findById(zoneId)); + _accountMgr.checkAccess(owner, vpcOff, zone); if (vpcOff == null || vpcOff.getState() != State.Enabled) { final InvalidParameterValueException ex = new InvalidParameterValueException("Unable to find vpc offering in " + State.Enabled + " state by specified id"); if (vpcOff == null) { @@ -997,12 +1016,6 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis throw new InvalidParameterValueException("Network domain must be specified for region level VPC"); } - // Validate zone - final DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId); - if (zone == null) { - throw new InvalidParameterValueException("Can't find zone by id specified"); - } - if (Grouping.AllocationState.Disabled == zone.getAllocationState() && !_accountMgr.isRootAdmin(caller.getId())) { // See DataCenterVO.java final PermissionDeniedException ex = new PermissionDeniedException("Cannot perform this operation since specified Zone is currently disabled"); @@ -1021,13 +1034,23 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis } } + checkVpcDns(vpcOff, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); + final boolean useDistributedRouter = vpcOff.isSupportsDistributedRouter(); final VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, owner.getId(), owner.getDomainId(), vpcOffId, cidr, networkDomain, useDistributedRouter, isRegionLevelVpcOff, - vpcOff.isRedundantRouter()); + vpcOff.isRedundantRouter(), ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); return createVpc(displayVpc, vpc); } + @Override + @ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription = "creating vpc", create = true) + public Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException { + return 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()); + } + @DB protected Vpc createVpc(final Boolean displayVpc, final VpcVO vpc) { final String cidr = vpc.getCidr(); @@ -2687,7 +2710,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Override public Network createVpcGuestNetwork(final long ntwkOffId, final String name, final String displayText, final String gateway, final String cidr, final String vlanId, String networkDomain, final Account owner, final Long domainId, final PhysicalNetwork pNtwk, final long zoneId, final ACLType aclType, final Boolean subdomainAccess, - final long vpcId, final Long aclId, final Account caller, final Boolean isDisplayNetworkEnabled, String externalId, String ip6Gateway, String ip6Cidr) throws ConcurrentOperationException, InsufficientCapacityException, + final long vpcId, final Long aclId, final Account caller, final Boolean isDisplayNetworkEnabled, String externalId, String ip6Gateway, String ip6Cidr, final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2) throws ConcurrentOperationException, InsufficientCapacityException, ResourceAllocationException { final Vpc vpc = getActiveVpc(vpcId); @@ -2712,7 +2735,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis // 2) Create network final Network guestNetwork = _ntwkMgr.createGuestNetwork(ntwkOffId, name, displayText, gateway, cidr, vlanId, false, networkDomain, owner, domainId, pNtwk, zoneId, aclType, - subdomainAccess, vpcId, ip6Gateway, ip6Cidr, isDisplayNetworkEnabled, null, null, externalId, null, null); + subdomainAccess, vpcId, ip6Gateway, ip6Cidr, isDisplayNetworkEnabled, null, null, externalId, null, null, ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); if (guestNetwork != null) { guestNetwork.setNetworkACLId(aclId); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index 11741a4e494..608448c8e47 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -3752,7 +3752,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir s_logger.debug("Creating network for account " + owner + " from the network offering id=" + requiredOfferings.get(0).getId() + " as a part of deployVM process"); Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), owner.getAccountName() + "-network", owner.getAccountName() + "-network", null, null, null, false, null, owner, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, null, null, true, null, null, - null, null, null); + null, null, null, null, null, null, null); if (newNetwork != null) { defaultNetwork = _networkDao.findById(newNetwork.getId()); } @@ -7277,7 +7277,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir Network newNetwork = _networkMgr.createGuestNetwork(requiredOfferings.get(0).getId(), newAccount.getAccountName() + "-network", newAccount.getAccountName() + "-network", null, null, null, false, null, newAccount, null, physicalNetwork, zone.getId(), ACLType.Account, null, null, - null, null, true, null, null, null, null, null); + null, null, true, null, null, null, null, null, null, null, null, null); // if the network offering has persistent set to true, implement the network if (requiredOfferings.get(0).isPersistent()) { DeployDestination dest = new DeployDestination(zone, null, null, null); diff --git a/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java b/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java index 7a0ea2e9721..93a1d30d178 100644 --- a/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java +++ b/server/src/test/java/com/cloud/network/CreatePrivateNetworkTest.java @@ -129,7 +129,7 @@ public class CreatePrivateNetworkTest { ACLType.Account, false, 1L, false); when(networkService._networkMgr.createGuestNetwork(eq(ntwkOff.getId()), eq("bla"), eq("fake"), eq("10.1.1.1"), eq("10.1.1.0/24"), nullable(String.class), nullable(Boolean.class), nullable(String.class), eq(account), nullable(Long.class), eq(physicalNetwork), eq(physicalNetwork.getDataCenterId()), eq(ACLType.Account), nullable(Boolean.class), eq(1L), nullable(String.class), nullable(String.class), - nullable(Boolean.class), nullable(String.class), nullable(Network.PVlanType.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenReturn(net); + nullable(Boolean.class), nullable(String.class), nullable(Network.PVlanType.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class), nullable(String.class))).thenReturn(net); when( networkService._networkMgr.createPrivateNetwork(eq(ntwkOff.getId()), eq("bla"), eq("fake"), eq("10.1.1.1"), eq("10.1.1.0/24"), anyString(), anyBoolean(), eq(account), eq(physicalNetwork), eq(1L))).thenReturn(net); diff --git a/server/src/test/java/com/cloud/network/Ipv6AddressManagerTest.java b/server/src/test/java/com/cloud/network/Ipv6AddressManagerTest.java index bd1484d588a..17676956a82 100644 --- a/server/src/test/java/com/cloud/network/Ipv6AddressManagerTest.java +++ b/server/src/test/java/com/cloud/network/Ipv6AddressManagerTest.java @@ -19,8 +19,6 @@ package com.cloud.network; import static org.mockito.Mockito.mock; -import com.cloud.dc.DataCenter; -import com.cloud.vm.NicProfile; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -28,6 +26,7 @@ import org.mockito.InjectMocks; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; +import com.cloud.dc.DataCenter; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.IpAddress.State; @@ -36,7 +35,9 @@ import com.cloud.network.dao.IPAddressDaoImpl; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.UserIpv6AddressDaoImpl; import com.cloud.user.Account; +import com.cloud.utils.Pair; import com.cloud.utils.net.NetUtils; +import com.cloud.vm.NicProfile; import com.cloud.vm.dao.NicSecondaryIpDaoImpl; import com.cloud.vm.dao.NicSecondaryIpVO; @@ -239,8 +240,7 @@ public class Ipv6AddressManagerTest { Mockito.when(network.getIp6Cidr()).thenReturn("2001:db8:100::/64"); Mockito.when(network.getIp6Gateway()).thenReturn("2001:db8:100::1"); - Mockito.when(dc.getIp6Dns1()).thenReturn("2001:db8::53:1"); - Mockito.when(dc.getIp6Dns1()).thenReturn("2001:db8::53:2"); + Mockito.when(networkModel.getNetworkIp6Dns(network, dc)).thenReturn(new Pair<>("2001:db8::53:1", "2001:db8::53:2")); String expected = "2001:db8:100:0:1c00:b1ff:fe00:af6"; diff --git a/server/src/test/java/com/cloud/network/Ipv6ServiceImplTest.java b/server/src/test/java/com/cloud/network/Ipv6ServiceImplTest.java index 1b0592abcb2..3b55717d209 100644 --- a/server/src/test/java/com/cloud/network/Ipv6ServiceImplTest.java +++ b/server/src/test/java/com/cloud/network/Ipv6ServiceImplTest.java @@ -120,13 +120,11 @@ public class Ipv6ServiceImplTest { DomainRouterDao domainRouterDao; @Mock AccountManager accountManager; - @Mock NetworkModel networkModel = Mockito.mock(NetworkModelImpl.class); @Mock IPAddressDao ipAddressDao; @Mock NetworkOrchestrationService networkOrchestrationService; - FirewallManager firewallManager = Mockito.mock(FirewallManager.class); @InjectMocks @@ -145,6 +143,7 @@ public class Ipv6ServiceImplTest { final String gateway = "fd17:5:8a43:e2a5::1"; final String macAddress = "1e:00:4c:00:00:03"; final String ipv6Address = "fd17:5:8a43:e2a5:1c00:4cff:fe00:3"; // Resulting IPv6 address using SLAAC + final Pair ipv6DnsPair = new Pair<>("2001:db8::53:1", "2001:db8::53:2"); public static final long ACCOUNT_ID = 1; private AccountVO account; @@ -485,6 +484,7 @@ public class Ipv6ServiceImplTest { @Test public void testIpv6NetworkUpdateNicIpv6() { Mockito.when(networkOfferingDao.isIpv6Supported(Mockito.anyLong())).thenReturn(true); + Mockito.when(networkModel.getNetworkIp6Dns(Mockito.any(Network.class), Mockito.any(DataCenter.class))).thenReturn(ipv6DnsPair); NicProfile nicProfile = new NicProfile(); nicProfile.setBroadcastUri(URI.create(vlan)); nicProfile.setMacAddress(macAddress); @@ -502,6 +502,7 @@ public class Ipv6ServiceImplTest { @Test public void testIpv6NetworkFromPlaceholderUpdateNicIpv6() { Mockito.when(networkOfferingDao.isIpv6Supported(Mockito.anyLong())).thenReturn(true); + Mockito.when(networkModel.getNetworkIp6Dns(Mockito.any(Network.class), Mockito.any(DataCenter.class))).thenReturn(ipv6DnsPair); NicProfile nicProfile = new NicProfile(); nicProfile.setBroadcastUri(URI.create(vlan)); nicProfile.setMacAddress(macAddress); diff --git a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java index e9c9db3cba2..6f437b8be56 100644 --- a/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/network/MockNetworkModelImpl.java @@ -24,6 +24,7 @@ import java.util.Set; import javax.naming.ConfigurationException; +import com.cloud.dc.DataCenter; import com.cloud.dc.Vlan; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InvalidParameterValueException; @@ -44,6 +45,7 @@ import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.user.Account; +import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; @@ -918,4 +920,19 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { return null; } + @Override + public Pair getNetworkIp4Dns(Network network, DataCenter zone) { + return new Pair<>(null, null); + } + + @Override + public Pair getNetworkIp6Dns(Network network, DataCenter zone) { + return new Pair<>(null, null); + } + + @Override + public void verifyIp4DnsPair(String ip4Dns1, String ip4Dns2) {} + + @Override + public void verifyIp6DnsPair(String ip4Dns1, String ip4Dns2) {} } diff --git a/server/src/test/java/com/cloud/network/NetworkModelImplTest.java b/server/src/test/java/com/cloud/network/NetworkModelImplTest.java new file mode 100644 index 00000000000..76d049b8868 --- /dev/null +++ b/server/src/test/java/com/cloud/network/NetworkModelImplTest.java @@ -0,0 +1,144 @@ +// 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.network; + +import org.junit.Assert; +import org.junit.Test; +import org.mockito.InjectMocks; +import org.mockito.Mockito; + +import com.cloud.dc.DataCenter; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.utils.Pair; + +public class NetworkModelImplTest { + final String[] ip4Dns1 = {"5.5.5.5", "6.6.6.6"}; + final String[] ip4Dns2 = {"7.7.7.7", "8.8.8.8"}; + final String[] ip6Dns1 = {"2001:4860:4860::5555", "2001:4860:4860::6666"}; + final String[] ip6Dns2 = {"2001:4860:4860::7777", "2001:4860:4860::8888"}; + + @InjectMocks + private NetworkModelImpl networkModel = new NetworkModelImpl(); + + private void prepareMocks(boolean isIp6, Network network, DataCenter zone, + String dns1, String dns2, String dns3, String dns4) { + if (isIp6) { + Mockito.when(network.getIp6Dns1()).thenReturn(dns1); + Mockito.when(zone.getIp6Dns1()).thenReturn(dns2); + Mockito.when(network.getIp6Dns2()).thenReturn(dns3); + Mockito.when(zone.getIp6Dns2()).thenReturn(dns4); + } else { + Mockito.when(network.getDns1()).thenReturn(dns1); + Mockito.when(zone.getDns1()).thenReturn(dns2); + Mockito.when(network.getDns2()).thenReturn(dns3); + Mockito.when(zone.getDns2()).thenReturn(dns4); + } + } + + private void testDnsCases(boolean isIp6) { + String[] dns1 = isIp6 ? ip6Dns1 : ip4Dns1; + String[] dns2 = isIp6 ? ip6Dns2 : ip4Dns2; + Network network = Mockito.mock(Network.class); + DataCenter zone = Mockito.mock(DataCenter.class); + // Both network and zone have valid dns + prepareMocks(isIp6, network, zone, dns1[0], dns1[1], dns2[0], dns1[1]); + Pair result = isIp6 ? networkModel.getNetworkIp6Dns(network, zone) : + networkModel.getNetworkIp4Dns(network, zone); + Assert.assertEquals(dns1[0], result.first()); + Assert.assertEquals(dns2[0], result.second()); + // Network has valid dns and zone don't + prepareMocks(isIp6, network, zone, dns1[0], null, dns2[0], null); + result = isIp6 ? networkModel.getNetworkIp6Dns(network, zone) : + networkModel.getNetworkIp4Dns(network, zone); + Assert.assertEquals(dns1[0], result.first()); + Assert.assertEquals(dns2[0], result.second()); + // Zone has a valid dns and network don't + prepareMocks(isIp6, network, zone, null, dns1[1], null, dns2[1]); + result = isIp6 ? networkModel.getNetworkIp6Dns(network, zone) : + networkModel.getNetworkIp4Dns(network, zone); + Assert.assertEquals(dns1[1], result.first()); + Assert.assertEquals(dns2[1], result.second()); + // Zone has a valid dns and network has only first dns + prepareMocks(isIp6, network, zone, dns1[0], dns1[1], null, dns2[1]); + result = isIp6 ? networkModel.getNetworkIp6Dns(network, zone) : + networkModel.getNetworkIp4Dns(network, zone); + Assert.assertEquals(dns1[0], result.first()); + Assert.assertNull(result.second()); + // Both network and zone only have the first dns + prepareMocks(isIp6, network, zone, dns1[0], dns1[1], null, null); + result = isIp6 ? networkModel.getNetworkIp6Dns(network, zone) : + networkModel.getNetworkIp4Dns(network, zone); + Assert.assertEquals(dns1[0], result.first()); + Assert.assertNull(result.second()); + // Both network and zone dns are null + prepareMocks(isIp6, network, zone, null, null, null, null); + result = isIp6 ? networkModel.getNetworkIp6Dns(network, zone) : + networkModel.getNetworkIp4Dns(network, zone); + Assert.assertNull(result.first()); + Assert.assertNull(result.second()); + } + + @Test + public void testGetNetworkIp4Dns() { + testDnsCases(false); + } + + @Test + public void testGetNetworkIp6Dns() { + testDnsCases(true); + } + + @Test(expected = InvalidParameterValueException.class) + public void testVerifyIp4DnsPairDns1NullFailure() { + networkModel.verifyIp4DnsPair(null, ip4Dns1[1]); + } + + @Test(expected = InvalidParameterValueException.class) + public void testVerifyIp4DnsPairInvalidDns1Failure() { + networkModel.verifyIp4DnsPair("invalid", ip4Dns1[1]); + } + + @Test(expected = InvalidParameterValueException.class) + public void testVerifyIp4DnsPairInvalidDns2Failure() { + networkModel.verifyIp4DnsPair(ip4Dns1[0], "invalid"); + } + + @Test + public void testVerifyIp4DnsPairValid() { + networkModel.verifyIp4DnsPair(ip4Dns1[0], ip4Dns1[1]); + } + + @Test(expected = InvalidParameterValueException.class) + public void testVerifyIp6DnsPairDns1NullFailure() { + networkModel.verifyIp6DnsPair(null, ip6Dns1[1]); + } + + @Test(expected = InvalidParameterValueException.class) + public void testVerifyIp6DnsPairInvalidDns1Failure() { + networkModel.verifyIp6DnsPair("invalid", ip6Dns1[1]); + } + + @Test(expected = InvalidParameterValueException.class) + public void testVerifyIp6DnsPairInvalidDns2Failure() { + networkModel.verifyIp6DnsPair(ip6Dns1[0], "invalid"); + } + + @Test + public void testVerifyIp6DnsPairValid() { + networkModel.verifyIp6DnsPair(ip6Dns1[0], ip6Dns1[1]); + } +} \ No newline at end of file diff --git a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java index 2520f3902ba..30def99f169 100644 --- a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java +++ b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java @@ -16,18 +16,89 @@ // under the License. package com.cloud.network; -import com.cloud.utils.Pair; -import com.cloud.utils.exception.CloudRuntimeException; +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.UUID; + +import org.apache.cloudstack.api.command.user.network.CreateNetworkCmd; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; 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.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +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.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.Pair; +import com.cloud.utils.component.ComponentContext; +import com.cloud.utils.exception.CloudRuntimeException; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(ComponentContext.class) public class NetworkServiceImplTest { + @Mock + AccountManager accountManager; + @Mock + NetworkOfferingDao networkOfferingDao; + @Mock + PhysicalNetworkDao physicalNetworkDao; + @Mock + DataCenterDao dataCenterDao; + @Mock + NetworkOrchestrationService networkOrchestrationService; + @Mock + Ipv6Service ipv6Service; + @Mock + NetworkModel networkModel; + @Mock + NetworkOfferingServiceMapDao networkOfferingServiceMapDao; + @InjectMocks private NetworkServiceImpl service = new NetworkServiceImpl(); private static final String VLAN_ID_900 = "900"; private static final String VLAN_ID_901 = "901"; private static final String VLAN_ID_902 = "902"; + public static final long ACCOUNT_ID = 1; + + private static final String IP4_GATEWAY = "10.0.16.1"; + private static final String IP4_NETMASK = "255.255.255.0"; + private static final String IP6_GATEWAY = "fd17:ac56:1234:2000::1"; + private static final String IP6_CIDR = "fd17:ac56:1234:2000::/64"; + final String[] ip4Dns = {"5.5.5.5", "6.6.6.6"}; + final String[] ip6Dns = {"2001:4860:4860::5555", "2001:4860:4860::6666"}; + + private AccountVO account; + private UserVO user; + + private void registerCallContext() { + account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); + account.setId(ACCOUNT_ID); + user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", + UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.register(user, account); + } @Test public void testGetPrivateVlanPairNoVlans() { @@ -117,4 +188,229 @@ public class NetworkServiceImplTest { service.performBasicPrivateVlanChecks(VLAN_ID_900, VLAN_ID_901, Network.PVlanType.Promiscuous); } + private void prepareCreateNetworkDnsMocks(CreateNetworkCmd cmd, Network.GuestType guestType, boolean ipv6, boolean isVpc, boolean dnsServiceSupported) { + long networkOfferingId = 1L; + Mockito.when(cmd.getNetworkOfferingId()).thenReturn(networkOfferingId); + NetworkOfferingVO networkOfferingVO = Mockito.mock(NetworkOfferingVO.class); + Mockito.when(networkOfferingVO.getId()).thenReturn(networkOfferingId); + Mockito.when(networkOfferingVO.getGuestType()).thenReturn(guestType); + Mockito.when(networkOfferingVO.getTrafficType()).thenReturn(Networks.TrafficType.Guest); + Mockito.when(networkOfferingVO.isSpecifyIpRanges()).thenReturn(true); + Mockito.when(networkOfferingDao.findById(networkOfferingId)).thenReturn(networkOfferingVO); + Mockito.when(accountManager.finalizeOwner(Mockito.any(), Mockito.any(), Mockito.anyLong(), Mockito.anyLong())).thenReturn(account); + if (Network.GuestType.Shared.equals(guestType)) { + Mockito.when(networkModel.isProviderForNetworkOffering(Mockito.any(), Mockito.anyLong())).thenReturn(true); + Mockito.when(cmd.getGateway()).thenReturn(IP4_GATEWAY); + Mockito.when(cmd.getNetmask()).thenReturn(IP4_NETMASK); + } + Mockito.when(accountManager.isNormalUser(Mockito.anyLong())).thenReturn(true); + Mockito.when(physicalNetworkDao.findById(Mockito.anyLong())).thenReturn(Mockito.mock(PhysicalNetworkVO.class)); + DataCenterVO zone = Mockito.mock(DataCenterVO.class); + Mockito.when(zone.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced); + Mockito.when(dataCenterDao.findById(Mockito.anyLong())).thenReturn(zone); + Mockito.when(networkOrchestrationService.finalizeServicesAndProvidersForNetwork(Mockito.any(), Mockito.anyLong())).thenReturn(new HashMap<>()); + Mockito.when(networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(networkOfferingId, Network.Service.Dns)).thenReturn(dnsServiceSupported); + if(ipv6 && Network.GuestType.Isolated.equals(guestType)) { + Mockito.when(networkOfferingDao.isIpv6Supported(networkOfferingId)).thenReturn(true); + try { + Mockito.when(ipv6Service.preAllocateIpv6SubnetForNetwork(Mockito.anyLong())).thenReturn(new Pair<>(IP6_GATEWAY, IP6_CIDR)); + } catch (ResourceAllocationException e) { + Assert.fail(String.format("failure with exception: %s", e.getMessage())); + } + } + Mockito.when(cmd.getSubdomainAccess()).thenReturn(null); + Mockito.when(cmd.getAssociatedNetworkId()).thenReturn(null); + if (isVpc) { + Mockito.when(cmd.getVpcId()).thenReturn(1L); + } else { + Mockito.when(cmd.getVpcId()).thenReturn(null); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testCreateL2NetworkDnsFailure() { + registerCallContext(); + CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class); + prepareCreateNetworkDnsMocks(cmd, Network.GuestType.L2, false, false, true); + Mockito.when(cmd.getIp4Dns1()).thenReturn(ip4Dns[0]); + try { + service.createGuestNetwork(cmd); + } catch (InsufficientCapacityException | ResourceAllocationException e) { + Assert.fail(String.format("failure with exception: %s", e.getMessage())); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testCreateNetworkDnsVpcFailure() { + registerCallContext(); + CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class); + prepareCreateNetworkDnsMocks(cmd, Network.GuestType.Isolated, false, true, true); + Mockito.when(cmd.getIp4Dns1()).thenReturn(ip4Dns[0]); + try { + service.createGuestNetwork(cmd); + } catch (InsufficientCapacityException | ResourceAllocationException e) { + Assert.fail(String.format("failure with exception: %s", e.getMessage())); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testCreateNetworkDnsOfferingServiceFailure() { + registerCallContext(); + CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class); + prepareCreateNetworkDnsMocks(cmd, Network.GuestType.Isolated, false, false, false); + Mockito.when(cmd.getIp4Dns1()).thenReturn(ip4Dns[0]); + try { + service.createGuestNetwork(cmd); + } catch (InsufficientCapacityException | ResourceAllocationException e) { + Assert.fail(String.format("failure with exception: %s", e.getMessage())); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testCreateIp4NetworkIp6DnsFailure() { + registerCallContext(); + CreateNetworkCmd cmd = Mockito.mock(CreateNetworkCmd.class); + prepareCreateNetworkDnsMocks(cmd, Network.GuestType.Isolated, false, false, true); + Mockito.when(cmd.getIp6Dns1()).thenReturn(ip4Dns[0]); + try { + service.createGuestNetwork(cmd); + } catch (InsufficientCapacityException | ResourceAllocationException e) { + Assert.fail(String.format("failure with exception: %s", e.getMessage())); + } + } + + @Test + public void testCheckAndUpdateNetworkNoUpdate() { + Assert.assertFalse(service.checkAndUpdateNetworkDns(Mockito.mock(NetworkVO.class), Mockito.mock(NetworkOffering.class), null, null, null, null)); + NetworkVO network1 = Mockito.mock(NetworkVO.class); + Mockito.when(network1.getDns1()).thenReturn(ip4Dns[0]); + Long offeringId = 1L; + NetworkOffering offering = Mockito.mock(NetworkOffering.class); + Mockito.when(offering.getId()).thenReturn(offeringId); + Mockito.when(networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(offeringId, Network.Service.Dns)).thenReturn(true); + Assert.assertFalse(service.checkAndUpdateNetworkDns(network1, offering, null, null, null, null)); + Assert.assertFalse(service.checkAndUpdateNetworkDns(network1, Mockito.mock(NetworkOffering.class), ip4Dns[0], null, null, null)); + Mockito.when(network1.getIp6Dns1()).thenReturn(ip6Dns[0]); + Assert.assertFalse(service.checkAndUpdateNetworkDns(network1, Mockito.mock(NetworkOffering.class), ip4Dns[0], null, ip6Dns[0], null)); + } + + @Test + public void testCheckAndUpdateNetworkOfferingChangeReset() { + NetworkVO networkVO = new NetworkVO(); + networkVO.setDns1(ip4Dns[0]); + Long offeringId = 1L; + NetworkOffering offering = Mockito.mock(NetworkOffering.class); + Mockito.when(offering.getId()).thenReturn(offeringId); + Mockito.when(networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(offeringId, Network.Service.Dns)).thenReturn(false); + Assert.assertTrue(service.checkAndUpdateNetworkDns(networkVO, offering, null, null, null, null)); + Assert.assertNull(networkVO.getDns1()); + Assert.assertNull(networkVO.getDns2()); + Assert.assertNull(networkVO.getIp6Dns1()); + Assert.assertNull(networkVO.getIp6Dns2()); + } + + @Test(expected = InvalidParameterValueException.class) + public void testCheckAndUpdateNetworkDnsL2NetworkFailure() { + NetworkVO networkVO = Mockito.mock(NetworkVO.class); + NetworkOffering offering = Mockito.mock(NetworkOffering.class); + Mockito.when(offering.getGuestType()).thenReturn(Network.GuestType.L2); + service.checkAndUpdateNetworkDns(networkVO, offering, ip4Dns[0], null, null, null); + } + + @Test(expected = InvalidParameterValueException.class) + public void testCheckAndUpdateNetworkDnsVpcTierFailure() { + NetworkVO networkVO = Mockito.mock(NetworkVO.class); + Mockito.when(networkVO.getVpcId()).thenReturn(1L); + NetworkOffering offering = Mockito.mock(NetworkOffering.class); + Mockito.when(offering.getGuestType()).thenReturn(Network.GuestType.Shared); + service.checkAndUpdateNetworkDns(networkVO, offering, ip4Dns[0], null, null, null); + } + + @Test(expected = InvalidParameterValueException.class) + public void testCheckAndUpdateNetworkDnsServiceFailure() { + NetworkVO networkVO = Mockito.mock(NetworkVO.class); + Mockito.when(networkVO.getVpcId()).thenReturn(null); + Long offeringId = 1L; + NetworkOffering offering = Mockito.mock(NetworkOffering.class); + Mockito.when(offering.getId()).thenReturn(offeringId); + Mockito.when(offering.getGuestType()).thenReturn(Network.GuestType.Shared); + Mockito.when(networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(offeringId, Network.Service.Dns)).thenReturn(false); + service.checkAndUpdateNetworkDns(networkVO, offering, ip4Dns[0], null, null, null); + } + + @Test(expected = InvalidParameterValueException.class) + public void testCheckAndUpdateNetworkNotSharedIp6Failure() { + NetworkVO networkVO = Mockito.mock(NetworkVO.class); + Mockito.when(networkVO.getVpcId()).thenReturn(null); + Mockito.when(networkVO.getIp6Cidr()).thenReturn(null); + Mockito.when(networkVO.getGuestType()).thenReturn(Network.GuestType.Shared); + Long offeringId = 1L; + NetworkOffering offering = Mockito.mock(NetworkOffering.class); + Mockito.when(offering.getId()).thenReturn(offeringId); + Mockito.when(networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(offeringId, Network.Service.Dns)).thenReturn(true); + service.checkAndUpdateNetworkDns(networkVO, offering, null, null, ip6Dns[0], null); + } + + @Test(expected = InvalidParameterValueException.class) + public void testCheckAndUpdateNetworkNotIsolatedIp6Failure() { + NetworkVO networkVO = Mockito.mock(NetworkVO.class); + Mockito.when(networkVO.getVpcId()).thenReturn(null); + Mockito.when(networkVO.getGuestType()).thenReturn(Network.GuestType.Isolated); + Long offeringId = 1L; + NetworkOffering offering = Mockito.mock(NetworkOffering.class); + Mockito.when(offering.getId()).thenReturn(offeringId); + Mockito.when(networkOfferingDao.isIpv6Supported(offeringId)).thenReturn(false); + Mockito.when(networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(offeringId, Network.Service.Dns)).thenReturn(true); + service.checkAndUpdateNetworkDns(networkVO, offering, null, null, ip6Dns[0], null); + } + + @Test + public void testCheckAndUpdateNetworkSuccess() { + NetworkVO networkVO = new NetworkVO(); + networkVO.setVpcId(null); + try { + Field id = networkVO.getClass().getDeclaredField("guestType"); + id.setAccessible(true); + id.set(networkVO, Network.GuestType.Shared); + } catch (NoSuchFieldException | IllegalAccessException e) { + Assert.fail(String.format("Unable to set network guestType, %s", e.getMessage())); + } + networkVO.setIp6Cidr("cidr"); + Long offeringId = 1L; + NetworkOffering offering = Mockito.mock(NetworkOffering.class); + Mockito.when(offering.getId()).thenReturn(offeringId); + Mockito.when(networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(offeringId, Network.Service.Dns)).thenReturn(true); + boolean updated = service.checkAndUpdateNetworkDns(networkVO, offering, ip4Dns[0], null, ip6Dns[0], null); + Assert.assertTrue(updated); + Assert.assertEquals(ip4Dns[0], networkVO.getDns1()); + Assert.assertNull(networkVO.getDns2()); + Assert.assertEquals(ip6Dns[0], networkVO.getIp6Dns1()); + Assert.assertNull(networkVO.getIp6Dns2()); + } + + @Test + public void testCheckAndUpdateNetworkResetSuccess() { + NetworkVO networkVO = new NetworkVO(); + networkVO.setVpcId(null); + networkVO.setDns1(ip4Dns[0]); + networkVO.setIp6Dns1(ip6Dns[0]); + try { + Field id = networkVO.getClass().getDeclaredField("guestType"); + id.setAccessible(true); + id.set(networkVO, Network.GuestType.Shared); + } catch (NoSuchFieldException | IllegalAccessException e) { + Assert.fail(String.format("Unable to set network guestType, %s", e.getMessage())); + } + networkVO.setIp6Cidr("cidr"); + Long offeringId = 1L; + NetworkOffering offering = Mockito.mock(NetworkOffering.class); + Mockito.when(offering.getId()).thenReturn(offeringId); + Mockito.when(networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(offeringId, Network.Service.Dns)).thenReturn(true); + boolean updated = service.checkAndUpdateNetworkDns(networkVO, offering, "", null, "", null); + Assert.assertTrue(updated); + Assert.assertNull(networkVO.getDns1()); + Assert.assertNull(networkVO.getDns2()); + Assert.assertNull(networkVO.getIp6Dns1()); + Assert.assertNull(networkVO.getIp6Dns2()); + } } diff --git a/server/src/test/java/com/cloud/network/guru/DirectNetworkGuruTest.java b/server/src/test/java/com/cloud/network/guru/DirectNetworkGuruTest.java index 750bee49c8a..ce5aabb6c13 100644 --- a/server/src/test/java/com/cloud/network/guru/DirectNetworkGuruTest.java +++ b/server/src/test/java/com/cloud/network/guru/DirectNetworkGuruTest.java @@ -16,31 +16,37 @@ // under the License. package com.cloud.network.guru; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +import java.util.Arrays; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + import com.cloud.dc.DataCenter.NetworkType; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.deploy.DeploymentPlan; import com.cloud.network.Network; -import com.cloud.network.NetworkModel; -import com.cloud.network.Networks.TrafficType; import com.cloud.network.Network.GuestType; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkProfile; +import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetwork.IsolationMethod; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.offering.NetworkOffering; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.user.Account; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.Arrays; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; +import com.cloud.utils.Pair; +import com.cloud.vm.NicProfile; public class DirectNetworkGuruTest { @@ -76,6 +82,9 @@ public class DirectNetworkGuruTest { @Mock Account owner; + final String[] ip4Dns = {"5.5.5.5", "6.6.6.6"}; + final String[] ip6Dns = {"2001:4860:4860::5555", "2001:4860:4860::6666"}; + @Before public void setup() { MockitoAnnotations.initMocks(this); @@ -128,4 +137,55 @@ public class DirectNetworkGuruTest { assertNotNull(guru.design(offering, plan, network, owner)); } + + @Test + public void testDesignDns() { + when(dcDao.findById(dc.getId())).thenReturn(dc); + when(plan.getDataCenterId()).thenReturn(1l); + when(plan.getPhysicalNetworkId()).thenReturn(1l); + when(physicalNetworkDao.findById(physicalNetwork.getId())).thenReturn(physicalNetwork); + when(offering.isRedundantRouter()).thenReturn(false); + when(network.getDns1()).thenReturn(ip4Dns[0]); + when(network.getDns2()).thenReturn(ip4Dns[1]); + when(network.getIp6Dns1()).thenReturn(ip6Dns[0]); + when(network.getIp6Dns2()).thenReturn(ip6Dns[1]); + + when(networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Network.Service.SecurityGroup)).thenReturn(false); + + Network config = guru.design(offering, plan, network, owner); + assertNotNull(config); + assertEquals(ip4Dns[0], config.getDns1()); + assertEquals(ip4Dns[1], config.getDns2()); + assertEquals(ip6Dns[0], config.getIp6Dns1()); + assertEquals(ip6Dns[1], config.getIp6Dns2()); + } + + @Test + public void testUpdateNicProfile() { + NicProfile nicProfile = new NicProfile(); + when(dcDao.findById(Mockito.anyLong())).thenReturn(dc); + when(networkModel.getNetworkIp4Dns(network, dc)).thenReturn(new Pair<>(ip4Dns[0], ip4Dns[1])); + when(networkModel.getNetworkIp6Dns(network, dc)).thenReturn(new Pair<>(ip6Dns[0], ip6Dns[1])); + guru.updateNicProfile(nicProfile, network); + assertNotNull(nicProfile); + assertEquals(ip4Dns[0], nicProfile.getIPv4Dns1()); + assertEquals(ip4Dns[1], nicProfile.getIPv4Dns2()); + assertEquals(ip6Dns[0], nicProfile.getIPv6Dns1()); + assertEquals(ip6Dns[1], nicProfile.getIPv6Dns2()); + } + + @Test + public void testUpdateNetworkProfile() { + NetworkProfile profile = new NetworkProfile(network); + when(dcDao.findById(Mockito.anyLong())).thenReturn(dc); + when(networkModel.getNetwork(Mockito.anyLong())).thenReturn(network); + when(networkModel.getNetworkIp4Dns(network, dc)).thenReturn(new Pair<>(ip4Dns[0], null)); + when(networkModel.getNetworkIp6Dns(network, dc)).thenReturn(new Pair<>(ip6Dns[0], null)); + guru.updateNetworkProfile(profile); + assertNotNull(profile); + assertEquals(ip4Dns[0], profile.getDns1()); + assertNull(profile.getDns2()); + assertEquals(ip6Dns[0], profile.getIp6Dns1()); + assertNull(profile.getIp6Dns2()); + } } diff --git a/server/src/test/java/com/cloud/network/guru/ExternalGuestNetworkGuruTest.java b/server/src/test/java/com/cloud/network/guru/ExternalGuestNetworkGuruTest.java new file mode 100644 index 00000000000..5a3db4819f9 --- /dev/null +++ b/server/src/test/java/com/cloud/network/guru/ExternalGuestNetworkGuruTest.java @@ -0,0 +1,123 @@ +// 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.network.guru; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.network.Network; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkProfile; +import com.cloud.network.Networks; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.offering.NetworkOffering; +import com.cloud.user.Account; +import com.cloud.utils.Pair; +import com.cloud.utils.component.ComponentContext; +import com.cloud.vm.NicProfile; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(ComponentContext.class) +public class ExternalGuestNetworkGuruTest { + @Mock + NetworkModel networkModel; + @Mock + DataCenterDao dataCenterDao; + @Mock + PhysicalNetworkDao physicalNetworkDao; + + @InjectMocks + protected ExternalGuestNetworkGuru guru = new ExternalGuestNetworkGuru(); + + final String[] ip4Dns = {"5.5.5.5", "6.6.6.6"}; + final String[] ip6Dns = {"2001:4860:4860::5555", "2001:4860:4860::6666"}; + + @Test + public void testDesignDns() { + Mockito.when(networkModel.areServicesSupportedByNetworkOffering(Mockito.anyLong(), Mockito.any())).thenReturn(false); + Mockito.when(networkModel.networkIsConfiguredForExternalNetworking(Mockito.anyLong(), Mockito.anyLong())).thenReturn(true); + NetworkOffering networkOffering = Mockito.mock(NetworkOffering.class); + Mockito.when(networkOffering.getTrafficType()).thenReturn(Networks.TrafficType.Guest); + Mockito.when(networkOffering.getGuestType()).thenReturn(Network.GuestType.Isolated); + DataCenterVO zone = Mockito.mock(DataCenterVO.class); + Mockito.when(zone.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced); + Mockito.when(dataCenterDao.findById(Mockito.anyLong())).thenReturn(zone); + PhysicalNetworkVO physicalNetwork = Mockito.mock(PhysicalNetworkVO.class); + Mockito.when(physicalNetwork.getId()).thenReturn(1L); + Mockito.when(physicalNetworkDao.findById(Mockito.anyLong())).thenReturn(physicalNetwork); + DeploymentPlan plan = Mockito.mock(DeploymentPlan.class); + Network network = Mockito.mock(Network.class); + Mockito.when(network.getDns1()).thenReturn(ip4Dns[0]); + Mockito.when(network.getDns2()).thenReturn(ip4Dns[1]); + Mockito.when(network.getIp6Dns1()).thenReturn(ip6Dns[0]); + Mockito.when(network.getIp6Dns2()).thenReturn(ip6Dns[1]); + Account owner = Mockito.mock(Account.class); + Network config = guru.design(networkOffering, plan, network, owner); + assertNotNull(config); + assertEquals(ip4Dns[0], config.getDns1()); + assertEquals(ip4Dns[1], config.getDns2()); + assertEquals(ip6Dns[0], config.getIp6Dns1()); + assertEquals(ip6Dns[1], config.getIp6Dns2()); + } + + @Test + public void testUpdateNicProfile() { + NicProfile nicProfile = new NicProfile(); + DataCenterVO zone = Mockito.mock(DataCenterVO.class); + Network network = Mockito.mock(Network.class); + Mockito.when(dataCenterDao.findById(Mockito.anyLong())).thenReturn(zone); + Mockito.when(networkModel.getNetworkIp4Dns(network, zone)).thenReturn(new Pair<>(ip4Dns[0], ip4Dns[1])); + Mockito.when(networkModel.getNetworkIp6Dns(network, zone)).thenReturn(new Pair<>(ip6Dns[0], ip6Dns[1])); + guru.updateNicProfile(nicProfile, network); + assertNotNull(nicProfile); + assertEquals(ip4Dns[0], nicProfile.getIPv4Dns1()); + assertEquals(ip4Dns[1], nicProfile.getIPv4Dns2()); + assertEquals(ip6Dns[0], nicProfile.getIPv6Dns1()); + assertEquals(ip6Dns[1], nicProfile.getIPv6Dns2()); + } + + @Test + public void testUpdateNetworkProfile() { + DataCenterVO zone = Mockito.mock(DataCenterVO.class); + Network network = Mockito.mock(Network.class); + NetworkProfile profile = new NetworkProfile(network); + Mockito.when(dataCenterDao.findById(Mockito.anyLong())).thenReturn(zone); + Mockito.when(networkModel.getNetwork(Mockito.anyLong())).thenReturn(network); + Mockito.when(networkModel.getNetworkIp4Dns(network, zone)).thenReturn(new Pair<>(ip4Dns[0], null)); + Mockito.when(networkModel.getNetworkIp6Dns(network, zone)).thenReturn(new Pair<>(ip6Dns[0], null)); + guru.updateNetworkProfile(profile); + assertNotNull(profile); + assertEquals(ip4Dns[0], profile.getDns1()); + assertNull(profile.getDns2()); + assertEquals(ip6Dns[0], profile.getIp6Dns1()); + assertNull(profile.getIp6Dns2()); + } +} 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 10b59fde8b1..9109bed8298 100644 --- a/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java +++ b/server/src/test/java/com/cloud/network/vpc/VpcManagerImplTest.java @@ -29,8 +29,11 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.UUID; import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd; +import org.apache.cloudstack.context.CallContext; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -38,28 +41,79 @@ import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.powermock.reflect.Whitebox; +import com.cloud.configuration.Resource; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceAllocationException; import com.cloud.network.Network; import com.cloud.network.Network.Capability; import com.cloud.network.Network.Provider; import com.cloud.network.Network.Service; import com.cloud.network.NetworkModel; import com.cloud.network.element.NetworkElement; +import com.cloud.network.vpc.dao.VpcOfferingDao; import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.user.AccountVO; +import com.cloud.user.ResourceLimitService; +import com.cloud.user.User; +import com.cloud.user.UserVO; import com.cloud.utils.net.NetUtils; + public class VpcManagerImplTest { @Mock - VpcOfferingServiceMapDao vpcOffSvcMapDao; + AccountManager accountManager; + @Mock + ResourceLimitService resourceLimitService; + @Mock + VpcOfferingDao vpcOfferingDao; + @Mock + DataCenterDao dataCenterDao; + @Mock + VpcOfferingServiceMapDao vpcOfferingServiceMapDao; VpcManagerImpl manager; + public static final long ACCOUNT_ID = 1; + private AccountVO account; + private UserVO user; + + private static final String IP4_GATEWAY = "10.0.16.1"; + private static final String IP4_NETMASK = "255.255.255.0"; + private static final String IP6_GATEWAY = "fd17:ac56:1234:2000::1"; + private static final String IP6_CIDR = "fd17:ac56:1234:2000::/64"; + + final String[] ip4Dns = {"5.5.5.5", "6.6.6.6"}; + final String[] ip6Dns = {"2001:4860:4860::5555", "2001:4860:4860::6666"}; + final String ip4Cidr = "172.16.0.0/22"; + final Long zoneId = 1L; + final Long vpcOfferingId = 1L; + final Long vpcOwnerId = 1L; + final String vpcName = "Test-VPC"; + final String vpcDomain = "domain"; + + private void registerCallContext() { + account = new AccountVO("testaccount", 1L, "networkdomain", Account.Type.NORMAL, "uuid"); + account.setId(ACCOUNT_ID); + user = new UserVO(1, "testuser", "password", "firstname", "lastName", "email", "timezone", + UUID.randomUUID().toString(), User.Source.UNKNOWN); + CallContext.register(user, account); + } + @Before public void setup() { MockitoAnnotations.initMocks(this); manager = new VpcManagerImpl(); - manager._vpcOffSvcMapDao = vpcOffSvcMapDao; + manager._accountMgr = accountManager; + manager._resourceLimitMgr = resourceLimitService; + manager._vpcOffDao = vpcOfferingDao; + manager._dcDao = dataCenterDao; + manager._vpcOffSvcMapDao = vpcOfferingServiceMapDao; + registerCallContext(); } @Test @@ -171,4 +225,47 @@ public class VpcManagerImplTest { Mockito.when(cmd.getInternetProtocol()).thenReturn(NetUtils.InternetProtocol.DualStack.toString()); manager.createVpcOffering(cmd); } + + private void mockVpcDnsResources(boolean supportDnsService, boolean isIpv6) { + Mockito.when(accountManager.getAccount(vpcOwnerId)).thenReturn(account); + VpcOfferingVO vpcOfferingVO = Mockito.mock(VpcOfferingVO.class); + Mockito.when(vpcOfferingVO.getId()).thenReturn(vpcOfferingId); + Mockito.when(vpcOfferingVO.getState()).thenReturn(VpcOffering.State.Enabled); + Mockito.when(vpcOfferingDao.findById(vpcOfferingId)).thenReturn(vpcOfferingVO); + DataCenterVO dataCenterVO = Mockito.mock(DataCenterVO.class); + Mockito.when(dataCenterVO.getId()).thenReturn(zoneId); + Mockito.when(dataCenterDao.findById(zoneId)).thenReturn(dataCenterVO); + Mockito.doNothing().when(accountManager).checkAccess(account, vpcOfferingVO, dataCenterVO); + Mockito.when(vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(vpcOfferingId, new Service[]{Service.Dns})).thenReturn(supportDnsService); + Mockito.when(vpcOfferingDao.isIpv6Supported(vpcOfferingId)).thenReturn(isIpv6); + try { + Mockito.doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc); + } catch (ResourceAllocationException e) { + Assert.fail(String.format("checkResourceLimit failure with exception: %s", e.getMessage())); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testCreateVpcDnsOfferingServiceFailure() { + mockVpcDnsResources(false, false); + try { + Mockito.doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc); + manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain, + ip4Dns[0], null, null, null, true); + } catch (ResourceAllocationException e) { + Assert.fail(String.format("failure with exception: %s", e.getMessage())); + } + } + + @Test(expected = InvalidParameterValueException.class) + public void testCreateVpcDnsIpv6OfferingFailure() { + mockVpcDnsResources(true, false); + try { + Mockito.doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc); + manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain, + ip4Dns[0], ip4Dns[1], ip6Dns[0], null, 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 09f00b7925f..f4738649107 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -663,8 +663,8 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches @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) throws ConcurrentOperationException, InsufficientCapacityException, - ResourceAllocationException { + String cidrv6, Boolean displayNetworkEnabled, String isolatedPvlan, Network.PVlanType isolatedPvlanType, String externalId, String routerIp, String routerIpv6, + String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2) throws ConcurrentOperationException, ResourceAllocationException { // TODO Auto-generated method stub return null; } diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java index 83ffa034761..ccb78e3b027 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkModelImpl.java @@ -25,6 +25,7 @@ import java.util.Set; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.DataCenter; import com.cloud.dc.Vlan; import com.cloud.exception.InsufficientAddressCapacityException; import com.cloud.exception.InvalidParameterValueException; @@ -52,6 +53,7 @@ import com.cloud.offering.NetworkOffering.Detail; import com.cloud.offerings.NetworkOfferingVO; import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; import com.cloud.user.Account; +import com.cloud.utils.Pair; import com.cloud.utils.component.ManagerBase; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; @@ -933,4 +935,19 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel { return null; } + @Override + public Pair getNetworkIp4Dns(Network network, DataCenter zone) { + return new Pair<>(null, null); + } + + @Override + public Pair getNetworkIp6Dns(Network network, DataCenter zone) { + return new Pair<>(null, null); + } + + @Override + public void verifyIp4DnsPair(String ip4Dns1, String ip4Dns2) {} + + @Override + public void verifyIp6DnsPair(String ip4Dns1, String ip4Dns2) {} } diff --git a/server/src/test/java/com/cloud/vpc/VpcApiUnitTest.java b/server/src/test/java/com/cloud/vpc/VpcApiUnitTest.java index 8899a045329..66e6d65834d 100644 --- a/server/src/test/java/com/cloud/vpc/VpcApiUnitTest.java +++ b/server/src/test/java/com/cloud/vpc/VpcApiUnitTest.java @@ -42,7 +42,7 @@ import junit.framework.TestCase; public class VpcApiUnitTest extends TestCase { @Inject VpcManagerImpl _vpcService = null; - VpcVO _vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false, false, false); + VpcVO _vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false, false, false, null, null, null, null); @Override @Before diff --git a/server/src/test/java/com/cloud/vpc/dao/MockVpcDaoImpl.java b/server/src/test/java/com/cloud/vpc/dao/MockVpcDaoImpl.java index f20de036f4c..6322a1d169e 100644 --- a/server/src/test/java/com/cloud/vpc/dao/MockVpcDaoImpl.java +++ b/server/src/test/java/com/cloud/vpc/dao/MockVpcDaoImpl.java @@ -96,9 +96,9 @@ public class MockVpcDaoImpl extends GenericDaoBase implements VpcDa public VpcVO findById(Long id) { VpcVO vo = null; if (id.longValue() == 1) { - vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false, false, false); + vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false, false, false, null, null, null, null); } else if (id.longValue() == 2) { - vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false, false, false); + vo = new VpcVO(1, "new vpc", "new vpc", 1, 1, 1, "0.0.0.0/0", "vpc domain", false, false, false, null, null, null, null); vo.setState(State.Inactive); } diff --git a/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java b/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java index ba57212e5a0..59437bf8cb1 100644 --- a/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java +++ b/server/src/test/java/com/cloud/vpc/dao/MockVpcOfferingServiceMapDaoImpl.java @@ -41,7 +41,7 @@ public class MockVpcOfferingServiceMapDaoImpl extends GenericDaoBase> /etc/dnsmasq-resolv.conf echo "nameserver $NS2" >> /etc/resolv.conf fi + + if [ -n "$IP6_NS1" ] + then + echo "nameserver $IP6_NS1" >> /etc/dnsmasq-resolv.conf + echo "nameserver $IP6_NS1" >> /etc/resolv.conf + fi + if [ -n "$IP6_NS2" ] + then + echo "nameserver $IP6_NS2" >> /etc/dnsmasq-resolv.conf + echo "nameserver $IP6_NS2" >> /etc/resolv.conf + fi + if [ -n "$MGMTNET" -a -n "$LOCAL_GW" ] then if [ "$HYPERVISOR" == "vmware" ] || [ "$HYPERVISOR" == "hyperv" ]; diff --git a/test/integration/component/test_network_vpc_custom_dns.py b/test/integration/component/test_network_vpc_custom_dns.py new file mode 100644 index 00000000000..bb44d79697b --- /dev/null +++ b/test/integration/component/test_network_vpc_custom_dns.py @@ -0,0 +1,732 @@ +# 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. +""" BVT test for custom DNS for Isolated network and VPC""" + +#Import Local Modules +from marvin.codes import (PASS, + FAIL, + FAILED) +from marvin.cloudstackTestCase import cloudstackTestCase +from marvin.cloudstackAPI import (createGuestNetworkIpv6Prefix, + listGuestNetworkIpv6Prefixes, + deleteGuestNetworkIpv6Prefix) +from marvin.lib.utils import (get_process_status, + get_host_credentials) +from marvin.lib.base import (Configurations, + Domain, + NetworkOffering, + VpcOffering, + Account, + PublicIpRange, + Network, + VPC, + Router, + ServiceOffering, + VirtualMachine, + Host, + NetworkACLList, + NetworkACL) +from marvin.lib.common import (get_domain, + get_zone, + get_test_template) +from marvin.sshClient import SshClient +from marvin.cloudstackException import CloudstackAPIException +from marvin.lib.decoratorGenerators import skipTestIf + +from nose.plugins.attrib import attr +from ipaddress import IPv6Network +from random import getrandbits +import time +import logging + +IP6_OFFERING_CONFIG_NAME = "ipv6.offering.enabled" + +IP4_DNS1 = "5.5.5.5" +IP4_DNS2 = "6.6.6.6" +IP6_DNS1 = "2001:4860:4860::5555" +IP6_DNS2 = "2001:4860:4860::6666" + +VPC_DATA = { + "cidr": "10.1.0.0/22", + "tier1_gateway": "10.1.1.1", + "tier2_gateway": "10.1.2.1", + "tier_netmask": "255.255.255.0" +} + +routerDetailsMap = {} + +def getRouterProcessStatus(apiclient, hypervisor, config, router, cmd): + if router.id not in routerDetailsMap or routerDetailsMap[router.id] is None: + connect_ip = apiclient.connection.mgtSvr + connect_user = apiclient.connection.user + connect_passwd = apiclient.connection.passwd + if hypervisor.lower() not in ('vmware', 'hyperv'): + hosts = Host.list( + apiclient, + zoneid=router.zoneid, + type='Routing', + state='Up', + id=router.hostid + ) + if not isinstance(hosts, list): + return [FAIL, "list host returns an invalid list"] + host = hosts[0] + connect_ip = host.ipaddress + hypervisor = None + try: + connect_user, connect_passwd= get_host_credentials( + config, host.ipaddress) + except KeyError: + return [FAIL, "Marvin configuration has no host credentials to check router services"] + details = {} + details['connect_ip'] = connect_ip + details['connect_user'] = connect_user + details['connect_passwd'] = connect_passwd + details['hypervisor'] = hypervisor + routerDetailsMap[router.id] = details + result = get_process_status( + routerDetailsMap[router.id]['connect_ip'], + 22, + routerDetailsMap[router.id]['connect_user'], + routerDetailsMap[router.id]['connect_passwd'], + router.linklocalip, + cmd, + hypervisor=routerDetailsMap[router.id]['hypervisor'] + ) + if type(result) != list or len(result) == 0: + return [FAIL, "%s on router %s returned invalid result" % (cmd, router.id)] + result = '\n'.join(result) + return [PASS, result] + +class TestNetworkCustomDns(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestNetworkCustomDns, cls).getClsTestClient() + cls.services = testClient.getParsedTestDataConfig() + cls.apiclient = testClient.getApiClient() + cls.dbclient = testClient.getDbConnection() + cls.test_ipv6_guestprefix = None + cls.initial_ipv6_offering_enabled = None + cls._cleanup = [] + routerDetailsMap = {} + + cls.logger = logging.getLogger('TestNetworkCustomDns') + + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls.services['mode'] = cls.zone.networktype + cls.ipv6NotSupported = False + + ipv6_guestprefix = cls.getGuestIpv6Prefix() + if ipv6_guestprefix == None: + cls.ipv6NotSupported = True + if cls.ipv6NotSupported == False: + ipv6_publiciprange = cls.getPublicIpv6Range() + if ipv6_publiciprange == None: + cls.ipv6NotSupported = True + + if cls.ipv6NotSupported == False: + cls.initial_ipv6_offering_enabled = Configurations.list( + cls.apiclient, + name=IP6_OFFERING_CONFIG_NAME)[0].value + Configurations.update(cls.apiclient, + IP6_OFFERING_CONFIG_NAME, + "true") + else: + cls.debug("IPv6 is not supported, skipping testing IPv6 details!") + cls.domain = get_domain(cls.apiclient) + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + cls.hypervisor = testClient.getHypervisorInfo() + cls.template = get_test_template( + cls.apiclient, + cls.zone.id, + cls.hypervisor) + return + + @classmethod + def tearDownClass(cls): + if cls.initial_ipv6_offering_enabled != None: + Configurations.update(cls.apiclient, + IP6_OFFERING_CONFIG_NAME, + cls.initial_ipv6_offering_enabled) + super(TestNetworkCustomDns, cls).tearDownClass() + if cls.test_ipv6_guestprefix != None: + cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() + cmd.id = cls.test_ipv6_guestprefix.id + cls.apiclient.deleteGuestNetworkIpv6Prefix(cmd) + + @classmethod + def getGuestIpv6Prefix(cls): + cmd = listGuestNetworkIpv6Prefixes.listGuestNetworkIpv6PrefixesCmd() + cmd.zoneid = cls.zone.id + ipv6_prefixes_response = cls.apiclient.listGuestNetworkIpv6Prefixes(cmd) + if isinstance(ipv6_prefixes_response, list) == True and len(ipv6_prefixes_response) > 0: + return ipv6_prefixes_response[0] + ipv6_guestprefix_service = cls.services["guestip6prefix"] + cmd = createGuestNetworkIpv6Prefix.createGuestNetworkIpv6PrefixCmd() + cmd.zoneid = cls.zone.id + cmd.prefix = ipv6_guestprefix_service["prefix"] + ipv6_guestprefix = cls.apiclient.createGuestNetworkIpv6Prefix(cmd) + cls.test_ipv6_guestprefix = ipv6_guestprefix + return ipv6_guestprefix + + @classmethod + def getPublicIpv6Range(cls): + list_public_ip_range_response = PublicIpRange.list( + cls.apiclient, + zoneid=cls.zone.id + ) + ipv4_range_vlan = None + if isinstance(list_public_ip_range_response, list) == True and len(list_public_ip_range_response) > 0: + for ip_range in list_public_ip_range_response: + if ip_range.ip6cidr != None and ip_range.ip6gateway != None: + return ip_range + if ip_range.netmask != None and ip_range.gateway != None: + vlan = ip_range.vlan + if ipv4_range_vlan == None and vlan.startswith("vlan://"): + vlan = vlan.replace("vlan://", "") + if vlan == "untagged": + ipv4_range_vlan = None + else: + ipv4_range_vlan = int(vlan) + ipv6_publiciprange_service = cls.services["publicip6range"] + ipv6_publiciprange_service["zoneid"] = cls.zone.id + ipv6_publiciprange_service["vlan"] = ipv4_range_vlan + ipv6_publiciprange = PublicIpRange.create( + cls.apiclient, + ipv6_publiciprange_service + ) + cls._cleanup.append(ipv6_publiciprange) + return ipv6_publiciprange + + def setUp(self): + self.services = self.testClient.getParsedTestDataConfig() + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + super(TestNetworkCustomDns, self).tearDown() + + def createTinyServiceOffering(self): + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["big"], + ) + self.cleanup.append(self.service_offering) + + def createNetworkOffering(self): + off_service = self.services["network_offering"] + if not self.ipv6NotSupported: + off_service["internetprotocol"] = "dualstack" + self.network_offering = NetworkOffering.create( + self.apiclient, + off_service + ) + self.cleanup.append(self.network_offering) + self.network_offering.update(self.apiclient, state='Enabled') + + + def deployNetwork(self): + network_service = self.services["network"] + network_service["networkoffering"] = self.network_offering.id + network_service["dns1"] = IP4_DNS1 + network_service["dns2"] = IP4_DNS2 + if not self.ipv6NotSupported: + network_service["ip6dns1"] = IP6_DNS1 + network_service["ip6dns2"] = IP6_DNS2 + self.network = Network.create( + self.apiclient, + self.services["network"], + self.account.name, + self.account.domainid, + zoneid=self.zone.id + ) + self.cleanup.append(self.network) + + def deployNetworkVm(self): + if self.template == FAILED: + assert False, "get_test_template() failed to return template" + self.services["virtual_machine"]["zoneid"] = self.zone.id + self.virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=self.network.id, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(self.virtual_machine) + + def getNetworkRouter(self, network, red_state="PRIMARY"): + routers = Router.list( + self.apiclient, + networkid=network.id, + listall=True + ) + self.assertTrue( + isinstance(routers, list) and len(routers) > 0, + "No routers found for network %s" % network.id + ) + if len(routers) == 1: + return routers[0] + for router in routers: + if router.redundantstate == red_state: + return router + + def checkNetwork(self): + self.debug("Listing network: %s" % (self.network.name)) + network = Network.list(self.apiclient,listall="true",id=self.network.id) + self.assertTrue( + isinstance(network, list), + "Check listNetworks response returns a valid list" + ) + self.assertEqual( + len(network), + 1, + "Network not found" + ) + network = network[0] + self.assertNotEqual(network, + None, + "User is not able to retrieve network details %s" % self.network.id) + self.assertEqual(network.dns1, + IP4_DNS1, + "IPv4 DNS1 is not same, expected=%s, actual=%s" % (IP4_DNS1, network.dns1)) + self.assertEqual(network.dns2, + IP4_DNS2, + "IPv4 DNS2 is not same, expected=%s, actual=%s" % (IP4_DNS2, network.dns2)) + if not self.ipv6NotSupported: + self.assertEqual(network.ip6dns1, + IP6_DNS1, + "IPv6 DNS1 is not same, expected=%s, actual=%s" % (IP6_DNS1, network.ip6dns1)) + self.assertEqual(network.ip6dns2, + IP6_DNS2, + "IPv6 DNS2 is not same, expected=%s, actual=%s" % (IP6_DNS2, network.ip6dns2)) + + def checkNetworkRouter(self): + router = self.getNetworkRouter(self.network) + dns_cmd = "cat /etc/resolv.conf" + res = getRouterProcessStatus(self.apiclient, self.hypervisor, self.config, router, dns_cmd) + if res[0] == FAIL: + self.fail("Failed to get router command result. %s" % res[1]) + res = res[1] + ns = "nameserver %s" % IP4_DNS1 + self.assertTrue(ns in res, + "Network router doesn't contain nameserver for DNS1: %s" % IP4_DNS1) + ns = "nameserver %s" % IP4_DNS2 + self.assertTrue(ns in res, + "Network router doesn't contain nameserver for DNS2: %s" % IP4_DNS2) + if not self.ipv6NotSupported: + ns = "nameserver %s" % IP6_DNS1 + self.assertTrue(ns in res, + "Network router doesn't contain nameserver for IPv6 DNS1: %s" % IP6_DNS1) + ns = "nameserver %s" % IP6_DNS2 + self.assertTrue(ns in res, + "Network router doesn't contain nameserver for IPv6 DNS2: %s" % IP6_DNS2) + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + def test_01_verify_network_dns(self): + """Test to verify custom DNS for network + + # Validate the following: + # 1. Create network, deploy VM + # 2. Verify network has required DNS details + # 3. SSH into VR for the network and verify it has required DNS details as nameserver + """ + + self.createNetworkOffering() + self.createTinyServiceOffering() + self.deployNetwork() + self.deployNetworkVm() + self.checkNetwork() + self.checkNetworkRouter() + +class TestVpcCustomDns(cloudstackTestCase): + + @classmethod + def setUpClass(cls): + testClient = super(TestVpcCustomDns, cls).getClsTestClient() + cls.services = testClient.getParsedTestDataConfig() + cls.apiclient = testClient.getApiClient() + cls.dbclient = testClient.getDbConnection() + cls.test_ipv6_guestprefix = None + cls.initial_ipv6_offering_enabled = None + cls._cleanup = [] + routerDetailsMap = {} + cls.vpcAllowAllAclDetailsMap = {} + + cls.logger = logging.getLogger('TestVpcCustomDns') + + cls.zone = get_zone(cls.apiclient, testClient.getZoneForTests()) + cls.services['mode'] = cls.zone.networktype + cls.ipv6NotSupported = False + + ipv6_guestprefix = cls.getGuestIpv6Prefix() + if ipv6_guestprefix == None: + cls.ipv6NotSupported = True + if cls.ipv6NotSupported == False: + ipv6_publiciprange = cls.getPublicIpv6Range() + if ipv6_publiciprange == None: + cls.ipv6NotSupported = True + + if cls.ipv6NotSupported == False: + cls.initial_ipv6_offering_enabled = Configurations.list( + cls.apiclient, + name=IP6_OFFERING_CONFIG_NAME)[0].value + Configurations.update(cls.apiclient, + IP6_OFFERING_CONFIG_NAME, + "true") + else: + cls.debug("IPv6 is not supported, skipping testing IPv6 details!") + cls.domain = get_domain(cls.apiclient) + cls.account = Account.create( + cls.apiclient, + cls.services["account"], + admin=True, + domainid=cls.domain.id + ) + cls._cleanup.append(cls.account) + cls.hypervisor = testClient.getHypervisorInfo() + cls.template = get_test_template( + cls.apiclient, + cls.zone.id, + cls.hypervisor) + return + + @classmethod + def tearDownClass(cls): + if cls.initial_ipv6_offering_enabled != None: + Configurations.update(cls.apiclient, + IP6_OFFERING_CONFIG_NAME, + cls.initial_ipv6_offering_enabled) + super(TestVpcCustomDns, cls).tearDownClass() + if cls.test_ipv6_guestprefix != None: + cmd = deleteGuestNetworkIpv6Prefix.deleteGuestNetworkIpv6PrefixCmd() + cmd.id = cls.test_ipv6_guestprefix.id + cls.apiclient.deleteGuestNetworkIpv6Prefix(cmd) + + @classmethod + def getGuestIpv6Prefix(cls): + cmd = listGuestNetworkIpv6Prefixes.listGuestNetworkIpv6PrefixesCmd() + cmd.zoneid = cls.zone.id + ipv6_prefixes_response = cls.apiclient.listGuestNetworkIpv6Prefixes(cmd) + if isinstance(ipv6_prefixes_response, list) == True and len(ipv6_prefixes_response) > 0: + return ipv6_prefixes_response[0] + ipv6_guestprefix_service = cls.services["guestip6prefix"] + cmd = createGuestNetworkIpv6Prefix.createGuestNetworkIpv6PrefixCmd() + cmd.zoneid = cls.zone.id + cmd.prefix = ipv6_guestprefix_service["prefix"] + ipv6_guestprefix = cls.apiclient.createGuestNetworkIpv6Prefix(cmd) + cls.test_ipv6_guestprefix = ipv6_guestprefix + return ipv6_guestprefix + + @classmethod + def getPublicIpv6Range(cls): + list_public_ip_range_response = PublicIpRange.list( + cls.apiclient, + zoneid=cls.zone.id + ) + ipv4_range_vlan = None + if isinstance(list_public_ip_range_response, list) == True and len(list_public_ip_range_response) > 0: + for ip_range in list_public_ip_range_response: + if ip_range.ip6cidr != None and ip_range.ip6gateway != None: + return ip_range + if ip_range.netmask != None and ip_range.gateway != None: + vlan = ip_range.vlan + if ipv4_range_vlan == None and vlan.startswith("vlan://"): + vlan = vlan.replace("vlan://", "") + if vlan == "untagged": + ipv4_range_vlan = None + else: + ipv4_range_vlan = int(vlan) + ipv6_publiciprange_service = cls.services["publicip6range"] + ipv6_publiciprange_service["zoneid"] = cls.zone.id + ipv6_publiciprange_service["vlan"] = ipv4_range_vlan + ipv6_publiciprange = PublicIpRange.create( + cls.apiclient, + ipv6_publiciprange_service + ) + cls._cleanup.append(ipv6_publiciprange) + return ipv6_publiciprange + + def setUp(self): + self.services = self.testClient.getParsedTestDataConfig() + self.apiclient = self.testClient.getApiClient() + self.dbclient = self.testClient.getDbConnection() + self.cleanup = [] + return + + def tearDown(self): + super(TestVpcCustomDns, self).tearDown() + + def createTinyServiceOffering(self): + self.service_offering = ServiceOffering.create( + self.apiclient, + self.services["service_offerings"]["big"], + ) + self.cleanup.append(self.service_offering) + + def createVpcOfferingInternal(self, is_redundant, is_ipv6): + off_service = self.services["vpc_offering"] + if is_redundant: + off_service["serviceCapabilityList"] = { + "SourceNat": { + "RedundantRouter": 'true' + }, + } + if is_ipv6: + off_service["internetprotocol"] = "dualstack" + vpc_offering = VpcOffering.create( + self.apiclient, + off_service + ) + self.cleanup.append(vpc_offering) + vpc_offering.update(self.apiclient, state='Enabled') + return vpc_offering + + def createVpcOffering(self, is_redundant=False): + self.vpc_offering = self.createVpcOfferingInternal(is_redundant, self.ipv6NotSupported == False) + + def deployAllowAllVpcInternal(self, cidr): + service = self.services["vpc"] + service["cidr"] = cidr + ip6Dns1 = None + ip6Dns2 = None + if not self.ipv6NotSupported: + ip6Dns1 = IP6_DNS1 + ip6Dns2 = IP6_DNS2 + + vpc = VPC.create( + self.apiclient, + self.services["vpc"], + vpcofferingid=self.vpc_offering.id, + zoneid=self.zone.id, + account=self.account.name, + domainid=self.account.domainid, + dns1=IP4_DNS1, + dns2=IP4_DNS2, + ip6dns1=ip6Dns1, + ip6dns2=ip6Dns2 + ) + self.cleanup.append(vpc) + acl = NetworkACLList.create( + self.apiclient, + services={}, + name="allowall", + description="allowall", + vpcid=vpc.id + ) + rule ={ + "protocol": "all", + "traffictype": "ingress", + } + NetworkACL.create(self.apiclient, + services=rule, + aclid=acl.id + ) + rule["traffictype"] = "egress" + NetworkACL.create(self.apiclient, + services=rule, + aclid=acl.id + ) + self.vpcAllowAllAclDetailsMap[vpc.id] = acl.id + return vpc + + def deployVpc(self): + self.vpc = self.deployAllowAllVpcInternal(VPC_DATA["cidr"]) + + def createNetworkTierOfferingInternal(self, is_ipv6, remove_lb=True): + off_service = self.services["nw_offering_isolated_vpc"] + if not remove_lb: # Remove Lb service + if "serviceProviderList" in off_service and "Lb" in off_service["serviceProviderList"].keys(): + providers = off_service["serviceProviderList"] + providers.pop("Lb") + off_service["serviceProviderList"] = providers + if "supportedservices" in off_service and "Lb" in off_service["supportedservices"]: + supportedServices = off_service["supportedservices"].split(",") + supportedServices.remove("Lb") + off_service["supportedservices"] = ",".join(supportedServices) + if is_ipv6: + off_service["internetprotocol"] = "dualstack" + network_offering = NetworkOffering.create( + self.apiclient, + off_service, + conservemode=False + ) + self.cleanup.append(network_offering) + network_offering.update(self.apiclient, state='Enabled') + return network_offering + + def createNetworkTierOffering(self): + self.network_offering = self.createNetworkTierOfferingInternal(self.ipv6NotSupported == False) + + def deployNetworkTierInternal(self, network_offering_id, vpc_id, tier_gateway, tier_netmask, acl_id=None, tier_name=None): + if not acl_id and vpc_id in self.vpcAllowAllAclDetailsMap: + acl_id = self.vpcAllowAllAclDetailsMap[vpc_id] + service = self.services["ntwk"] + if tier_name: + service["name"] = tier_name + service["displaytext"] = "vpc-%s" % tier_name + network = Network.create( + self.apiclient, + service, + self.account.name, + self.account.domainid, + networkofferingid=network_offering_id, + vpcid=vpc_id, + zoneid=self.zone.id, + gateway=tier_gateway, + netmask=tier_netmask, + aclid=acl_id + ) + self.cleanup.append(network) + return network + + def deployNetworkTier(self): + self.network = self.deployNetworkTierInternal( + self.network_offering.id, + self.vpc.id, + VPC_DATA["tier1_gateway"], + VPC_DATA["tier_netmask"] + ) + + def deployNetworkTierVmInternal(self, network): + if self.template == FAILED: + assert False, "get_test_template() failed to return template" + self.services["virtual_machine"]["zoneid"] = self.zone.id + virtual_machine = VirtualMachine.create( + self.apiclient, + self.services["virtual_machine"], + templateid=self.template.id, + accountid=self.account.name, + domainid=self.account.domainid, + networkids=network, + serviceofferingid=self.service_offering.id + ) + self.cleanup.append(virtual_machine) + return virtual_machine + + def deployNetworkTierVm(self): + self.virtual_machine = self.deployNetworkTierVmInternal(self.network.id) + + def checkVpcBasic(self): + self.debug("Listing VPC: %s" % (self.vpc.name)) + vpc = VPC.list(self.apiclient,listall="true",id=self.vpc.id) + self.assertTrue( + isinstance(vpc, list), + "Check listVpcs response returns a valid list" + ) + self.assertEqual( + len(vpc), + 1, + "Network not found" + ) + vpc = vpc[0] + self.assertEqual(vpc.dns1, + IP4_DNS1, + "IPv4 DNS1 is not same, expected=%s, actual=%s" % (IP4_DNS1, vpc.dns1)) + self.assertEqual(vpc.dns2, + IP4_DNS2, + "IPv4 DNS2 is not same, expected=%s, actual=%s" % (IP4_DNS2, vpc.dns2)) + if not self.ipv6NotSupported: + self.assertEqual(vpc.ip6dns1, + IP6_DNS1, + "IPv6 DNS1 is not same, expected=%s, actual=%s" % (IP6_DNS1, vpc.ip6dns1)) + self.assertEqual(vpc.ip6dns2, + IP6_DNS2, + "IPv6 DNS2 is not same, expected=%s, actual=%s" % (IP6_DNS2, vpc.ip6dns2)) + + def getVpcRouter(self, vpc, red_state="PRIMARY"): + routers = Router.list( + self.apiclient, + vpcid=vpc.id, + listall=True + ) + self.assertTrue( + isinstance(routers, list) and len(routers) > 0, + "No routers found for VPC %s" % vpc.id + ) + if len(routers) == 1: + return routers[0] + for router in routers: + if router.redundantstate == red_state: + return router + + def checkVpcRouter(self): + router = self.getVpcRouter(self.vpc) + dns_cmd = "cat /etc/resolv.conf" + res = getRouterProcessStatus(self.apiclient, self.hypervisor, self.config, router, dns_cmd) + if res[0] == FAIL: + self.fail("Failed to get router command result. %s" % res[1]) + res = res[1] + ns = "nameserver %s" % IP4_DNS1 + self.assertTrue(ns in res, + "Network router doesn't contain nameserver for DNS1: %s" % IP4_DNS1) + ns = "nameserver %s" % IP4_DNS2 + self.assertTrue(ns in res, + "Network router doesn't contain nameserver for DNS2: %s" % IP4_DNS2) + if not self.ipv6NotSupported: + ns = "nameserver %s" % IP6_DNS1 + self.assertTrue(ns in res, + "Network router doesn't contain nameserver for IPv6 DNS1: %s" % IP6_DNS1) + ns = "nameserver %s" % IP6_DNS2 + self.assertTrue(ns in res, + "Network router doesn't contain nameserver for IPv6 DNS2: %s" % IP6_DNS2) + + @attr( + tags=[ + "advanced", + "basic", + "eip", + "sg", + "advancedns", + "smoke"], + required_hardware="false") + def test_01_verify_vpc_dns(self): + """Test to verify custom DNS for VPC + # Validate the following: + # 1. Create VPC, deploy network tier and VM in it + # 2. Verify VPC details + # 3. Verify VPC router details + """ + + self.createVpcOffering() + self.createNetworkTierOffering() + self.createTinyServiceOffering() + self.deployVpc() + self.deployNetworkTier() + self.deployNetworkTierVm() + self.checkVpcBasic() + self.checkVpcRouter() + diff --git a/tools/marvin/marvin/lib/base.py b/tools/marvin/marvin/lib/base.py index 00d212b139f..c6a595810b0 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -3240,6 +3240,14 @@ class Network: cmd.endipv6 = services["endipv6"] if "routeripv6" in services: cmd.routeripv6 = services["routeripv6"] + if "dns1" in services: + cmd.dns1 = services["dns1"] + if "dns2" in services: + cmd.dns2 = services["dns2"] + if "ip6dns1" in services: + cmd.ip6dns1 = services["ip6dns1"] + if "ip6dns2" in services: + cmd.ip6dns2 = services["ip6dns2"] if accountid: cmd.account = accountid diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js index 94e7ffeabc1..b83087396dc 100644 --- a/ui/src/config/section/network.js +++ b/ui/src/config/section/network.js @@ -39,7 +39,7 @@ export default { return fields }, details: () => { - var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domain', 'associatednetwork', 'associatednetworkid', 'ip6firewall', 'ip6routing', 'ip6routes'] + var fields = ['name', 'id', 'description', 'type', 'traffictype', 'vpcid', 'vlan', 'broadcasturi', 'cidr', 'ip6cidr', 'netmask', 'gateway', 'aclname', 'ispersistent', 'restartrequired', 'reservediprange', 'redundantrouter', 'networkdomain', 'egressdefaultpolicy', 'zonename', 'account', 'domain', 'associatednetwork', 'associatednetworkid', 'ip6firewall', 'ip6routing', 'ip6routes', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2'] if (!isAdmin()) { fields = fields.filter(function (e) { return e !== 'broadcasturi' }) } @@ -166,7 +166,7 @@ export default { permission: ['listVPCs'], resourceType: 'Vpc', columns: ['name', 'state', 'displaytext', 'cidr', 'account', 'zonename'], - details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain'], + details: ['name', 'id', 'displaytext', 'cidr', 'networkdomain', 'ip6routes', 'ispersistent', 'redundantvpcrouter', 'restartrequired', 'zonename', 'account', 'domain', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2'], searchFilters: ['name', 'zoneid', 'domainid', 'account', 'tags'], related: [{ name: 'vm', diff --git a/ui/src/views/network/CreateIsolatedNetworkForm.vue b/ui/src/views/network/CreateIsolatedNetworkForm.vue index 7e6e83e9f93..6ea2389880a 100644 --- a/ui/src/views/network/CreateIsolatedNetworkForm.vue +++ b/ui/src/views/network/CreateIsolatedNetworkForm.vue @@ -105,14 +105,14 @@ - + - + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
service.name === 'Dns') + return dnsServices && dnsServices.length === 1 + } + return false + } + }, methods: { initForm () { this.formRef = ref() @@ -457,29 +513,11 @@ export default { displayText: values.displaytext, networkOfferingId: this.selectedNetworkOffering.id } - if (this.isValidTextValueForKey(values, 'gateway')) { - params.gateway = values.gateway - } - if (this.isValidTextValueForKey(values, 'netmask')) { - params.netmask = values.netmask - } - if (this.isValidTextValueForKey(values, 'startipv4')) { - params.startip = values.startipv4 - } - if (this.isValidTextValueForKey(values, 'endipv4')) { - params.endip = values.endipv4 - } - if (this.isValidTextValueForKey(values, 'externalid')) { - params.externalid = values.externalid - } - if (this.isValidTextValueForKey(values, 'vpcid')) { - params.vpcid = this.selectedVpc.id - } - if (this.isValidTextValueForKey(values, 'vlanid')) { - params.vlan = values.vlanid - } - if (this.isValidTextValueForKey(values, 'networkdomain')) { - params.networkdomain = values.networkdomain + var usefulFields = ['gateway', 'netmask', 'startip', 'endip', 'dns1', 'dns2', 'ip6dns1', 'ip6dns2', 'externalid', 'vpcid', 'vlan', 'networkdomain'] + for (var field of usefulFields) { + if (this.isValidTextValueForKey(values, field)) { + params[field] = values[field] + } } if ('domainid' in values && values.domainid > 0) { params.domainid = this.selectedDomain.id diff --git a/ui/src/views/network/CreateSharedNetworkForm.vue b/ui/src/views/network/CreateSharedNetworkForm.vue index 1b62e473dc7..1d26cf1b1ef 100644 --- a/ui/src/views/network/CreateSharedNetworkForm.vue +++ b/ui/src/views/network/CreateSharedNetworkForm.vue @@ -85,12 +85,12 @@ - + @@ -265,86 +265,158 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +