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 b106418f068b..bd08ce811013 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 void setIsolationuri(URI isolationUri) { 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 49408b9110da..0fee5c04d25c 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 void setIp6Address(String ip6Address) { 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 971bb308b1da..c27290f902d6 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.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 @@ List generateVmData(String userData, String serviceOffering, long data 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 f3c178767e85..5289bda62992 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 NetworkProfile(Network network) { externalId = network.getExternalId(); } + @Override public String getDns1() { return dns1; } + @Override public String getDns2() { return dns2; } @@ -114,6 +118,24 @@ public void setDns2(String dns2) { 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 432c8839ad89..664b6eb7e958 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 enum State { 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 088239708f19..3829e5dbd7ae 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.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 @@ 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 903793b6e94d..9973af2db9e4 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 Long getAclId() { 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 bb27579992fe..49a23a145f41 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 boolean getForced(){ } 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 8f6568fbe594..4df741f1d23a 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 String getNetworkDomain() { 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 Boolean getDisplayVpc() { @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 7bed68f99b41..19edc1b3944f 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 void setIpv6Routes(Set ipv6Routes) { 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 3b5661f8a80c..4ca594870ac6 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 void setIpv6Routes(Set ipv6Routes) { 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 b79cce5f604e..cde00df026e7 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 @@ void prepare(VirtualMachineProfile profile, DeployDestination dest, ReservationC 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 56e70ca56f1f..4974345d4e23 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 3284511e01a5..a8464f39c3ec 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 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.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.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 void setDhcpProviders(final List dhcpProviders) { 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 NicProfile prepareNic(final VirtualMachineProfile vmProfile, final Deploy profile.setSecurityGroupEnabled(_networkModel.isSecurityGroupSupportedInNetwork(network)); guru.updateNicProfile(profile, network); - + updateRouterDefaultDns(vmProfile, profile); configureExtraDhcpOptions(network, nicId); return profile; } @@ -2430,7 +2463,8 @@ public Network createPrivateNetwork(final long networkOfferingId, final String n // 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 Network createPrivateNetwork(final long networkOfferingId, final String n 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 Network doInTransaction(final TransactionStatus status) { 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 ff0a6faae2db..f9adc9c643d0 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.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 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 void setUp() { 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 @@ private void encodeVlanIdIntoBroadcastUriPrepareAndTest(String vlanId, String is 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 d0f3192c60c2..038daeaa10b5 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 void setFirewallProvider(String firewallProvider) { 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 DataCenterVO(String name, String description, String dns1, String dns2, S 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 4dbcda670a7a..e6869b4b264b 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.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 NetworkVO(long id, Network that, long offeringId, String guruName, long d 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 long getDataCenterId() { return dataCenterId; } + @Override public String getDns1() { return dns1; } @@ -479,6 +499,7 @@ public void setDns1(String dns) { dns1 = dns; } + @Override public String getDns2() { return dns2; } @@ -487,6 +508,24 @@ public void setDns2(String dns) { 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 83034b3fdbe0..c2c57b11fbff 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 VpcVO() { } 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 VpcVO(final long zoneId, final String name, final String displayText, fin 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 getEntityType() { 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 56de31819d28..06cfd25e670b 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 9e14bb5348f6..c7400f6edfd5 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 List listByVpcOffId(long vpcOffId) { } @Override - public boolean areServicesSupportedByNetworkOffering(long networkOfferingId, Service... services) { + public boolean areServicesSupportedByVpcOffering(long vpcOfferingId, Service... services) { SearchCriteria 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 4ec812cc1c0a..f57b75c73766 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 68acee88abe1..bf6932f05d43 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.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.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 void doInTransactionWithoutResult(TransactionStatus status) { } 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 @@ private void getBaremetalIp(NicProfile nic, Pod pod, VirtualMachineProfile vm, N 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 fa96e8b24b77..e1c205e29cb2 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.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 @@ private Network getKubernetesClusterNetworkIfMissing(final String clusterName, f 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 411d0e1c0a35..52502f39cf12 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 NetworkResponse createNetworkResponse(ResponseView view, Network network) 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 VpcResponse createVpcResponse(ResponseView view, Vpc vpc) { 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 20b7a2b67d6b..3c8b6c58d9bf 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 NicTO toNicTO(NicProfile profile) { 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 7ec4d0fd37ef..010925b70062 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 Ternary, Network> doInTransaction(Transa 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 void doInTransactionWithoutResult(TransactionStatus status) throws Insuff 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 void doInTransactionWithoutResult(TransactionStatus status) throws Insuff 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 2a53708fc59b..0d683292cfaa 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.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 void setNicIp6Address(final NicProfile nic, final DataCenter dc, final Ne 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 dd0f6c49e37f..47537a69d199 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 void updateNicIpv6(NicProfile nic, DataCenter dc, Network network) throws } 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 748ea5c68a67..1df42a54acd2 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 Long makeCopyOfVpc(long vpcId, long vpcOfferingId) { 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 c601c13e6ad5..d183ba0dd921 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.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.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 String getValidNetworkCidr(Network guestNetwork) { 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 6428e064bb1d..3cb5b415c075 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.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; @@ -557,6 +558,70 @@ private Set getPublicIpPurposeInRules(PublicIp ip, boolean includeRevok 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) { @@ -1259,6 +1324,10 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac 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); @@ -1472,7 +1541,7 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac 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."); } @@ -1595,9 +1664,11 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac 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)); @@ -1739,7 +1810,7 @@ private Network commitNetwork(final Long networkOfferingId, final String gateway 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 @@ -1798,7 +1869,7 @@ public Network doInTransaction(TransactionStatus status) throws InsufficientCapa } } 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"); @@ -1808,7 +1879,7 @@ public Network doInTransaction(TransactionStatus status) throws InsufficientCapa } 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) { @@ -2573,6 +2644,10 @@ public Network updateGuestNetwork(final UpdateNetworkCmd cmd) { 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; @@ -2701,6 +2776,11 @@ public Network updateGuestNetwork(final UpdateNetworkCmd cmd) { } } + 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 5629eb2d1c17..7d7fb1564e89 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 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.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.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 Network design(NetworkOffering offering, DeploymentPlan plan, Network use 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 IsolationMethod[] getIsolationMethods() { @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 void doInTransactionWithoutResult(TransactionStatus status) { @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 01b33893b5c5..f0ddc128b956 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.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 void doInTransactionWithoutResult(TransactionStatus status) { } 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 void doInTransactionWithoutResult(TransactionStatus status) throws Insuff } }); - 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 6524abe1e455..2f7051154c84 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.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 void reserve(NicProfile nic, Network config, VirtualMachineProfile vm, De 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 be3d57eb48d4..47d90aa8816f 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.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 Network design(final NetworkOffering offering, final DeploymentPlan plan, 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 NicProfile allocate(final Network network, NicProfile nic, final VirtualM 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 NicProfile allocate(final Network network, NicProfile nic, final VirtualM @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 boolean trash(final Network network, final NetworkOffering offering) { @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 6e2f1db5f27a..a5eac9aac1b8 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.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 @@ protected void getIp(NicProfile nic, DataCenter dc, Network network) throws Insu 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 boolean trash(Network network, NetworkOffering offering) { @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 c3f7ee3d9416..e8374b39f53d 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.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.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 @@ protected void getIp(NicProfile nic, DataCenter dc, VirtualMachineProfile vm, Ne 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 @@ protected void getIp(NicProfile nic, DataCenter dc, VirtualMachineProfile vm, Ne @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 boolean trash(Network network, NetworkOffering offering) { @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 6839a6ae135b..517b65b83e16 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 SetupGuestNetworkCommand createSetupGuestNetworkCommand(final DomainRoute 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 5ce43629087d..c8daff60ba81 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 @@ public boolean finalizeVirtualMachineProfile(final VirtualMachineProfile profile buf.append(" gateway=").append(nic.getIPv4Gateway()); } if (ipv6) { - defaultIp6Dns1 = nic.getIPv6Dns1() != null? nic.getIPv6Dns1() : dc.getIp6Dns1(); - defaultIp6Dns2 = nic.getIPv6Dns2() != null? nic.getIPv6Dns2() : dc.getIp6Dns2(); + defaultIp6Dns1 = nic.getIPv6Dns1(); + defaultIp6Dns2 = nic.getIPv6Dns2(); buf.append(" ip6gateway=").append(nic.getIPv6Gateway()); } defaultDns1 = nic.getIPv4Dns1(); diff --git a/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java b/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java index 1d1b9494d763..ad20fb9a3a1c 100644 --- a/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java +++ b/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java @@ -252,6 +252,8 @@ public boolean finalizeVirtualMachineProfile(final VirtualMachineProfile profile if (domainRouterVO.getState() == State.Starting || domainRouterVO.getState() == State.Running) { String defaultDns1 = null; String defaultDns2 = null; + String defaultIp6Dns1 = null; + String defaultIp6Dns2 = null; // remove public and guest nics as we will plug them later final Iterator it = profile.getNics().iterator(); while (it.hasNext()) { @@ -261,6 +263,8 @@ public boolean finalizeVirtualMachineProfile(final VirtualMachineProfile profile if (nic.getTrafficType() == TrafficType.Public) { defaultDns1 = nic.getIPv4Dns1(); defaultDns2 = nic.getIPv4Dns2(); + defaultIp6Dns1 = nic.getIPv6Dns1(); + defaultIp6Dns2 = nic.getIPv6Dns2(); } s_logger.debug("Removing nic " + nic + " of type " + nic.getTrafficType() + " from the nics passed on vm start. " + "The nic will be plugged later"); it.remove(); @@ -276,6 +280,12 @@ public boolean finalizeVirtualMachineProfile(final VirtualMachineProfile profile if (defaultDns2 != null) { buf.append(" dns2=").append(defaultDns2); } + if (defaultIp6Dns1 != null) { + buf.append(" ip6dns1=").append(defaultIp6Dns1); + } + if (defaultIp6Dns2 != null) { + buf.append(" ip6dns2=").append(defaultIp6Dns2); + } } } diff --git a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java index 781ee38c8a40..1c4911d9d365 100644 --- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java @@ -47,6 +47,7 @@ import org.apache.cloudstack.api.command.admin.vpc.CreateVPCOfferingCmd; import org.apache.cloudstack.api.command.admin.vpc.UpdateVPCOfferingCmd; 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.ListVPCOfferingsCmd; @@ -57,6 +58,7 @@ import org.apache.cloudstack.managed.context.ManagedContextRunnable; import org.apache.cloudstack.query.QueryService; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.log4j.Logger; import com.cloud.api.query.dao.VpcOfferingJoinDao; @@ -261,6 +263,17 @@ protected void setupSupportedVpcHypervisorsList() { hTypes.add(HypervisorType.Ovm3); } + private void checkVpcDns(VpcOffering vpcOffering, String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2) { + if (ObjectUtils.anyNotNull(ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2) && !areServicesSupportedByVpcOffering(vpcOffering.getId(), Service.Dns)) { + throw new InvalidParameterValueException("DNS can not be specified for VPCs with offering that do not support DNS service"); + } + if (!_vpcOffDao.isIpv6Supported(vpcOffering.getId()) && !org.apache.commons.lang3.StringUtils.isAllBlank(ip6Dns1, ip6Dns2)) { + throw new InvalidParameterValueException("IPv6 DNS can be specified for IPv6 enabled VPC"); + } + _ntwkModel.verifyIp4DnsPair(ip4Dns1, ip4Dns2); + _ntwkModel.verifyIp6DnsPair(ip6Dns1, ip6Dns2); + } + @Override @DB public boolean configure(final String name, final Map params) throws ConfigurationException { @@ -788,7 +801,7 @@ public Pair, Integer> listVpcOfferings(ListVPCOfferi } 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 List getVpcOfferingZones(Long vpcOfferingId) { @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 Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwner // 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 Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwner 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 Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwner } } + 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 boolean isIpAllocatedToVpc(final IpAddress ip) { @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 Network createVpcGuestNetwork(final long ntwkOffId, final String name, fi // 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 9d56c339eed3..75cee8422462 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -3732,7 +3732,7 @@ private NetworkVO createDefaultNetworkForAccount(DataCenter zone, Account owner, 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()); } @@ -7257,7 +7257,7 @@ public void doInTransactionWithoutResult(TransactionStatus status) { 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 7a0ea2e97218..93a1d30d1789 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 void setup() throws Exception { 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 bd1484d588a3..17676956a82b 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 @@ 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.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.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 void setNICIPv6AddressTest() throws InsufficientAddressCapacityException 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 1b0592abcb2b..3b55717d209a 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 void testIpv4NetworkUpdateNicIpv6() { @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 void testIpv6NetworkUpdateNicIpv6() { @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 e9c9db3cba22..6f437b8be563 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 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.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 String getValidNetworkCidr(Network guestNetwork) { 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 000000000000..76d049b8868a --- /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 2520f3902ba3..30def99f1695 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 void testPerformBasicChecksPromiscuousTypeExpectedDifferentVlanIds() { 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 750bee49c8a0..ce5aabb6c13a 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.Network.GuestType; import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkProfile; import com.cloud.network.Networks.TrafficType; -import com.cloud.network.Network.GuestType; 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 void testCanDesign() { 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 000000000000..5a3db4819f91 --- /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 10b59fde8b11..9109bed82983 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.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.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 void testDisabledConfigCreateIpv6VpcOffering() { 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 09f00b7925fd..f47386491077 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 Network createPrivateNetwork(final long networkOfferingId, final String n @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 83ffa0347618..ccb78e3b0277 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 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.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 String getValidNetworkCidr(Network guestNetwork) { 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 8899a045329e..66e6d65834d7 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 @@ 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 f20de036f4c6..6322a1d169e9 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 void persistVpcServiceProviders(long vpcId, Map> ser 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 ba57212e5a03..59437bf8cb18 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 List listByVpcOffId(long vpcOffId) { * @see com.cloud.network.vpc.Dao.VpcOfferingServiceMapDao#areServicesSupportedByNetworkOffering(long, com.cloud.network.Network.Service[]) */ @Override - public boolean areServicesSupportedByNetworkOffering(long networkOfferingId, Service[] services) { + public boolean areServicesSupportedByVpcOffering(long vpcOfferingId, Service[] services) { // TODO Auto-generated method stub return false; } diff --git a/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh b/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh index bfb062188254..4eeb19bae6b8 100755 --- a/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh +++ b/systemvm/debian/opt/cloud/bin/setup/vpcrouter.sh @@ -59,6 +59,18 @@ EOF echo "nameserver $NS2" >> /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 000000000000..bb44d79697bc --- /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 00d212b139ff..c6a595810b01 100755 --- a/tools/marvin/marvin/lib/base.py +++ b/tools/marvin/marvin/lib/base.py @@ -3240,6 +3240,14 @@ def create(cls, apiclient, services, accountid=None, domainid=None, 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 94e7ffeabc13..b83087396dcb 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 90df22be8a76..ed67ce566b6f 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() @@ -458,29 +514,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 fdcb890fd07b..dd125214ec99 100644 --- a/ui/src/views/network/CreateSharedNetworkForm.vue +++ b/ui/src/views/network/CreateSharedNetworkForm.vue @@ -85,12 +85,12 @@ - + @@ -265,86 +265,158 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +