diff --git a/api/src/main/java/com/cloud/agent/api/to/NicTO.java b/api/src/main/java/com/cloud/agent/api/to/NicTO.java index 3a616170d7e0..573363c04fb1 100644 --- a/api/src/main/java/com/cloud/agent/api/to/NicTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/NicTO.java @@ -32,6 +32,9 @@ public class NicTO extends NetworkTO { Map details; boolean dpdkEnabled; Integer mtu; + Long networkId; + + String networkSegmentName; public NicTO() { super(); @@ -127,4 +130,20 @@ public Integer getMtu() { public void setMtu(Integer mtu) { this.mtu = mtu; } + + public Long getNetworkId() { + return networkId; + } + + public void setNetworkId(Long networkId) { + this.networkId = networkId; + } + + public String getNetworkSegmentName() { + return networkSegmentName; + } + + public void setNetworkSegmentName(String networkSegmentName) { + this.networkSegmentName = networkSegmentName; + } } diff --git a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java index db6cba775483..b4f4619be9ac 100644 --- a/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java +++ b/api/src/main/java/com/cloud/agent/api/to/VirtualMachineTO.java @@ -82,6 +82,7 @@ public class VirtualMachineTO { Map guestOsDetails = new HashMap(); Map extraConfig = new HashMap<>(); + Map networkIdToNetworkNameMap = new HashMap<>(); DeployAsIsInfoTO deployAsIsInfo; public VirtualMachineTO(long id, String instanceName, VirtualMachine.Type type, int cpus, Integer speed, long minRam, long maxRam, BootloaderType bootloader, @@ -392,6 +393,14 @@ public Map getExtraConfig() { return extraConfig; } + public Map getNetworkIdToNetworkNameMap() { + return networkIdToNetworkNameMap; + } + + public void setNetworkIdToNetworkNameMap(Map networkIdToNetworkNameMap) { + this.networkIdToNetworkNameMap = networkIdToNetworkNameMap; + } + public String getBootType() { return bootType; } diff --git a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java index e445e50f82cb..e160227749db 100644 --- a/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java +++ b/api/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelper.java @@ -22,4 +22,5 @@ public interface KubernetesClusterHelper extends Adapter { ControlledEntity findByUuid(String uuid); + ControlledEntity findByVmId(long vmId); } diff --git a/api/src/main/java/com/cloud/network/IpAddress.java b/api/src/main/java/com/cloud/network/IpAddress.java index cf2e2f82db9f..ae1af4505773 100644 --- a/api/src/main/java/com/cloud/network/IpAddress.java +++ b/api/src/main/java/com/cloud/network/IpAddress.java @@ -97,4 +97,6 @@ enum Purpose { void setRuleState(State ruleState); + boolean isForSystemVms(); + } diff --git a/api/src/main/java/com/cloud/network/Network.java b/api/src/main/java/com/cloud/network/Network.java index 458169c80aa3..3b13ef7bd9cc 100644 --- a/api/src/main/java/com/cloud/network/Network.java +++ b/api/src/main/java/com/cloud/network/Network.java @@ -205,6 +205,8 @@ public static class Provider { //Add Tungsten Fabric provider public static final Provider Tungsten = new Provider("Tungsten", false); + public static final Provider Nsx = new Provider("Nsx", false); + private final String name; private final boolean isExternal; @@ -427,6 +429,8 @@ public void setIp6Address(String ip6Address) { long getDataCenterId(); + long getAccountId(); + long getNetworkOfferingId(); @Override diff --git a/api/src/main/java/com/cloud/network/NetworkService.java b/api/src/main/java/com/cloud/network/NetworkService.java index 82d229da459d..51799e25cda6 100644 --- a/api/src/main/java/com/cloud/network/NetworkService.java +++ b/api/src/main/java/com/cloud/network/NetworkService.java @@ -19,6 +19,7 @@ import java.util.List; import java.util.Map; +import com.cloud.dc.DataCenter; import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin; import org.apache.cloudstack.api.command.admin.network.DedicateGuestVlanRangeCmd; import org.apache.cloudstack.api.command.admin.network.ListDedicatedGuestVlanRangesCmd; @@ -55,6 +56,7 @@ import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.vm.Nic; import com.cloud.vm.NicSecondaryIp; +import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; /** * The NetworkService interface is the "public" api to entities that make requests to the orchestration engine @@ -87,6 +89,8 @@ IpAddress allocateIP(Account ipOwner, long zoneId, Long networkId, Boolean displ IpAddress reserveIpAddress(Account account, Boolean displayIp, Long ipAddressId) throws ResourceAllocationException; + IpAddress reserveIpAddressWithVlanDetail(Account account, DataCenter zone, Boolean displayIp, String vlanDetailKey) throws ResourceAllocationException; + boolean releaseReservedIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; boolean releaseIpAddress(long ipAddressId) throws InsufficientAddressCapacityException; @@ -254,4 +258,9 @@ Network createPrivateNetwork(String networkName, String displayText, long physic PublicIpQuarantine updatePublicIpAddressInQuarantine(UpdateQuarantinedIpCmd cmd); void removePublicIpAddressFromQuarantine(RemoveQuarantinedIpCmd cmd); + + InternalLoadBalancerElementService getInternalLoadBalancerElementByType(VirtualRouterProvider.Type type); + InternalLoadBalancerElementService getInternalLoadBalancerElementByNetworkServiceProviderId(long networkProviderId); + InternalLoadBalancerElementService getInternalLoadBalancerElementById(long providerId); + List getInternalLoadBalancerElements(); } diff --git a/api/src/main/java/com/cloud/network/Networks.java b/api/src/main/java/com/cloud/network/Networks.java index aeed5d4aec6a..dfa0ddb84cae 100644 --- a/api/src/main/java/com/cloud/network/Networks.java +++ b/api/src/main/java/com/cloud/network/Networks.java @@ -128,7 +128,8 @@ public URI toUri(T value) { }, UnDecided(null, null), OpenDaylight("opendaylight", String.class), - TUNGSTEN("tf", String.class); + TUNGSTEN("tf", String.class), + NSX("nsx", String.class); private final String scheme; private final Class type; diff --git a/api/src/main/java/com/cloud/network/VirtualRouterProvider.java b/api/src/main/java/com/cloud/network/VirtualRouterProvider.java index aca526b1832b..98410ca09f88 100644 --- a/api/src/main/java/com/cloud/network/VirtualRouterProvider.java +++ b/api/src/main/java/com/cloud/network/VirtualRouterProvider.java @@ -21,7 +21,7 @@ public interface VirtualRouterProvider extends InternalIdentity, Identity { public enum Type { - VirtualRouter, ElasticLoadBalancerVm, VPCVirtualRouter, InternalLbVm, NetScalerVm + VirtualRouter, ElasticLoadBalancerVm, VPCVirtualRouter, InternalLbVm, NetScalerVm, Nsx } public Type getType(); diff --git a/api/src/main/java/com/cloud/network/element/NetworkACLServiceProvider.java b/api/src/main/java/com/cloud/network/element/NetworkACLServiceProvider.java index 8c3243c99f4b..852a650cfcde 100644 --- a/api/src/main/java/com/cloud/network/element/NetworkACLServiceProvider.java +++ b/api/src/main/java/com/cloud/network/element/NetworkACLServiceProvider.java @@ -21,6 +21,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.network.Network; import com.cloud.network.vpc.NetworkACLItem; +import com.cloud.network.vpc.Vpc; public interface NetworkACLServiceProvider extends NetworkElement { @@ -32,4 +33,6 @@ public interface NetworkACLServiceProvider extends NetworkElement { */ boolean applyNetworkACLs(Network config, List rules) throws ResourceUnavailableException; + boolean reorderAclRules(Vpc vpc, List networks, List networkACLItems); + } diff --git a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java index 52f654007f28..cbadbb18a8f1 100644 --- a/api/src/main/java/com/cloud/network/guru/NetworkGuru.java +++ b/api/src/main/java/com/cloud/network/guru/NetworkGuru.java @@ -79,20 +79,24 @@ public interface NetworkGuru extends Adapter { * be used to make determination can be isolation methods, services * provided on the guest network and the service provider that's on the * guest network. - * + *

* If a network is already fully substantiated with the necessary resources * during this design phase, then the state should be set to Setup. If * the resources are not allocated at this point, the state should be set * to Allocated. * - * @param offering network offering that contains the package of services - * the end user intends to use on that network. - * @param plan where is this network being deployed. + * @param offering network offering that contains the package of services + * the end user intends to use on that network. + * @param plan where is this network being deployed. * @param userSpecified user specified parameters for this network. - * @param owner owner of this network. + * @param name + * @param vpcId + * @param owner owner of this network. * @return Network */ - Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner); + Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner); + + void setup(Network network, long networkId); /** * For guest networks that are in Allocated state after the design stage, diff --git a/api/src/main/java/com/cloud/network/nsx/NsxProvider.java b/api/src/main/java/com/cloud/network/nsx/NsxProvider.java new file mode 100644 index 000000000000..19cb3b4b939e --- /dev/null +++ b/api/src/main/java/com/cloud/network/nsx/NsxProvider.java @@ -0,0 +1,34 @@ +// 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.nsx; + +import org.apache.cloudstack.api.Identity; +import org.apache.cloudstack.api.InternalIdentity; + +public interface NsxProvider extends InternalIdentity, Identity { + String getHostname(); + + String getPort(); + String getProviderName(); + String getUsername(); + long getZoneId(); + + String getTier0Gateway(); + String getEdgeCluster(); + + String getTransportZone(); +} diff --git a/api/src/main/java/com/cloud/network/nsx/NsxService.java b/api/src/main/java/com/cloud/network/nsx/NsxService.java new file mode 100644 index 000000000000..79ad9547c736 --- /dev/null +++ b/api/src/main/java/com/cloud/network/nsx/NsxService.java @@ -0,0 +1,26 @@ +// 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.nsx; + +import com.cloud.network.IpAddress; +import com.cloud.network.vpc.Vpc; + +public interface NsxService { + + boolean createVpcNetwork(Long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled); + boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address); +} diff --git a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java index b4df8e38dbac..3aab57d5d3d2 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcOffering.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcOffering.java @@ -29,6 +29,8 @@ public enum State { public static final String defaultVPCOfferingName = "Default VPC offering"; public static final String defaultVPCNSOfferingName = "Default VPC offering with Netscaler"; public static final String redundantVPCOfferingName = "Redundant VPC offering"; + public static final String DEFAULT_VPC_NAT_NSX_OFFERING_NAME = "VPC offering with NSX - NAT Mode"; + public static final String DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME = "VPC offering with NSX - Route Mode"; /** * @@ -53,6 +55,10 @@ public enum State { */ boolean isDefault(); + boolean isForNsx(); + + String getNsxMode(); + /** * @return service offering id used by VPC virtual router */ diff --git a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java index 5cccd6c5a823..1ce3cf8ab0e9 100644 --- a/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java +++ b/api/src/main/java/com/cloud/network/vpc/VpcProvisioningService.java @@ -36,7 +36,8 @@ public interface VpcProvisioningService { VpcOffering createVpcOffering(String name, String displayText, List supportedServices, Map> serviceProviders, Map serviceCapabilitystList, NetUtils.InternetProtocol internetProtocol, - Long serviceOfferingId, List domainIds, List zoneIds, VpcOffering.State state); + Long serviceOfferingId, Boolean forNsx, String mode, + List domainIds, List zoneIds, VpcOffering.State state); Pair,Integer> listVpcOfferings(ListVPCOfferingsCmd cmd); diff --git a/api/src/main/java/com/cloud/offering/NetworkOffering.java b/api/src/main/java/com/cloud/offering/NetworkOffering.java index 207880ea28c0..cf01fbf30e20 100644 --- a/api/src/main/java/com/cloud/offering/NetworkOffering.java +++ b/api/src/main/java/com/cloud/offering/NetworkOffering.java @@ -43,6 +43,11 @@ public enum Detail { InternalLbProvider, PublicLbProvider, servicepackageuuid, servicepackagedescription, PromiscuousMode, MacAddressChanges, ForgedTransmits, MacLearning, RelatedNetworkOffering, domainid, zoneid, pvlanType, internetProtocol } + public enum NsxMode { + NATTED, + ROUTED + } + public final static String SystemPublicNetwork = "System-Public-Network"; public final static String SystemControlNetwork = "System-Control-Network"; public final static String SystemManagementNetwork = "System-Management-Network"; @@ -52,6 +57,11 @@ public enum Detail { public final static String DefaultSharedNetworkOfferingWithSGService = "DefaultSharedNetworkOfferingWithSGService"; public static final String DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE = "DefaultTungstenSharedNetworkOfferingWithSGService"; + public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC = "DefaultNATNSXNetworkOfferingForVpc"; + public static final String DEFAULT_NAT_NSX_OFFERING_FOR_VPC_WITH_ILB = "DefaultNATNSXNetworkOfferingForVpcWithInternalLB"; + public static final String DEFAULT_ROUTED_NSX_OFFERING_FOR_VPC = "DefaultRoutedNSXNetworkOfferingForVpc"; + public static final String DEFAULT_NAT_NSX_OFFERING = "DefaultNATNSXNetworkOffering"; + public static final String DEFAULT_ROUTED_NSX_OFFERING = "DefaultRoutedNSXNetworkOffering"; public final static String QuickCloudNoServices = "QuickCloudNoServices"; public final static String DefaultIsolatedNetworkOfferingWithSourceNatService = "DefaultIsolatedNetworkOfferingWithSourceNatService"; public final static String OvsIsolatedNetworkOfferingWithSourceNatService = "OvsIsolatedNetworkOfferingWithSourceNatService"; @@ -90,6 +100,10 @@ public enum Detail { boolean isForTungsten(); + boolean isForNsx(); + + String getNsxMode(); + TrafficType getTrafficType(); boolean isSpecifyVlan(); diff --git a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java index 416072f1210a..f10769d73190 100644 --- a/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/main/java/org/apache/cloudstack/api/ApiConstants.java @@ -303,6 +303,8 @@ public class ApiConstants { public static final String MIGRATIONS = "migrations"; public static final String MEMORY = "memory"; public static final String MODE = "mode"; + public static final String NSX_MODE = "nsxmode"; + public static final String NSX_ENABLED = "isnsxenabled"; public static final String NAME = "name"; public static final String METHOD_NAME = "methodname"; public static final String NETWORK_DOMAIN = "networkdomain"; @@ -704,6 +706,12 @@ public class ApiConstants { public static final String VSWITCH_TYPE_PUBLIC_TRAFFIC = "publicvswitchtype"; public static final String VSWITCH_NAME_GUEST_TRAFFIC = "guestvswitchname"; public static final String VSWITCH_NAME_PUBLIC_TRAFFIC = "publicvswitchname"; + + // NSX + public static final String EDGE_CLUSTER = "edgecluster"; + public static final String TIER0_GATEWAY = "tier0gateway"; + + public static final String TRANSPORT_ZONE = "transportzone"; // Tungsten-Fabric public static final String TUNGSTEN_VIRTUAL_ROUTER_UUID = "tungstenvirtualrouteruuid"; public static final String TUNGSTEN_PROVIDER_HOSTNAME = "tungstenproviderhostname"; @@ -825,6 +833,9 @@ public class ApiConstants { public static final String FORCE_ENCAP = "forceencap"; public static final String SPLIT_CONNECTIONS = "splitconnections"; public static final String FOR_VPC = "forvpc"; + public static final String FOR_NSX = "fornsx"; + public static final String NSX_SUPPORT_LB = "nsxsupportlb"; + public static final String NSX_SUPPORTS_INTERNAL_LB = "nsxsupportsinternallb"; public static final String FOR_TUNGSTEN = "fortungsten"; public static final String SHRINK_OK = "shrinkok"; public static final String NICIRA_NVP_DEVICE_ID = "nvpdeviceid"; @@ -834,6 +845,11 @@ public class ApiConstants { public static final String NICIRA_NVP_L2_GATEWAYSERVICE_UUID = "l2gatewayserviceuuid"; public static final String NSX_LOGICAL_SWITCH = "nsxlogicalswitch"; public static final String NSX_LOGICAL_SWITCH_PORT = "nsxlogicalswitchport"; + public static final String NSX_PROVIDER_UUID = "nsxprovideruuid"; + public static final String NSX_PROVIDER_HOSTNAME = "nsxproviderhostname"; + + public static final String NSX_PROVIDER_PORT = "nsxproviderport"; + public static final String NSX_CONTROLLER_ID = "nsxcontrollerid"; public static final String S3_ACCESS_KEY = "accesskey"; public static final String S3_SECRET_KEY = "secretkey"; public static final String S3_END_POINT = "endpoint"; @@ -958,6 +974,7 @@ public class ApiConstants { public static final String SUPPORTS_REGION_LEVEL_VPC = "supportsregionLevelvpc"; public static final String SUPPORTS_STRECHED_L2_SUBNET = "supportsstrechedl2subnet"; public static final String SUPPORTS_PUBLIC_ACCESS = "supportspublicaccess"; + public static final String SUPPORTS_INTERNAL_LB = "supportsinternallb"; public static final String SUPPORTS_VM_AUTOSCALING = "supportsvmautoscaling"; public static final String REGION_LEVEL_VPC = "regionlevelvpc"; public static final String STRECHED_L2_SUBNET = "strechedl2subnet"; @@ -1076,14 +1093,13 @@ public class ApiConstants { public static final String SOURCE_NAT_IP = "sourcenatipaddress"; public static final String SOURCE_NAT_IP_ID = "sourcenatipaddressid"; public static final String HAS_RULES = "hasrules"; + public static final String NSX_DETAIL_KEY = "forNsx"; public static final String DISK_PATH = "diskpath"; public static final String IMPORT_SOURCE = "importsource"; public static final String TEMP_PATH = "temppath"; public static final String OBJECT_STORAGE = "objectstore"; - public static final String HEURISTIC_RULE = "heuristicrule"; public static final String HEURISTIC_TYPE_VALID_OPTIONS = "Valid options are: ISO, SNAPSHOT, TEMPLATE and VOLUME."; - public static final String MANAGEMENT = "management"; public static final String IS_VNF = "isvnf"; public static final String VNF_NICS = "vnfnics"; diff --git a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java index 323fd4e6f64d..b206cd011c1d 100644 --- a/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/BaseCmd.java @@ -38,7 +38,6 @@ import org.apache.cloudstack.alert.AlertService; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.context.CallContext; -import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; import org.apache.cloudstack.network.lb.ApplicationLoadBalancerService; import org.apache.cloudstack.network.lb.InternalLoadBalancerVMService; import org.apache.cloudstack.query.QueryService; @@ -201,8 +200,6 @@ public static enum CommandType { @Inject public AffinityGroupService _affinityGroupService; @Inject - public InternalLoadBalancerElementService _internalLbElementSvc; - @Inject public InternalLoadBalancerVMService _internalLbSvc; @Inject public NetworkModel _ntwkModel; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java index 18dfc87397ae..c94d326ee622 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ConfigureInternalLoadBalancerElementCmd.java @@ -17,11 +17,6 @@ package org.apache.cloudstack.api.command.admin.internallb; -import java.util.List; - -import javax.inject.Inject; - - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -47,9 +42,6 @@ responseHasSensitiveInfo = false) public class ConfigureInternalLoadBalancerElementCmd extends BaseAsyncCmd { - @Inject - private List _service; - ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -98,7 +90,8 @@ public String getEventDescription() { @Override public void execute() throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { CallContext.current().setEventDetails("Internal load balancer element: " + id); - VirtualRouterProvider result = _service.get(0).configureInternalLoadBalancerElement(getId(), getEnabled()); + InternalLoadBalancerElementService service = _networkService.getInternalLoadBalancerElementById(id); + VirtualRouterProvider result = service.configureInternalLoadBalancerElement(getId(), getEnabled()); if (result != null) { InternalLoadBalancerElementResponse routerResponse = _responseGenerator.createInternalLbElementResponse(result); routerResponse.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/CreateInternalLoadBalancerElementCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/CreateInternalLoadBalancerElementCmd.java index 971f097fca52..924287b673ba 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/CreateInternalLoadBalancerElementCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/CreateInternalLoadBalancerElementCmd.java @@ -16,11 +16,6 @@ // under the License. package org.apache.cloudstack.api.command.admin.internallb; -import java.util.List; - -import javax.inject.Inject; - - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.ApiErrorCode; @@ -45,9 +40,6 @@ responseHasSensitiveInfo = false) public class CreateInternalLoadBalancerElementCmd extends BaseAsyncCreateCmd { - @Inject - private List _service; - ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -83,7 +75,8 @@ public long getEntityOwnerId() { @Override public void execute() { CallContext.current().setEventDetails("Virtual router element Id: " + getEntityId()); - VirtualRouterProvider result = _service.get(0).getInternalLoadBalancerElement(getEntityId()); + InternalLoadBalancerElementService service = _networkService.getInternalLoadBalancerElementByNetworkServiceProviderId(getNspId()); + VirtualRouterProvider result = service.getInternalLoadBalancerElement(getEntityId()); if (result != null) { InternalLoadBalancerElementResponse response = _responseGenerator.createInternalLbElementResponse(result); response.setResponseName(getCommandName()); @@ -95,7 +88,8 @@ public void execute() { @Override public void create() throws ResourceAllocationException { - VirtualRouterProvider result = _service.get(0).addInternalLoadBalancerElement(getNspId()); + InternalLoadBalancerElementService service = _networkService.getInternalLoadBalancerElementByNetworkServiceProviderId(getNspId()); + VirtualRouterProvider result = service.addInternalLoadBalancerElement(getNspId()); if (result != null) { setEntityId(result.getId()); setEntityUuid(result.getUuid()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLoadBalancerElementsCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLoadBalancerElementsCmd.java index 6c2fadee7370..b17cc22e7466 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLoadBalancerElementsCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/internallb/ListInternalLoadBalancerElementsCmd.java @@ -17,11 +17,9 @@ package org.apache.cloudstack.api.command.admin.internallb; import java.util.ArrayList; +import java.util.Collections; import java.util.List; -import javax.inject.Inject; - - import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseListCmd; @@ -46,9 +44,6 @@ responseHasSensitiveInfo = false) public class ListInternalLoadBalancerElementsCmd extends BaseListCmd { - @Inject - private InternalLoadBalancerElementService _service; - ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// @@ -86,12 +81,21 @@ public Boolean getEnabled() { @Override public void execute() throws ResourceUnavailableException, InsufficientCapacityException, ServerApiException, ConcurrentOperationException, ResourceAllocationException { - List providers = _service.searchForInternalLoadBalancerElements(getId(), getNspId(), getEnabled()); + List services; + if (id == null && nspId == null) { + services = _networkService.getInternalLoadBalancerElements(); + } else { + InternalLoadBalancerElementService elementService = id != null ? _networkService.getInternalLoadBalancerElementById(id) : _networkService.getInternalLoadBalancerElementByNetworkServiceProviderId(nspId); + services = Collections.singletonList(elementService); + } ListResponse response = new ListResponse(); List providerResponses = new ArrayList(); - for (VirtualRouterProvider provider : providers) { - InternalLoadBalancerElementResponse providerResponse = _responseGenerator.createInternalLbElementResponse(provider); - providerResponses.add(providerResponse); + for (InternalLoadBalancerElementService service : services) { + List providers = service.searchForInternalLoadBalancerElements(getId(), getNspId(), getEnabled()); + for (VirtualRouterProvider provider : providers) { + InternalLoadBalancerElementResponse providerResponse = _responseGenerator.createInternalLbElementResponse(provider); + providerResponses.add(providerResponse); + } } response.setResponses(providerResponses); response.setResponseName(getCommandName()); diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java index f2b1a18831a0..9117bcfc193a 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/network/CreateNetworkOfferingCmd.java @@ -24,10 +24,14 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import com.cloud.network.Network; +import com.cloud.network.VirtualRouterProvider; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.cloudstack.api.APICommand; @@ -46,6 +50,16 @@ import com.cloud.offering.NetworkOffering.Availability; import com.cloud.user.Account; +import static com.cloud.network.Network.Service.Dhcp; +import static com.cloud.network.Network.Service.Dns; +import static com.cloud.network.Network.Service.Lb; +import static com.cloud.network.Network.Service.StaticNat; +import static com.cloud.network.Network.Service.SourceNat; +import static com.cloud.network.Network.Service.PortForwarding; +import static com.cloud.network.Network.Service.NetworkACL; +import static com.cloud.network.Network.Service.UserData; +import static com.cloud.network.Network.Service.Firewall; + @APICommand(name = "createNetworkOffering", description = "Creates a network offering.", responseObject = NetworkOfferingResponse.class, since = "3.0.0", requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateNetworkOfferingCmd extends BaseCmd { @@ -126,6 +140,30 @@ public class CreateNetworkOfferingCmd extends BaseCmd { description = "true if network offering is meant to be used for VPC, false otherwise.") private Boolean forVpc; + @Parameter(name = ApiConstants.FOR_NSX, + type = CommandType.BOOLEAN, + description = "true if network offering is meant to be used for NSX, false otherwise.", + since = "4.20.0") + private Boolean forNsx; + + @Parameter(name = ApiConstants.NSX_MODE, + type = CommandType.STRING, + description = "Indicates the mode with which the network will operate. Valid option: NATTED or ROUTED", + since = "4.20.0") + private String nsxMode; + + @Parameter(name = ApiConstants.NSX_SUPPORT_LB, + type = CommandType.BOOLEAN, + description = "true if network offering for NSX network offering supports Load balancer service.", + since = "4.20.0") + private Boolean nsxSupportsLbService; + + @Parameter(name = ApiConstants.NSX_SUPPORTS_INTERNAL_LB, + type = CommandType.BOOLEAN, + description = "true if network offering for NSX network offering supports Internal Load balancer service.", + since = "4.20.0") + private Boolean nsxSupportsInternalLbService; + @Parameter(name = ApiConstants.FOR_TUNGSTEN, type = CommandType.BOOLEAN, description = "true if network offering is meant to be used for Tungsten-Fabric, false otherwise.") @@ -210,7 +248,27 @@ public Long getServiceOfferingId() { } public List getSupportedServices() { - return supportedServices == null ? new ArrayList() : supportedServices; + if (!isForNsx()) { + return supportedServices == null ? new ArrayList() : supportedServices; + } else { + List services = new ArrayList<>(List.of( + Dhcp.getName(), + Dns.getName(), + StaticNat.getName(), + SourceNat.getName(), + PortForwarding.getName(), + UserData.getName() + )); + if (getNsxSupportsLbService()) { + services.add(Lb.getName()); + } + if (Boolean.TRUE.equals(forVpc)) { + services.add(NetworkACL.getName()); + } else { + services.add(Firewall.getName()); + } + return services; + } } public String getGuestIpType() { @@ -240,6 +298,22 @@ public Boolean getForVpc() { return forVpc; } + public boolean isForNsx() { + return BooleanUtils.isTrue(forNsx); + } + + public String getNsxMode() { + return nsxMode; + } + + public boolean getNsxSupportsLbService() { + return BooleanUtils.isTrue(nsxSupportsLbService); + } + + public boolean getNsxSupportsInternalLbService() { + return BooleanUtils.isTrue(nsxSupportsInternalLbService); + } + public Boolean getForTungsten() { return forTungsten; } @@ -260,9 +334,8 @@ public Integer getMaxconnections() { } public Map> getServiceProviders() { - Map> serviceProviderMap = null; - if (serviceProviderList != null && !serviceProviderList.isEmpty()) { - serviceProviderMap = new HashMap>(); + Map> serviceProviderMap = new HashMap<>(); + if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isForNsx()) { Collection servicesCollection = serviceProviderList.values(); Iterator iter = servicesCollection.iterator(); while (iter.hasNext()) { @@ -278,11 +351,37 @@ public Map> getServiceProviders() { providerList.add(provider); serviceProviderMap.put(service, providerList); } + } else if (Boolean.TRUE.equals(forNsx)) { + getServiceProviderMapForNsx(serviceProviderMap); } - return serviceProviderMap; } + private void getServiceProviderMapForNsx(Map> serviceProviderMap) { + String routerProvider = Boolean.TRUE.equals(getForVpc()) ? VirtualRouterProvider.Type.VPCVirtualRouter.name() : + VirtualRouterProvider.Type.VirtualRouter.name(); + List unsupportedServices = new ArrayList<>(List.of("Vpn", "SecurityGroup", "Connectivity", + "Gateway", "BaremetalPxeService")); + List routerSupported = List.of("Dhcp", "Dns", "UserData"); + List allServices = Service.listAllServices().stream().map(Service::getName).collect(Collectors.toList()); + if (routerProvider.equals(VirtualRouterProvider.Type.VPCVirtualRouter.name())) { + unsupportedServices.add("Firewall"); + } else { + unsupportedServices.add("NetworkACL"); + } + for (String service : allServices) { + if (unsupportedServices.contains(service)) + continue; + if (routerSupported.contains(service)) + serviceProviderMap.put(service, List.of(routerProvider)); + else + serviceProviderMap.put(service, List.of(Network.Provider.Nsx.getName())); + if (!getNsxSupportsLbService()) { + serviceProviderMap.remove(Lb.getName()); + } + } + } + public Map getServiceCapabilities(Service service) { Map capabilityMap = null; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java index cceadea85322..c0ba99a82333 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vlan/CreateVlanIpRangeCmd.java @@ -39,6 +39,8 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.user.Account; +import java.util.Objects; + @APICommand(name = "createVlanIpRange", description = "Creates a VLAN IP range.", responseObject = VlanIpRangeResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateVlanIpRangeCmd extends BaseCmd { @@ -112,6 +114,9 @@ public class CreateVlanIpRangeCmd extends BaseCmd { @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if IP range is set to system vms, false if not") private Boolean forSystemVms; + @Parameter(name = ApiConstants.FOR_NSX, type = CommandType.BOOLEAN, description = "true if the IP range is used for NSX resource", since = "4.20.0") + private boolean forNsx; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -152,8 +157,12 @@ public String getStartIp() { return startIp; } + public boolean isForNsx() { + return !Objects.isNull(forNsx) && forNsx; + } + public String getVlan() { - if (vlan == null || vlan.isEmpty()) { + if ((vlan == null || vlan.isEmpty()) && !isForNsx()) { vlan = "untagged"; } return vlan; diff --git a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java index 382c081e01e2..dd5c815238e1 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmd.java @@ -24,10 +24,15 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.Network; +import com.cloud.network.VirtualRouterProvider; import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.ZoneResponse; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang.BooleanUtils; import org.apache.commons.lang3.StringUtils; import org.apache.cloudstack.api.APICommand; @@ -44,6 +49,15 @@ import com.cloud.network.vpc.VpcOffering; import com.cloud.user.Account; +import static com.cloud.network.Network.Service.Dhcp; +import static com.cloud.network.Network.Service.Dns; +import static com.cloud.network.Network.Service.Lb; +import static com.cloud.network.Network.Service.StaticNat; +import static com.cloud.network.Network.Service.SourceNat; +import static com.cloud.network.Network.Service.PortForwarding; +import static com.cloud.network.Network.Service.NetworkACL; +import static com.cloud.network.Network.Service.UserData; + @APICommand(name = "createVPCOffering", description = "Creates VPC offering", responseObject = VpcOfferingResponse.class, requestHasSensitiveInfo = false, responseHasSensitiveInfo = false) public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { @@ -60,7 +74,6 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { @Parameter(name = ApiConstants.SUPPORTED_SERVICES, type = CommandType.LIST, - required = true, collectionType = CommandType.STRING, description = "services supported by the vpc offering") private List supportedServices; @@ -99,6 +112,24 @@ public class CreateVPCOfferingCmd extends BaseAsyncCreateCmd { since = "4.13") private List zoneIds; + @Parameter(name = ApiConstants.FOR_NSX, + type = CommandType.BOOLEAN, + description = "true if network offering is meant to be used for NSX, false otherwise.", + since = "4.20.0") + private Boolean forNsx; + + @Parameter(name = ApiConstants.NSX_MODE, + type = CommandType.STRING, + description = "Indicates the mode with which the network will operate. Valid option: NATTED or ROUTED", + since = "4.20.0") + private String nsxMode; + + @Parameter(name = ApiConstants.NSX_SUPPORT_LB, + type = CommandType.BOOLEAN, + description = "true if network offering for NSX VPC offering supports Load balancer service.", + since = "4.20.0") + private Boolean nsxSupportsLbService; + @Parameter(name = ApiConstants.ENABLE, type = CommandType.BOOLEAN, description = "set to true if the offering is to be enabled during creation. Default is false", @@ -118,13 +149,41 @@ public String getDisplayText() { } public List getSupportedServices() { + if (!isForNsx() && CollectionUtils.isEmpty(supportedServices)) { + throw new InvalidParameterValueException("Supported services needs to be provided"); + } + if (isForNsx()) { + supportedServices = new ArrayList<>(List.of( + Dhcp.getName(), + Dns.getName(), + StaticNat.getName(), + SourceNat.getName(), + NetworkACL.getName(), + PortForwarding.getName(), + UserData.getName() + )); + if (getNsxSupportsLbService()) { + supportedServices.add(Lb.getName()); + } + } return supportedServices; } + public boolean isForNsx() { + return BooleanUtils.isTrue(forNsx); + } + + public String getNsxMode() { + return nsxMode; + } + + public boolean getNsxSupportsLbService() { + return org.apache.commons.lang3.BooleanUtils.isTrue(nsxSupportsLbService); + } + public Map> getServiceProviders() { - Map> serviceProviderMap = null; - if (serviceProviderList != null && !serviceProviderList.isEmpty()) { - serviceProviderMap = new HashMap>(); + Map> serviceProviderMap = new HashMap<>(); + if (serviceProviderList != null && !serviceProviderList.isEmpty() && !isForNsx()) { Collection> servicesCollection = serviceProviderList.values(); Iterator> iter = servicesCollection.iterator(); while (iter.hasNext()) { @@ -132,7 +191,7 @@ public Map> getServiceProviders() { if (logger.isTraceEnabled()) { logger.trace("service provider entry specified: " + obj); } - HashMap services = (HashMap)obj; + HashMap services = (HashMap) obj; String service = services.get("service"); String provider = services.get("provider"); List providerList = null; @@ -144,11 +203,31 @@ public Map> getServiceProviders() { providerList.add(provider); serviceProviderMap.put(service, providerList); } + } else if (Boolean.TRUE.equals(forNsx)) { + getServiceProviderMapForNsx(serviceProviderMap); } return serviceProviderMap; } + private void getServiceProviderMapForNsx(Map> serviceProviderMap) { + List unsupportedServices = List.of("Vpn", "BaremetalPxeService", "SecurityGroup", "Connectivity", + "Gateway", "Firewall"); + List routerSupported = List.of("Dhcp", "Dns", "UserData"); + List allServices = Network.Service.listAllServices().stream().map(Network.Service::getName).collect(Collectors.toList()); + for (String service : allServices) { + if (unsupportedServices.contains(service)) + continue; + if (routerSupported.contains(service)) + serviceProviderMap.put(service, List.of(VirtualRouterProvider.Type.VPCVirtualRouter.name())); + else + serviceProviderMap.put(service, List.of(Network.Provider.Nsx.getName())); + } + if (!getNsxSupportsLbService()) { + serviceProviderMap.remove(Lb.getName()); + } + } + public Map> getServiceCapabilityList() { return serviceCapabilityList; } diff --git a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java index 5c1c61130ba6..5760ca3ba1c3 100644 --- a/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java +++ b/api/src/main/java/org/apache/cloudstack/api/command/user/address/ListPublicIpAddressesCmd.java @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.List; +import org.apache.commons.lang.BooleanUtils; import org.apache.cloudstack.acl.RoleType; import org.apache.cloudstack.api.APICommand; @@ -104,6 +105,9 @@ public class ListPublicIpAddressesCmd extends BaseListRetrieveOnlyResourceCountC @Parameter(name = ApiConstants.FOR_DISPLAY, type = CommandType.BOOLEAN, description = "list resources by display flag; only ROOT admin is eligible to pass this parameter", since = "4.4", authorized = {RoleType.Admin}) private Boolean display; + @Parameter(name = ApiConstants.FOR_SYSTEM_VMS, type = CommandType.BOOLEAN, description = "true if range is dedicated for system VMs", since = "4.20.0") + private Boolean forSystemVMs; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -175,6 +179,10 @@ public String getState() { return state; } + public boolean getForSystemVMs() { + return BooleanUtils.isTrue(forSystemVMs); + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// diff --git a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java index e2bf6ef5c60f..8a9bf7789dde 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/IPAddressResponse.java @@ -167,6 +167,10 @@ public class IPAddressResponse extends BaseResponseWithAnnotations implements Co @Param(description="whether the ip address has Firewall/PortForwarding/LoadBalancing rules defined") private boolean hasRules; + @SerializedName(ApiConstants.FOR_SYSTEM_VMS) + @Param(description="true if range is dedicated for System VMs") + private boolean forSystemVms; + public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; } @@ -316,4 +320,8 @@ public void setNetworkName(String networkName) { public void setHasRules(final boolean hasRules) { this.hasRules = hasRules; } + + public void setForSystemVms(boolean forSystemVms) { + this.forSystemVms = forSystemVms; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java index b92725d883e4..b73163a5d057 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/NetworkOfferingResponse.java @@ -99,10 +99,18 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "true if network offering can be used by VPC networks only") private Boolean forVpc; + @SerializedName(ApiConstants.FOR_NSX) + @Param(description = "true if network offering can be used by NSX networks only") + private Boolean forNsx; + @SerializedName(ApiConstants.FOR_TUNGSTEN) @Param(description = "true if network offering can be used by Tungsten-Fabric networks only") private Boolean forTungsten; + @SerializedName(ApiConstants.NSX_MODE) + @Param(description = "Mode in which the network will operate. This parameter is only relevant for NSX offerings") + private String nsxMode; + @SerializedName(ApiConstants.IS_PERSISTENT) @Param(description = "true if network offering supports persistent networks, false otherwise") private Boolean isPersistent; @@ -127,6 +135,10 @@ public class NetworkOfferingResponse extends BaseResponseWithAnnotations { @Param(description = "true if network offering supports public access for guest networks", since = "4.10.0") private Boolean supportsPublicAccess; + @SerializedName(ApiConstants.SUPPORTS_INTERNAL_LB) + @Param(description = "true if network offering supports public access for guest networks", since = "4.20.0") + private Boolean supportsInternalLb; + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description = "the domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") private String domainId; @@ -215,10 +227,18 @@ public void setForVpc(Boolean forVpc) { this.forVpc = forVpc; } + public void setForNsx(Boolean forNsx) { + this.forNsx = forNsx; + } + public void setForTungsten(Boolean forTungsten) { this.forTungsten = forTungsten; } + public void setNsxMode(String nsxMode) { + this.nsxMode = nsxMode; + } + public void setIsPersistent(Boolean isPersistent) { this.isPersistent = isPersistent; } @@ -243,6 +263,10 @@ public void setSupportsPublicAccess(Boolean supportsPublicAccess) { this.supportsPublicAccess = supportsPublicAccess; } + public void setSupportsInternalLb(Boolean supportsInternalLb) { + this.supportsInternalLb = supportsInternalLb; + } + public String getDomainId() { return domainId; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java index a22e2eb7024d..aac6dd3c5776 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VlanIpRangeResponse.java @@ -123,6 +123,10 @@ public class VlanIpRangeResponse extends BaseResponse implements ControlledEntit @Param(description = "indicates whether VLAN IP range is dedicated to system vms or not") private Boolean forSystemVms; + @SerializedName(ApiConstants.FOR_NSX) + @Param(description = "indicates whether IP range is dedicated to NSX resources or not") + private Boolean forNsx; + public void setId(String id) { this.id = id; } @@ -235,4 +239,8 @@ public void setIp6Gateway(String ip6Gateway) { public void setIp6Cidr(String ip6Cidr) { this.ip6Cidr = ip6Cidr; } + + public void setForNsx(Boolean forNsx) { + this.forNsx = forNsx; + } } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java index 6881969646b2..ce00827f06d0 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/VpcOfferingResponse.java @@ -63,9 +63,17 @@ public class VpcOfferingResponse extends BaseResponse { private Boolean supportsDistributedRouter; @SerializedName((ApiConstants.SUPPORTS_REGION_LEVEL_VPC)) - @Param(description = " indicated if the offering can support region level vpc", since = "4.4") + @Param(description = "indicated if the offering can support region level vpc", since = "4.4") private Boolean supportsRegionLevelVpc; + @SerializedName(ApiConstants.FOR_NSX) + @Param(description = "true if vpc offering can be used by NSX networks only") + private Boolean forNsx; + + @SerializedName(ApiConstants.NSX_MODE) + @Param(description = "Mode in which the network will operate. This parameter is only relevant for NSX offerings") + private String nsxMode; + @SerializedName(ApiConstants.DOMAIN_ID) @Param(description = "the domain ID(s) this disk offering belongs to. Ignore this information as it is not currently applicable.") private String domainId; @@ -138,6 +146,14 @@ public void setDomain(String domain) { this.domain = domain; } + public void setForNsx(Boolean forNsx) { + this.forNsx = forNsx; + } + + public void setNsxMode(String nsxMode) { + this.nsxMode = nsxMode; + } + public String getZoneId() { return zoneId; } diff --git a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java index 4e8e665836c3..a898cd9d5774 100644 --- a/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java +++ b/api/src/main/java/org/apache/cloudstack/api/response/ZoneResponse.java @@ -145,6 +145,10 @@ public class ZoneResponse extends BaseResponseWithAnnotations implements SetReso @Param(description = "the type of the zone - core or edge", since = "4.18.0") String type; + @SerializedName(ApiConstants.NSX_ENABLED) + @Param(description = "true, if zone is NSX enabled", since = "4.20.0") + private boolean nsxEnabled = false; + public ZoneResponse() { tags = new LinkedHashSet(); } @@ -368,4 +372,8 @@ public void setType(String type) { public String getType() { return type; } + + public void setNsxEnabled(boolean nsxEnabled) { + this.nsxEnabled = nsxEnabled; + } } diff --git a/api/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElementService.java b/api/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElementService.java index 76706a4cfc9e..1fff54f5f8ff 100644 --- a/api/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElementService.java +++ b/api/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElementService.java @@ -52,4 +52,6 @@ public interface InternalLoadBalancerElementService extends PluggableService { * @return */ List searchForInternalLoadBalancerElements(Long id, Long ntwkSvsProviderId, Boolean enabled); + + VirtualRouterProvider.Type getProviderType(); } diff --git a/api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java b/api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java index 9f3a661f485b..fb4fe121cc70 100644 --- a/api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java +++ b/api/src/main/java/org/apache/cloudstack/user/ResourceReservation.java @@ -34,6 +34,8 @@ Resource.ResourceType getResourceType(); + Long getResourceId(); + String getTag(); Long getReservedAmount(); diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java index 8b95456a84c2..ef10ebff4678 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/offering/CreateNetworkOfferingCmdTest.java @@ -23,14 +23,16 @@ import org.mockito.InjectMocks; import org.springframework.test.util.ReflectionTestUtils; + public class CreateNetworkOfferingCmdTest { @InjectMocks private CreateNetworkOfferingCmd createNetworkOfferingCmd = new CreateNetworkOfferingCmd(); + String netName = "network"; + @Test public void createVpcNtwkOffWithEmptyDisplayText() { - String netName = "network"; ReflectionTestUtils.setField(createNetworkOfferingCmd, "networkOfferingName", netName); Assert.assertEquals(createNetworkOfferingCmd.getDisplayText(), netName); } diff --git a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java index 16b716d7d638..290a2850c9a6 100644 --- a/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java +++ b/api/src/test/java/org/apache/cloudstack/api/command/admin/vpc/CreateVPCOfferingCmdTest.java @@ -52,15 +52,15 @@ public void testServiceProvidersEmpty() throws IllegalArgumentException, IllegalAccessException { CreateVPCOfferingCmd cmd = new CreateVPCOfferingCmd(); ApiCmdTestUtil.set(cmd, ApiConstants.SERVICE_PROVIDER_LIST, new HashMap>()); - Assert.assertNull(cmd.getServiceProviders()); + Assert.assertTrue(cmd.getServiceProviders().isEmpty()); } @Test - public void getDetailsNull() throws IllegalArgumentException, + public void getDetailsEmpty() throws IllegalArgumentException, IllegalAccessException { CreateVPCOfferingCmd cmd = new CreateVPCOfferingCmd(); ApiCmdTestUtil.set(cmd, ApiConstants.SERVICE_PROVIDER_LIST, null); - Assert.assertNull(cmd.getServiceProviders()); + Assert.assertTrue(cmd.getServiceProviders().isEmpty()); } @Test diff --git a/client/pom.xml b/client/pom.xml index 91399097d648..9ff3f2ada392 100644 --- a/client/pom.xml +++ b/client/pom.xml @@ -296,6 +296,11 @@ cloud-plugin-network-tungsten ${project.version} + + org.apache.cloudstack + cloud-plugin-network-nsx + ${project.version} + org.apache.cloudstack cloud-plugin-network-elb diff --git a/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java b/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java index e9781993239b..06583f2d0d3d 100644 --- a/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java +++ b/core/src/main/java/com/cloud/agent/api/SetupGuestNetworkCommand.java @@ -35,6 +35,7 @@ public class SetupGuestNetworkCommand extends NetworkElementCommand { String routerIpv6 = null; String routerIpv6Gateway = null; String routerIpv6Cidr = null; + boolean isVrGuestGateway = false; public NicTO getNic() { return nic; @@ -114,4 +115,12 @@ public void setDefaultIp6Dns1(String defaultIp6Dns1) { public void setDefaultIp6Dns2(String defaultIp6Dns2) { this.defaultIp6Dns2 = defaultIp6Dns2; } + + public boolean isVrGuestGateway() { + return isVrGuestGateway; + } + + public void setVrGuestGateway(boolean vrGuestGateway) { + isVrGuestGateway = vrGuestGateway; + } } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java index aee1e7795712..1a6824ceb7fc 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/facade/SetGuestNetworkConfigItem.java @@ -75,6 +75,7 @@ public List generateConfig(final NetworkElementCommand cmd) { guestNetwork.setRouterIp6(command.getRouterIpv6()); guestNetwork.setRouterIp6Gateway(command.getRouterIpv6Gateway()); guestNetwork.setRouterIp6Cidr(command.getRouterIpv6Cidr()); + guestNetwork.setVrGuestGateway(command.isVrGuestGateway()); return generateConfigItems(guestNetwork); } diff --git a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java index bb5e443c2e8c..a416b4bc5e41 100644 --- a/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java +++ b/core/src/main/java/com/cloud/agent/resource/virtualnetwork/model/GuestNetwork.java @@ -37,6 +37,7 @@ public class GuestNetwork extends ConfigBase { private String routerIp6; private String routerIp6Gateway; private String routerIp6Cidr; + private boolean isVrGuestGateway; private Integer mtu; @@ -202,4 +203,12 @@ public void setMtu(Integer mtu) { public Integer getMtu() { return mtu; } + + public boolean isVrGuestGateway() { + return isVrGuestGateway; + } + + public void setVrGuestGateway(boolean vrGuestGateway) { + isVrGuestGateway = vrGuestGateway; + } } diff --git a/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml b/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml index a36d12431551..49775fe41e1c 100644 --- a/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml +++ b/core/src/main/resources/META-INF/cloudstack/core/spring-core-registry-core-context.xml @@ -350,4 +350,12 @@ + + + + + + + + diff --git a/core/src/main/resources/META-INF/cloudstack/network/spring-core-lifecycle-network-context-inheritable.xml b/core/src/main/resources/META-INF/cloudstack/network/spring-core-lifecycle-network-context-inheritable.xml index 8dbaf6105811..2240d1f2606a 100644 --- a/core/src/main/resources/META-INF/cloudstack/network/spring-core-lifecycle-network-context-inheritable.xml +++ b/core/src/main/resources/META-INF/cloudstack/network/spring-core-lifecycle-network-context-inheritable.xml @@ -103,4 +103,9 @@ + + + + + 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 2005b70b4394..110592161f96 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 @@ -105,6 +105,9 @@ public interface NetworkOrchestrationService { static final ConfigKey TUNGSTEN_ENABLED = new ConfigKey<>(Boolean.class, "tungsten.plugin.enable", "Advanced", "false", "Indicates whether to enable the Tungsten plugin", false, ConfigKey.Scope.Zone, null); + static final ConfigKey NSX_ENABLED = new ConfigKey<>(Boolean.class, "nsx.plugin.enable", "Advanced", "false", + "Indicates whether to enable the NSX plugin", false, ConfigKey.Scope.Zone, null); + List setupNetwork(Account owner, NetworkOffering offering, DeploymentPlan plan, String name, String displayText, boolean isDefault) throws ConcurrentOperationException; diff --git a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java index 5343fb632b54..bbddd8fd471f 100644 --- a/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java +++ b/engine/components-api/src/main/java/com/cloud/configuration/ConfigurationManager.java @@ -63,6 +63,9 @@ public interface ConfigurationManager { static final String VM_USERDATA_MAX_LENGTH_STRING = "vm.userdata.max.length"; static final ConfigKey VM_USERDATA_MAX_LENGTH = new ConfigKey<>("Advanced", Integer.class, VM_USERDATA_MAX_LENGTH_STRING, "32768", "Max length of vm userdata after base64 decoding. Default is 32768 and maximum is 1048576", true); + public static final ConfigKey AllowNonRFC1918CompliantIPs = new ConfigKey<>(Boolean.class, + "allow.non.rfc1918.compliant.ips", "Advanced", "false", + "Allows non-compliant RFC 1918 IPs for Shared, Isolated networks and VPCs", true, null); /** * @param offering @@ -97,7 +100,6 @@ public interface ConfigurationManager { // * @param volatileVm // * @param hostTag // * @param networkRate -// * TODO // * @param id // * @param useVirtualNetwork // * @param deploymentPlanner @@ -167,11 +169,9 @@ HostPodVO createPod(long userId, String podName, DataCenter zone, String gateway * @param zoneType * @param allocationState * @param networkDomain - * TODO * @param isSecurityGroupEnabled - * TODO - * @param ip6Dns1 TODO - * @param ip6Dns2 TODO + * @param ip6Dns1 + * @param ip6Dns2 * @return * @throws * @throws @@ -186,7 +186,7 @@ DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, * * @param userId * @param vlanDbId - * @param caller TODO + * @param caller * @return success/failure */ boolean deleteVlanAndPublicIpRange(long userId, long vlanDbId, Account caller); @@ -197,30 +197,25 @@ DataCenterVO createZone(long userId, String zoneName, String dns1, String dns2, /** * Creates a new network offering + * * @param name * @param displayText * @param trafficType * @param tags * @param specifyVlan * @param networkRate - * TODO * @param serviceProviderMap - * TODO * @param isDefault - * TODO * @param type - * TODO * @param systemOnly - * TODO * @param serviceOfferingId - * @param conserveMode - * ; + * @param conserveMode ; * @param specifyIpRanges - * TODO - * @param isPersistent - * ; - * @param details TODO + * @param isPersistent ; + * @param details * @param forVpc + * @param forTungsten + * @param forNsx * @param domainIds * @param zoneIds * @return network offering object @@ -230,10 +225,10 @@ NetworkOfferingVO createNetworkOffering(String name, String displayText, Traffic Integer networkRate, Map> serviceProviderMap, boolean isDefault, Network.GuestType type, boolean systemOnly, Long serviceOfferingId, boolean conserveMode, Map> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent, Map details, boolean egressDefaultPolicy, Integer maxconn, boolean enableKeepAlive, Boolean forVpc, - Boolean forTungsten, List domainIds, List zoneIds, boolean enableOffering, final NetUtils.InternetProtocol internetProtocol); + Boolean forTungsten, boolean forNsx, String mode, List domainIds, List zoneIds, boolean enableOffering, final NetUtils.InternetProtocol internetProtocol); Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, boolean forSystemVms, Long podId, String startIP, String endIP, - String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr) + String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanIp6Gateway, String vlanIp6Cidr, boolean forNsx) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException; void createDefaultSystemNetworks(long zoneId) throws ConcurrentOperationException; diff --git a/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java b/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java index d1153a291f8b..d69a72a02c5c 100644 --- a/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java +++ b/engine/components-api/src/main/java/com/cloud/network/addr/PublicIp.java @@ -269,4 +269,11 @@ public State getRuleState() { public void setRuleState(State ruleState) { _addr.setRuleState(ruleState); } + + @Override + public boolean isForSystemVms() { + return false; + } + + } diff --git a/engine/components-api/src/main/java/com/cloud/network/vpc/NetworkACLManager.java b/engine/components-api/src/main/java/com/cloud/network/vpc/NetworkACLManager.java index 4200ea8c6019..de69b8941838 100644 --- a/engine/components-api/src/main/java/com/cloud/network/vpc/NetworkACLManager.java +++ b/engine/components-api/src/main/java/com/cloud/network/vpc/NetworkACLManager.java @@ -19,6 +19,7 @@ import java.util.List; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.Network; import com.cloud.network.dao.NetworkVO; public interface NetworkACLManager { @@ -91,4 +92,6 @@ public interface NetworkACLManager { boolean revokeACLItemsForPrivateGw(PrivateGateway gateway) throws ResourceUnavailableException; boolean applyACLToPrivateGw(PrivateGateway gateway) throws ResourceUnavailableException; + + boolean reorderAclRules(VpcVO vpc, List networks, List networkACLItems); } diff --git a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java index 873993892714..ea44a8d8cb98 100755 --- a/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java @@ -1,4 +1,4 @@ -// Licensed to the Apacohe Software Foundation (ASF) under one +// 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 @@ -36,6 +36,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.TimeZone; import java.util.UUID; @@ -48,6 +49,12 @@ import javax.naming.ConfigurationException; import javax.persistence.EntityExistsException; +import com.cloud.configuration.Resource; +import com.cloud.domain.Domain; +import com.cloud.domain.dao.DomainDao; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.user.dao.AccountDao; import com.cloud.event.ActionEventUtils; import com.google.gson.Gson; import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; @@ -81,6 +88,7 @@ import org.apache.cloudstack.framework.messagebus.MessageHandler; import org.apache.cloudstack.jobs.JobInfo; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao; import org.apache.cloudstack.storage.datastore.db.StoragePoolVO; import org.apache.cloudstack.storage.to.VolumeObjectTO; @@ -290,6 +298,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject private VMInstanceDao _vmDao; @Inject + private ReservationDao _reservationDao; + @Inject private ServiceOfferingDao _offeringDao; @Inject private DiskOfferingDao _diskOfferingDao; @@ -385,6 +395,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac private DomainRouterJoinDao domainRouterJoinDao; @Inject private AnnotationDao annotationDao; + @Inject + private AccountDao accountDao; + @Inject + private VpcDao vpcDao; + @Inject + private DomainDao domainDao; VmWorkJobHandlerProxy _jobHandlerProxy = new VmWorkJobHandlerProxy(this); @@ -599,11 +615,18 @@ private boolean isValidSystemVMType(VirtualMachine vm) { VirtualMachine.Type.ConsoleProxy.equals(vm.getType()); } - protected void advanceExpunge(VMInstanceVO vm) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException { + private boolean isVmDestroyed(VMInstanceVO vm) { if (vm == null || vm.getRemoved() != null) { if (logger.isDebugEnabled()) { logger.debug("Unable to find vm or vm is expunged: " + vm); } + return true; + } + return false; + } + + protected void advanceExpunge(VMInstanceVO vm) throws ResourceUnavailableException, OperationTimedoutException, ConcurrentOperationException { + if (isVmDestroyed(vm)) { return; } @@ -674,28 +697,31 @@ protected void advanceExpunge(VMInstanceVO vm) throws ResourceUnavailableExcepti // send hypervisor-dependent commands before removing final List finalizeExpungeCommands = hvGuru.finalizeExpunge(vm); - if (CollectionUtils.isNotEmpty(finalizeExpungeCommands) || CollectionUtils.isNotEmpty(nicExpungeCommands)) { - if (hostId != null) { - final Commands cmds = new Commands(Command.OnError.Stop); - addAllExpungeCommandsFromList(finalizeExpungeCommands, cmds, vm); - addAllExpungeCommandsFromList(nicExpungeCommands, cmds, vm); - _agentMgr.send(hostId, cmds); - if (!cmds.isSuccessful()) { - for (final Answer answer : cmds.getAnswers()) { - if (!answer.getResult()) { - logger.warn("Failed to expunge vm due to: " + answer.getDetails()); - throw new CloudRuntimeException("Unable to expunge " + vm + " due to " + answer.getDetails()); - } - } - } - } - } + handleUnsuccessfulExpungeOperation(finalizeExpungeCommands, nicExpungeCommands, vm, hostId); if (logger.isDebugEnabled()) { logger.debug("Expunged " + vm); } } + private void handleUnsuccessfulExpungeOperation(List finalizeExpungeCommands, List nicExpungeCommands, + VMInstanceVO vm, Long hostId) throws OperationTimedoutException, AgentUnavailableException { + if (CollectionUtils.isNotEmpty(finalizeExpungeCommands) || CollectionUtils.isNotEmpty(nicExpungeCommands) && (hostId != null)) { + final Commands cmds = new Commands(Command.OnError.Stop); + addAllExpungeCommandsFromList(finalizeExpungeCommands, cmds, vm); + addAllExpungeCommandsFromList(nicExpungeCommands, cmds, vm); + _agentMgr.send(hostId, cmds); + if (!cmds.isSuccessful()) { + for (final Answer answer : cmds.getAnswers()) { + if (!answer.getResult()) { + logger.warn("Failed to expunge vm due to: " + answer.getDetails()); + throw new CloudRuntimeException(String.format("Unable to expunge %s due to %s", vm, answer.getDetails())); + } + } + } + } + } + protected void handleUnsuccessfulCommands(Commands cmds, VMInstanceVO vm) throws CloudRuntimeException { String cmdsStr = cmds.toString(); String vmToString = vm.toString(); @@ -892,7 +918,7 @@ protected boolean checkWorkItems(final VMInstanceVO vm, final State state) throw @DB protected Ternary changeToStartState(final VirtualMachineGuru vmGuru, final VMInstanceVO vm, final User caller, - final Account account) throws ConcurrentOperationException { + final Account account, Account owner, ServiceOfferingVO offering, VirtualMachineTemplate template) throws ConcurrentOperationException { final long vmId = vm.getId(); ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Starting, vm.getType(), vm.getId()); @@ -912,6 +938,9 @@ public Ternary doInTransaction(final if (logger.isDebugEnabled()) { logger.debug("Successfully transitioned to start state for " + vm + " reservation id = " + work.getId()); } + if (VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) { + _resourceLimitMgr.incrementVmResourceCount(owner.getAccountId(), vm.isDisplay(), offering, template); + } return new Ternary<>(vm, context, work); } @@ -1104,7 +1133,10 @@ public void orchestrateStart(final String vmUuid, final Map start = changeToStartState(vmGuru, vm, caller, account); + final Account owner = _entityMgr.findById(Account.class, vm.getAccountId()); + final ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); + final VirtualMachineTemplate template = _entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vm.getTemplateId()); + final Ternary start = changeToStartState(vmGuru, vm, caller, account, owner, offering, template); if (start == null) { return; } @@ -1114,8 +1146,6 @@ public void orchestrateStart(final String vmUuid, final Map sshAccessDetails = _networkMgr.getSystemVMAccessDetails(vm); @@ -1460,6 +1486,55 @@ public void orchestrateStart(final String vmUuid, final Map networkToNetworkNameMap = new HashMap<>(); + if (VirtualMachine.Type.User.equals(vm.getType())) { + List userVmJoinVOs = userVmJoinDao.searchByIds(vm.getId()); + if (userVmJoinVOs != null && !userVmJoinVOs.isEmpty()) { + for (UserVmJoinVO userVmJoinVO : userVmJoinVOs) { + addToNetworkNameMap(userVmJoinVO.getNetworkId(), vm.getDataCenterId(), networkToNetworkNameMap); + } + vmTO.setNetworkIdToNetworkNameMap(networkToNetworkNameMap); + } + } else if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) { + List routerJoinVO = domainRouterJoinDao.getRouterByIdAndTrafficType(vm.getId(), Networks.TrafficType.Guest); + for (DomainRouterJoinVO router : routerJoinVO) { + NetworkVO guestNetwork = _networkDao.findById(router.getNetworkId()); + if (guestNetwork.getVpcId() == null && guestNetwork.getBroadcastDomainType() == Networks.BroadcastDomainType.NSX) { + addToNetworkNameMap(router.getNetworkId(), vm.getDataCenterId(), networkToNetworkNameMap); + } + } + vmTO.setNetworkIdToNetworkNameMap(networkToNetworkNameMap); + } + } + + private void addToNetworkNameMap(long networkId, long dataCenterId, Map networkToNetworkNameMap) { + NetworkVO networkVO = _networkDao.findById(networkId); + Account acc = accountDao.findById(networkVO.getAccountId()); + Domain domain = domainDao.findById(networkVO.getDomainId()); + DataCenter zone = _dcDao.findById(dataCenterId); + if (Objects.isNull(zone)) { + throw new CloudRuntimeException(String.format("Failed to find zone with ID: %s", dataCenterId)); + } + if (Objects.isNull(acc)) { + throw new CloudRuntimeException(String.format("Failed to find account with ID: %s", networkVO.getAccountId())); + } + if (Objects.isNull(domain)) { + throw new CloudRuntimeException(String.format("Failed to find domain with ID: %s", networkVO.getDomainId())); + } + String networkName = String.format("D%s-A%s-Z%s", domain.getId(), acc.getId(), zone.getId()); + if (Objects.isNull(networkVO.getVpcId())) { + networkName += "-S" + networkVO.getId(); + } else { + VpcVO vpc = vpcDao.findById(networkVO.getVpcId()); + if (Objects.isNull(vpc)) { + throw new CloudRuntimeException(String.format("Failed to find VPC with ID: %s", networkVO.getVpcId())); + } + networkName = String.format("%s-V%s-S%s", networkName, vpc.getId(), networkVO.getId()); + } + networkToNetworkNameMap.put(networkVO.getId(), networkName); + } + /** * Setting pod id to null can result in migration of Volumes across pods. This is not desirable for VMs which * have a volume in Ready state (happens when a VM is shutdown and started again). @@ -2204,16 +2279,21 @@ private void advanceStop(final VMInstanceVO vm, final boolean cleanUpEvenIfUnabl _workDao.update(work.getId(), work); } - boolean result = stateTransitTo(vm, Event.OperationSucceeded, null); - if (result) { - vm.setPowerState(PowerState.PowerOff); - _vmDao.update(vm.getId(), vm); - if (VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) { - ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - _resourceLimitMgr.decrementVmResourceCount(vm.getAccountId(), vm.isDisplay(), offering, template); + boolean result = Transaction.execute(new TransactionCallbackWithException() { + @Override + public Boolean doInTransaction(TransactionStatus status) throws NoTransitionException { + boolean result = stateTransitTo(vm, Event.OperationSucceeded, null); + + if (result && VirtualMachine.Type.User.equals(vm.type) && ResourceCountRunningVMsonly.value()) { + ServiceOfferingVO offering = _offeringDao.findById(vm.getId(), vm.getServiceOfferingId()); + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + _resourceLimitMgr.decrementVmResourceCount(vm.getAccountId(), vm.isDisplay(), offering, template); + } + return result; } - } else { + }); + + if (!result) { throw new CloudRuntimeException("unable to stop " + vm); } } catch (final NoTransitionException e) { @@ -2246,6 +2326,12 @@ public boolean stateTransitTo(final VirtualMachine vm1, final VirtualMachine.Eve vm.setLastHostId(vm.getHostId()); } } + + if (e.equals(VirtualMachine.Event.DestroyRequested) || e.equals(VirtualMachine.Event.ExpungeOperation)) { + _reservationDao.setResourceId(Resource.ResourceType.user_vm, null); + _reservationDao.setResourceId(Resource.ResourceType.cpu, null); + _reservationDao.setResourceId(Resource.ResourceType.memory, null); + } return _stateMachine.transitTo(vm, e, new Pair<>(vm.getHostId(), hostId), _vmDao); } @@ -2734,6 +2820,7 @@ protected void migrate(final VMInstanceVO vm, final long srcHostId, final Deploy final VirtualMachineTO to = toVmTO(profile); final PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to); + setVmNetworkDetails(vm, to); ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId()); work.setStep(Step.Prepare); 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 e7cd1a3ef3a2..5e3c8cfaa579 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,6 +38,9 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.VlanDetailsDao; +import com.cloud.network.dao.NsxProviderDao; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; @@ -56,6 +59,7 @@ import org.apache.cloudstack.network.dao.NetworkPermissionDao; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import com.cloud.agent.AgentManager; @@ -253,6 +257,7 @@ import com.cloud.vm.dao.UserVmDao; import com.cloud.vm.dao.VMInstanceDao; import com.googlecode.ipv6.IPv6Address; +import org.jetbrains.annotations.NotNull; /** * NetworkManagerImpl implements NetworkManager. @@ -337,8 +342,12 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra Ipv6Service ipv6Service; @Inject RouterNetworkDao routerNetworkDao; + @Inject + private VlanDetailsDao vlanDetailsDao; List networkGurus; + @Inject + private NsxProviderDao nsxProviderDao; @Override public List getNetworkGurus() { @@ -500,6 +509,7 @@ public boolean configure(final String name, final Map params) th defaultTungstenSharedSGEnabledNetworkOfferingProviders.put(Service.UserData, tungstenProvider); defaultTungstenSharedSGEnabledNetworkOfferingProviders.put(Service.SecurityGroup, tungstenProvider); + final Map> defaultIsolatedSourceNatEnabledNetworkOfferingProviders = new HashMap>(); defaultProviders.clear(); defaultProviders.add(Network.Provider.VirtualRouter); @@ -536,27 +546,27 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { if (_networkOfferingDao.findByUniqueName(NetworkOffering.QuickCloudNoServices) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.QuickCloudNoServices, "Offering for QuickCloud with no services", TrafficType.Guest, null, true, Availability.Optional, null, new HashMap>(), true, Network.GuestType.Shared, false, null, true, null, true, - false, null, false, null, true, false, false, null, null, true, null); + false, null, false, null, true, false, false, false, null, null, null, true, null); } //#2 - SG enabled network offering if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOfferingWithSGService) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOfferingWithSGService, "Offering for Shared Security group enabled networks", TrafficType.Guest, null, true, Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, - null, true, false, null, false, null, true, false, false, null, null, true, null); + null, true, false, null, false, null, true, false, false, false, null, null, null, true, null); } //#3 - shared network offering with no SG service if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedNetworkOffering) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedNetworkOffering, "Offering for Shared networks", TrafficType.Guest, null, true, Availability.Optional, null, defaultSharedNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, null, true, false, null, false, - null, true, false, false, null, null, true, null); + null, true, false, false, false, null,null, null, true, null); } if (_networkOfferingDao.findByUniqueName(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DEFAULT_TUNGSTEN_SHARED_NETWORK_OFFERING_WITH_SGSERVICE, "Offering for Tungsten Shared Security group enabled networks", TrafficType.Guest, null, true, Availability.Optional, null, defaultTungstenSharedSGEnabledNetworkOfferingProviders, true, Network.GuestType.Shared, false, null, true, - null, true, false, null, false, null, true, false, true,null, null, true, null); + null, true, false, null, false, null, true, false, true, false, null, null,null, true, null); offering.setState(NetworkOffering.State.Enabled); _networkOfferingDao.update(offering.getId(), offering); } @@ -566,14 +576,14 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingWithSourceNatService, "Offering for Isolated networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Required, null, defaultIsolatedSourceNatEnabledNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, true, null, false, false, null, false, null, - true, false, false, null, null, true, null); + true, false, false, false, null, null,null, true, null); } //#5 - default vpc offering with LB service if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworks, "Offering for Isolated VPC networks with Source Nat service enabled", TrafficType.Guest, null, false, Availability.Optional, null, - defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, null, null, true, null); + defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null); } //#6 - default vpc offering with no LB service @@ -582,14 +592,14 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { defaultVPCOffProviders.remove(Service.Lb); offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksNoLB, "Offering for Isolated VPC networks with Source Nat service enabled and LB service disabled", TrafficType.Guest, null, false, Availability.Optional, - null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, null, null, true, null); + null, defaultVPCOffProviders, true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null,true, null); } //#7 - isolated offering with source nat disabled if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOffering) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOffering, "Offering for Isolated networks with no Source Nat service", TrafficType.Guest, null, true, Availability.Optional, null, defaultIsolatedNetworkOfferingProviders, true, Network.GuestType.Isolated, false, null, - true, null, true, false, null, false, null, true, false, false, null, null, true, null); + true, null, true, false, null, false, null, true, false, false, false, null, null, null, true, null); } //#8 - network offering with internal lb service @@ -611,7 +621,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultIsolatedNetworkOfferingForVpcNetworksWithInternalLB, "Offering for Isolated VPC networks with Internal Lb support", TrafficType.Guest, null, false, Availability.Optional, null, internalLbOffProviders, - true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, null, null, true, null); + true, Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, true, null); offering.setInternalLb(true); offering.setPublicLb(false); _networkOfferingDao.update(offering.getId(), offering); @@ -642,7 +652,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { if (_networkOfferingDao.findByUniqueName(NetworkOffering.DefaultSharedEIPandELBNetworkOffering) == null) { offering = _configMgr.createNetworkOffering(NetworkOffering.DefaultSharedEIPandELBNetworkOffering, "Offering for Shared networks with Elastic IP and Elastic LB capabilities", TrafficType.Guest, null, true, Availability.Optional, null, - netscalerServiceProviders, true, Network.GuestType.Shared, false, null, true, serviceCapabilityMap, true, false, null, false, null, true, false, false, null, null, true, null); + netscalerServiceProviders, true, Network.GuestType.Shared, false, null, true, serviceCapabilityMap, true, false, null, false, null, true, false, false, false, null, null, null, true, null); offering.setDedicatedLB(false); _networkOfferingDao.update(offering.getId(), offering); } @@ -738,20 +748,8 @@ public List setupNetwork(final Account owner, final NetworkOf .getBroadcastDomainType() == BroadcastDomainType.Vlan || predefined.getBroadcastDomainType() == BroadcastDomainType.Lswitch || predefined .getBroadcastDomainType() == BroadcastDomainType.Vxlan)) { final List configs = _networksDao.listBy(owner.getId(), offering.getId(), plan.getDataCenterId()); - if (configs.size() > 0) { - if (logger.isDebugEnabled()) { - logger.debug("Found existing network configuration for offering " + offering + ": " + configs.get(0)); - } - - if (errorIfAlreadySetup) { - final InvalidParameterValueException ex = new InvalidParameterValueException( - "Found existing network configuration (with specified id) for offering (with specified id)"); - ex.addProxyObject(offering.getUuid(), "offeringId"); - ex.addProxyObject(configs.get(0).getUuid(), "networkConfigId"); - throw ex; - } else { - return configs; - } + if (!configs.isEmpty()) { + return existingConfiguration(offering, configs, errorIfAlreadySetup); } } @@ -760,7 +758,7 @@ public List setupNetwork(final Account owner, final NetworkOf long related = -1; for (final NetworkGuru guru : networkGurus) { - final Network network = guru.design(offering, plan, predefined, owner); + final Network network = guru.design(offering, plan, predefined, name, vpcId, owner); if (network == null) { continue; } @@ -783,11 +781,8 @@ public List setupNetwork(final Account owner, final NetworkOf Transaction.execute(new TransactionCallbackNoReturn() { @Override public void doInTransactionWithoutResult(final TransactionStatus status) { - final NetworkVO vo = new NetworkVO(id, network, offering.getId(), guru.getName(), owner.getDomainId(), owner.getId(), relatedFile, name, displayText, predefined - .getNetworkDomain(), offering.getGuestType(), plan.getDataCenterId(), plan.getPhysicalNetworkId(), aclType, offering.isSpecifyIpRanges(), - vpcId, offering.isRedundantRouter(), predefined.getExternalId()); - vo.setDisplayNetwork(isDisplayNetworkEnabled == null ? true : isDisplayNetworkEnabled); - vo.setStrechedL2Network(offering.isSupportingStrechedL2()); + final NetworkVO vo = getNetworkVO(id, offering, plan, predefined, + network, guru, owner, name, displayText,relatedFile, aclType,vpcId, isDisplayNetworkEnabled); final NetworkVO networkPersisted = _networksDao.persist(vo, vo.getGuestType() == Network.GuestType.Isolated, finalizeServicesAndProvidersForNetwork(offering, plan.getPhysicalNetworkId())); networks.add(networkPersisted); @@ -804,13 +799,14 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } if (domainId != null && aclType == ACLType.Domain) { - _networksDao.addDomainToNetwork(id, domainId, subdomainAccess == null ? true : subdomainAccess); + _networksDao.addDomainToNetwork(id, domainId, subdomainAccess == null || subdomainAccess); } } }); + guru.setup(network, relatedFile); } - if (networks.size() < 1) { + if (networks.isEmpty()) { // see networkOfferingVO.java final CloudRuntimeException ex = new CloudRuntimeException("Unable to convert network offering with specified id to network profile"); ex.addProxyObject(offering.getUuid(), "offeringId"); @@ -824,6 +820,37 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { } } + @NotNull + private static NetworkVO getNetworkVO(long id, final NetworkOffering offering, final DeploymentPlan plan, final Network predefined, + Network network, final NetworkGuru guru, final Account owner, + final String name, final String displayText, long relatedFile, final ACLType aclType, + final Long vpcId, final Boolean isDisplayNetworkEnabled) { + final NetworkVO vo = new NetworkVO(id, network, offering.getId(), guru.getName(), owner.getDomainId(), owner.getId(), + relatedFile, name, displayText, predefined.getNetworkDomain(), offering.getGuestType(), + plan.getDataCenterId(), plan.getPhysicalNetworkId(), aclType, offering.isSpecifyIpRanges(), + vpcId, offering.isRedundantRouter(), predefined.getExternalId()); + vo.setDisplayNetwork(isDisplayNetworkEnabled == null || isDisplayNetworkEnabled); + vo.setStrechedL2Network(offering.isSupportingStrechedL2()); + return vo; + } + + private List existingConfiguration(final NetworkOffering offering, List configs, + final boolean errorIfAlreadySetup) { + if (logger.isDebugEnabled()) { + logger.debug("Found existing network configuration for offering " + offering + ": " + configs.get(0)); + } + + if (errorIfAlreadySetup) { + final InvalidParameterValueException ex = new InvalidParameterValueException( + "Found existing network configuration (with specified id) for offering (with specified id)"); + ex.addProxyObject(offering.getUuid(), "offeringId"); + ex.addProxyObject(configs.get(0).getUuid(), "networkConfigId"); + throw ex; + } else { + return configs; + } + } + @Override @DB public void allocate(final VirtualMachineProfile vm, final LinkedHashMap> networks, final Map> extraDhcpOptions) throws InsufficientCapacityException, @@ -1025,6 +1052,12 @@ public Pair allocateNic(final NicProfile requested, final N return null; } + if (isNicAllocatedForNsxPublicNetworkOnVR(network, profile, vm)) { + String guruName = "NsxPublicNetworkGuru"; + NetworkGuru nsxGuru = AdapterBase.getAdapterByName(networkGurus, guruName); + nsxGuru.allocate(network, profile, vm); + } + if (isDefaultNic != null) { profile.setDefaultNic(isDefaultNic); } @@ -1057,6 +1090,36 @@ public Pair allocateNic(final NicProfile requested, final N return new Pair(vmNic, Integer.valueOf(deviceId)); } + private boolean isNicAllocatedForNsxPublicNetworkOnVR(Network network, NicProfile requested, VirtualMachineProfile vm) { + if (ObjectUtils.anyNull(network, requested, vm)) { + return false; + } + boolean isVirtualRouter = vm.getType() == Type.DomainRouter; + boolean isPublicTraffic = network.getTrafficType() == TrafficType.Public; + if (!isVirtualRouter || !isPublicTraffic || requested.getIPv4Address() == null) { + return false; + } + long dataCenterId = vm.getVirtualMachine().getDataCenterId(); + if (nsxProviderDao.findByZoneId(dataCenterId) == null) { + return false; + } + + Long vpcId = _ipAddressDao.findByIp(requested.getIPv4Address()).getVpcId(); + List ips = _ipAddressDao.listByAssociatedVpc(vpcId, true); + + if (CollectionUtils.isEmpty(ips)) { + return false; + } + ips = ips.stream().filter(x -> !x.getAddress().addr().equals(requested.getIPv4Address())).collect(Collectors.toList()); + IPAddressVO ip = ips.get(0); + VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(ip.getVlanId(), ApiConstants.NSX_DETAIL_KEY); + if (vlanDetail == null) { + return false; + } + boolean isForNsx = vlanDetail.getValue().equalsIgnoreCase("true"); + return isForNsx && !ip.isForSystemVms(); + } + private void setMtuDetailsInVRNic(final Pair networks, Network network, NicVO vo) { if (TrafficType.Public == network.getTrafficType()) { if (networks == null) { @@ -2814,10 +2877,9 @@ private Network createGuestNetwork(final long networkOfferingId, final String na } // Check if cidr is RFC1918 compliant if the network is Guest Isolated for IPv4 - if (cidr != null && ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) { - if (!NetUtils.validateGuestCidr(cidr)) { + if (cidr != null && (ntwkOff.getGuestType() == Network.GuestType.Isolated && ntwkOff.getTrafficType() == TrafficType.Guest) && + !NetUtils.validateGuestCidr(cidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { throw new InvalidParameterValueException("Virtual Guest Cidr " + cidr + " is not RFC 1918 or 6598 compliant"); - } } final String networkDomainFinal = networkDomain; @@ -3844,7 +3906,8 @@ public void doInTransactionWithoutResult(final TransactionStatus status) throws private boolean cleanupNetworkResources(final long networkId, final Account caller, final long callerUserId) { boolean success = true; - final Network network = _networksDao.findById(networkId); + final NetworkVO network = _networksDao.findById(networkId); + final NetworkOfferingVO networkOffering= _networkOfferingDao.findById(network.getNetworkOfferingId()); //remove all PF/Static Nat rules for the network try { @@ -4724,6 +4787,6 @@ public ConfigKey[] getConfigKeys() { return new ConfigKey[]{NetworkGcWait, NetworkGcInterval, NetworkLockTimeout, GuestDomainSuffix, NetworkThrottlingRate, MinVRVersion, PromiscuousMode, MacAddressChanges, ForgedTransmits, MacLearning, RollingRestartEnabled, - TUNGSTEN_ENABLED }; + TUNGSTEN_ENABLED, NSX_ENABLED }; } } diff --git a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java index e357c062c171..9b32980087c0 100644 --- a/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java +++ b/engine/orchestration/src/test/java/com/cloud/vm/VirtualMachineManagerImplTest.java @@ -39,6 +39,18 @@ import java.util.Random; import java.util.stream.Collectors; +import com.cloud.agent.api.to.VirtualMachineTO; +import com.cloud.api.query.vo.UserVmJoinVO; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.user.AccountVO; +import com.cloud.user.dao.AccountDao; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.subsystem.api.storage.StoragePoolAllocator; import org.apache.cloudstack.framework.config.ConfigKey; @@ -182,6 +194,16 @@ public class VirtualMachineManagerImplTest { @Mock private UserVmVO userVmMock; @Mock + private NetworkDao networkDao; + @Mock + private AccountDao accountDao; + @Mock + private DomainDao domainDao; + @Mock + private DataCenterDao dcDao; + @Mock + private VpcDao vpcDao; + @Mock private EntityManager _entityMgr; @Mock private DeploymentPlanningManager _dpMgr; @@ -938,10 +960,53 @@ public void checkAndAttemptMigrateVmAcrossClusterNonValid() { virtualMachineManagerImpl.checkAndAttemptMigrateVmAcrossCluster(vm, destinationClusterId, map); } + @Test + public void checkIfVmNetworkDetailsReturnedIsCorrect() { + VMInstanceVO vm = new VMInstanceVO(1L, 1L, "VM1", "i-2-2-VM", + VirtualMachine.Type.User, 1L, HypervisorType.KVM, 1L, 1L, 1L, + 1L, false, false); + + VirtualMachineTO vmTO = new VirtualMachineTO() { + }; + UserVmJoinVO userVm = new UserVmJoinVO(); + NetworkVO networkVO = mock(NetworkVO.class); + AccountVO accountVO = mock(AccountVO.class); + DomainVO domainVO = mock(DomainVO.class); + domainVO.setName("testDomain"); + DataCenterVO dataCenterVO = mock(DataCenterVO.class); + VpcVO vpcVO = mock(VpcVO.class); + + networkVO.setAccountId(1L); + networkVO.setName("testNet"); + networkVO.setVpcId(1L); + + accountVO.setAccountName("testAcc"); + + vpcVO.setName("VPC1"); + + + List userVms = List.of(userVm); + Mockito.when(userVmJoinDaoMock.searchByIds(anyLong())).thenReturn(userVms); + Mockito.when(networkDao.findById(anyLong())).thenReturn(networkVO); + Mockito.when(accountDao.findById(anyLong())).thenReturn(accountVO); + Mockito.when(domainDao.findById(anyLong())).thenReturn(domainVO); + Mockito.when(dcDao.findById(anyLong())).thenReturn(dataCenterVO); + Mockito.when(vpcDao.findById(anyLong())).thenReturn(vpcVO); + Mockito.when(dataCenterVO.getId()).thenReturn(1L); + when(accountVO.getId()).thenReturn(2L); + Mockito.when(domainVO.getId()).thenReturn(3L); + Mockito.when(vpcVO.getId()).thenReturn(4L); + Mockito.when(networkVO.getId()).thenReturn(5L); + virtualMachineManagerImpl.setVmNetworkDetails(vm, vmTO); + assertEquals(1, vmTO.getNetworkIdToNetworkNameMap().size()); + assertEquals("D3-A2-Z1-V4-S5", vmTO.getNetworkIdToNetworkNameMap().get(5L)); + } + @Test public void testOrchestrateStartNonNullPodId() throws Exception { VMInstanceVO vmInstance = new VMInstanceVO(); ReflectionTestUtils.setField(vmInstance, "id", 1L); + ReflectionTestUtils.setField(vmInstance, "accountId", 1L); ReflectionTestUtils.setField(vmInstance, "uuid", "vm-uuid"); ReflectionTestUtils.setField(vmInstance, "serviceOfferingId", 2L); ReflectionTestUtils.setField(vmInstance, "instanceName", "myVm"); @@ -955,6 +1020,7 @@ public void testOrchestrateStartNonNullPodId() throws Exception { User user = mock(User.class); Account account = mock(Account.class); + Account owner = mock(Account.class); ReservationContext ctx = mock(ReservationContext.class); @@ -978,12 +1044,13 @@ public void testOrchestrateStartNonNullPodId() throws Exception { doReturn(vmGuru).when(virtualMachineManagerImpl).getVmGuru(vmInstance); Ternary start = new Ternary<>(vmInstance, ctx, work); - Mockito.doReturn(start).when(virtualMachineManagerImpl).changeToStartState(vmGuru, vmInstance, user, account); + Mockito.doReturn(start).when(virtualMachineManagerImpl).changeToStartState(vmGuru, vmInstance, user, account, owner, serviceOffering, template); when(ctx.getJournal()).thenReturn(Mockito.mock(Journal.class)); when(serviceOfferingDaoMock.findById(vmInstance.getId(), vmInstance.getServiceOfferingId())).thenReturn(serviceOffering); + when(_entityMgr.findById(Account.class, vmInstance.getAccountId())).thenReturn(owner); when(_entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vmInstance.getTemplateId())).thenReturn(template); Host destHost = mock(Host.class); @@ -1035,6 +1102,7 @@ public void testOrchestrateStartNonNullPodId() throws Exception { public void testOrchestrateStartNullPodId() throws Exception { VMInstanceVO vmInstance = new VMInstanceVO(); ReflectionTestUtils.setField(vmInstance, "id", 1L); + ReflectionTestUtils.setField(vmInstance, "accountId", 1L); ReflectionTestUtils.setField(vmInstance, "uuid", "vm-uuid"); ReflectionTestUtils.setField(vmInstance, "serviceOfferingId", 2L); ReflectionTestUtils.setField(vmInstance, "instanceName", "myVm"); @@ -1048,6 +1116,7 @@ public void testOrchestrateStartNullPodId() throws Exception { User user = mock(User.class); Account account = mock(Account.class); + Account owner = mock(Account.class); ReservationContext ctx = mock(ReservationContext.class); @@ -1071,12 +1140,13 @@ public void testOrchestrateStartNullPodId() throws Exception { doReturn(vmGuru).when(virtualMachineManagerImpl).getVmGuru(vmInstance); Ternary start = new Ternary<>(vmInstance, ctx, work); - Mockito.doReturn(start).when(virtualMachineManagerImpl).changeToStartState(vmGuru, vmInstance, user, account); + Mockito.doReturn(start).when(virtualMachineManagerImpl).changeToStartState(vmGuru, vmInstance, user, account, owner, serviceOffering, template); when(ctx.getJournal()).thenReturn(Mockito.mock(Journal.class)); when(serviceOfferingDaoMock.findById(vmInstance.getId(), vmInstance.getServiceOfferingId())).thenReturn(serviceOffering); + when(_entityMgr.findById(Account.class, vmInstance.getAccountId())).thenReturn(owner); when(_entityMgr.findByIdIncludingRemoved(VirtualMachineTemplate.class, vmInstance.getTemplateId())).thenReturn(template); Host destHost = mock(Host.class); 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 fde93238451a..581f78990691 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 @@ -367,6 +367,10 @@ public Mode getMode() { return mode; } + public void setAccountId(long accountId) { + this.accountId = accountId; + } + @Override public long getAccountId() { return accountId; diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDao.java b/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDao.java new file mode 100644 index 000000000000..0fc775377112 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDao.java @@ -0,0 +1,30 @@ +// 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.dao; + +import com.cloud.network.element.NsxProviderVO; +import com.cloud.utils.db.GenericDao; + +import java.util.List; + +public interface NsxProviderDao extends GenericDao { + NsxProviderVO findByZoneId(long zoneId); + + NsxProviderVO findByUuid(String uuid); + + List findAll(); +} diff --git a/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDaoImpl.java b/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDaoImpl.java new file mode 100644 index 000000000000..cf7b5d405c00 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/dao/NsxProviderDaoImpl.java @@ -0,0 +1,65 @@ +// 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.dao; + +import com.cloud.network.element.NsxProviderVO; +import com.cloud.utils.db.DB; +import com.cloud.utils.db.GenericDaoBase; +import com.cloud.utils.db.SearchBuilder; +import com.cloud.utils.db.SearchCriteria; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +@DB() +public class NsxProviderDaoImpl extends GenericDaoBase + implements NsxProviderDao { + + final SearchBuilder allFieldsSearch; + + public NsxProviderDaoImpl() { + super(); + allFieldsSearch = createSearchBuilder(); + allFieldsSearch.and("id", allFieldsSearch.entity().getId(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("uuid", allFieldsSearch.entity().getUuid(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("hostname", allFieldsSearch.entity().getHostname(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("provider_name", allFieldsSearch.entity().getProviderName(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("tier0_gateway", allFieldsSearch.entity().getTier0Gateway(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("zone_id", allFieldsSearch.entity().getZoneId(), + SearchCriteria.Op.EQ); + allFieldsSearch.and("edge_cluster", allFieldsSearch.entity().getEdgeCluster(), + SearchCriteria.Op.EQ); + allFieldsSearch.done(); + } + @Override + public NsxProviderVO findByZoneId(long zoneId) { + SearchCriteria sc = allFieldsSearch.create(); + sc.setParameters("zone_id", zoneId); + return findOneBy(sc); + } + + @Override + public List findAll() { + return listAll(); + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/element/NsxProviderVO.java b/engine/schema/src/main/java/com/cloud/network/element/NsxProviderVO.java new file mode 100644 index 000000000000..f08e08b1ca04 --- /dev/null +++ b/engine/schema/src/main/java/com/cloud/network/element/NsxProviderVO.java @@ -0,0 +1,285 @@ +// 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.element; + +import com.cloud.network.nsx.NsxProvider; +import com.cloud.utils.db.Encrypt; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import javax.persistence.Table; +import java.util.Date; +import java.util.UUID; + +@Entity +@Table(name = "nsx_providers") +public class NsxProviderVO implements NsxProvider { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + long id; + + @Column(name = "zone_id") + private long zoneId; + + @Column(name = "host_id") + private long hostId; + + @Column(name = "uuid") + private String uuid; + + @Column(name = "provider_name") + private String providerName; + + @Column(name = "hostname") + private String hostname; + + @Column(name = "port") + private String port = "443"; + + @Column(name = "username") + private String username; + + @Encrypt + @Column(name = "password") + private String password; + + @Column(name = "tier0_gateway") + private String tier0Gateway; + + @Column(name = "edge_cluster") + private String edgeCluster; + + @Column(name = "transport_zone") + private String transportZone; + + @Column(name = "created") + private Date created; + + @Column(name = "removed") + private Date removed; + public NsxProviderVO() { + this.uuid = UUID.randomUUID().toString(); + } + + @Override + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + @Override + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + @Override + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + @Override + public String getProviderName() { + return providerName; + } + + public void setProviderName(String providerName) { + this.providerName = providerName; + } + + @Override + public String getHostname() { + return hostname; + } + + public void setPort(String port) { + this.port = port; + } + + @Override + public String getPort() { + return port; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getTier0Gateway() { + return tier0Gateway; + } + + public void setTier0Gateway(String tier0Gateway) { + this.tier0Gateway = tier0Gateway; + } + + public String getEdgeCluster() { + return edgeCluster; + } + + public void setEdgeCluster(String edgeCluster) { + this.edgeCluster = edgeCluster; + } + + public String getTransportZone() { + return transportZone; + } + + public void setTransportZone(String transportZone) { + this.transportZone = transportZone; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public Date getRemoved() { + return removed; + } + + public void setRemoved(Date removed) { + this.removed = removed; + } + + public static final class Builder { + private long zoneId; + private long hostId; + private String providerName; + private String hostname; + private String port; + private String username; + private String password; + private String tier0Gateway; + private String edgeCluster; + private String transportZone; + + + public Builder() { + // Default constructor + } + + public Builder setZoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder setHostId(long hostId) { + this.hostId = hostId; + return this; + } + + public Builder setProviderName(String providerName) { + this.providerName = providerName; + return this; + } + + public Builder setHostname(String hostname) { + this.hostname = hostname; + return this; + } + + public Builder setPort(String port) { + this.port = port; + return this; + } + + public Builder setUsername(String username) { + this.username = username; + return this; + } + + public Builder setPassword(String password) { + this.password = password; + return this; + } + + public Builder setTier0Gateway(String tier0Gateway) { + this.tier0Gateway = tier0Gateway; + return this; + } + + public Builder setEdgeCluster(String edgeCluster) { + this.edgeCluster = edgeCluster; + return this; + } + + public Builder setTransportZone(String transportZone) { + this.transportZone = transportZone; + return this; + } + public NsxProviderVO build() { + NsxProviderVO provider = new NsxProviderVO(); + provider.setZoneId(this.zoneId); + provider.setHostId(this.hostId); + provider.setUuid(UUID.randomUUID().toString()); + provider.setProviderName(this.providerName); + provider.setHostname(this.hostname); + provider.setPort(this.port); + provider.setUsername(this.username); + provider.setPassword(this.password); + provider.setTier0Gateway(this.tier0Gateway); + provider.setEdgeCluster(this.edgeCluster); + provider.setTransportZone(this.transportZone); + provider.setCreated(new Date()); + return provider; + } + } +} diff --git a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java index aa26f16568a9..350dda3f3b8b 100644 --- a/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/network/vpc/VpcOfferingVO.java @@ -58,6 +58,12 @@ public class VpcOfferingVO implements VpcOffering { @Column(name = "default") boolean isDefault = false; + @Column(name = "for_nsx") + boolean forNsx = false; + + @Column(name = "nsx_mode") + String nsxMode; + @Column(name = GenericDao.REMOVED_COLUMN) Date removed; @@ -144,6 +150,22 @@ public boolean isDefault() { return isDefault; } + public boolean isForNsx() { + return forNsx; + } + + public void setForNsx(boolean forNsx) { + this.forNsx = forNsx; + } + + public String getNsxMode() { + return nsxMode; + } + + public void setNsxMode(String nsxMode) { + this.nsxMode = nsxMode; + } + public void setUniqueName(String uniqueName) { this.uniqueName = uniqueName; } diff --git a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java index ae5e6fb95ea9..b2fabf2e3cd3 100644 --- a/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java +++ b/engine/schema/src/main/java/com/cloud/offerings/NetworkOfferingVO.java @@ -136,6 +136,12 @@ public class NetworkOfferingVO implements NetworkOffering { @Column(name = "for_tungsten") boolean forTungsten = false; + @Column(name = "for_nsx") + boolean forNsx = false; + + @Column(name = "nsx_mode") + String nsxMode; + @Column(name = "egress_default_policy") boolean egressdefaultpolicy; @@ -195,6 +201,24 @@ public void setForTungsten(boolean forTungsten) { this.forTungsten = forTungsten; } + @Override + public boolean isForNsx() { + return forNsx; + } + + public void setForNsx(boolean forNsx) { + this.forNsx = forNsx; + } + + @Override + public String getNsxMode() { + return nsxMode; + } + + public void setNsxMode(String nsxMode) { + this.nsxMode = nsxMode; + } + @Override public long getId() { return id; diff --git a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java index d8dfbb6f0762..e12859ea8d6c 100644 --- a/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java +++ b/engine/schema/src/main/java/com/cloud/storage/VolumeVO.java @@ -182,6 +182,7 @@ public class VolumeVO implements Volume { @Column(name = "encrypt_format") private String encryptFormat; + // Real Constructor public VolumeVO(Type type, String name, long dcId, long domainId, long accountId, long diskOfferingId, Storage.ProvisioningType provisioningType, long size, diff --git a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java index a60e8f048452..31d64daf147c 100644 --- a/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/storage/dao/VolumeDaoImpl.java @@ -23,9 +23,15 @@ import java.util.Collections; import java.util.Date; import java.util.List; +import java.util.stream.Collectors; import javax.inject.Inject; +import com.cloud.configuration.Resource; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import org.apache.cloudstack.reservation.ReservationVO; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; @@ -71,6 +77,8 @@ public class VolumeDaoImpl extends GenericDaoBase implements Vol protected GenericSearchBuilder secondaryStorageSearch; private final SearchBuilder poolAndPathSearch; @Inject + ReservationDao reservationDao; + @Inject ResourceTagDao _tagsDao; protected static final String SELECT_VM_SQL = "SELECT DISTINCT instance_id from volumes v where v.host_id = ? and v.mirror_state = ?"; @@ -443,6 +451,7 @@ public VolumeDaoImpl() { CountByAccount.and("account", CountByAccount.entity().getAccountId(), SearchCriteria.Op.EQ); CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); CountByAccount.and("displayVolume", CountByAccount.entity().isDisplayVolume(), Op.EQ); + CountByAccount.and("idNIN", CountByAccount.entity().getId(), Op.NIN); CountByAccount.done(); primaryStorageSearch = createSearchBuilder(SumCount.class); @@ -454,6 +463,7 @@ public VolumeDaoImpl() { primaryStorageSearch.and("displayVolume", primaryStorageSearch.entity().isDisplayVolume(), Op.EQ); primaryStorageSearch.and("isRemoved", primaryStorageSearch.entity().getRemoved(), Op.NULL); primaryStorageSearch.and("NotCountStates", primaryStorageSearch.entity().getState(), Op.NIN); + primaryStorageSearch.and("idNIN", primaryStorageSearch.entity().getId(), Op.NIN); primaryStorageSearch.done(); primaryStorageSearch2 = createSearchBuilder(SumCount.class); @@ -468,6 +478,7 @@ public VolumeDaoImpl() { primaryStorageSearch2.and("displayVolume", primaryStorageSearch2.entity().isDisplayVolume(), Op.EQ); primaryStorageSearch2.and("isRemoved", primaryStorageSearch2.entity().getRemoved(), Op.NULL); primaryStorageSearch2.and("NotCountStates", primaryStorageSearch2.entity().getState(), Op.NIN); + primaryStorageSearch2.and("idNIN", primaryStorageSearch2.entity().getId(), Op.NIN); primaryStorageSearch2.done(); secondaryStorageSearch = createSearchBuilder(SumCount.class); @@ -506,15 +517,24 @@ public Pair getCountAndTotalByPool(long poolId) { @Override public Long countAllocatedVolumesForAccount(long accountId) { + List reservations = reservationDao.getReservationsForAccount(accountId, Resource.ResourceType.volume, null); + List reservedResourceIds = reservations.stream().filter(reservation -> reservation.getReservedAmount() > 0).map(ReservationVO::getResourceId).collect(Collectors.toList()); + SearchCriteria sc = CountByAccount.create(); sc.setParameters("account", accountId); - sc.setParameters("state", Volume.State.Destroy, Volume.State.Expunged); + sc.setParameters("state", State.Destroy, State.Expunged); sc.setParameters("displayVolume", 1); + if (CollectionUtils.isNotEmpty(reservedResourceIds)) { + sc.setParameters("idNIN", reservedResourceIds.toArray()); + } return customSearch(sc, null).get(0); } @Override public long primaryStorageUsedForAccount(long accountId, List virtualRouters) { + List reservations = reservationDao.getReservationsForAccount(accountId, Resource.ResourceType.volume, null); + List reservedResourceIds = reservations.stream().filter(reservation -> reservation.getReservedAmount() > 0).map(ReservationVO::getResourceId).collect(Collectors.toList()); + SearchCriteria sc; if (!virtualRouters.isEmpty()) { sc = primaryStorageSearch2.create(); @@ -526,6 +546,9 @@ public long primaryStorageUsedForAccount(long accountId, List virtualRoute sc.setParameters("states", State.Allocated); sc.setParameters("NotCountStates", State.Destroy, State.Expunged); sc.setParameters("displayVolume", 1); + if (CollectionUtils.isNotEmpty(reservedResourceIds)) { + sc.setParameters("idNIN", reservedResourceIds.toArray()); + } List storageSpace = customSearch(sc, null); if (storageSpace != null) { return storageSpace.get(0).sum; @@ -863,4 +886,14 @@ public List listAllocatedVolumesForAccountDiskOfferingIdsAndNotForVms( } return listBy(sc); } + + @Override + public VolumeVO persist(VolumeVO entity) { + return Transaction.execute((TransactionCallback) status -> { + VolumeVO volume = super.persist(entity); + reservationDao.setResourceId(Resource.ResourceType.volume, volume.getId()); + reservationDao.setResourceId(Resource.ResourceType.primary_storage, volume.getId()); + return volume; + }); + } } diff --git a/engine/schema/src/main/java/com/cloud/usage/UsageVO.java b/engine/schema/src/main/java/com/cloud/usage/UsageVO.java index 10b295f593cf..50884e3c0120 100644 --- a/engine/schema/src/main/java/com/cloud/usage/UsageVO.java +++ b/engine/schema/src/main/java/com/cloud/usage/UsageVO.java @@ -17,6 +17,7 @@ package com.cloud.usage; import java.util.Date; +import java.util.TimeZone; import javax.persistence.Column; import javax.persistence.Entity; @@ -27,9 +28,11 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; +import com.cloud.utils.DateUtil; import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.usage.Usage; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; +import org.apache.commons.lang3.StringUtils; @Entity @Table(name = "cloud_usage") @@ -400,6 +403,12 @@ public void setHidden(boolean hidden) { @Override public String toString() { - return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "usageId", "usageType", "startDate", "endDate"); + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "id", "usageId", "usageType"); + } + + public String toString(TimeZone timeZone) { + String startDateString = DateUtil.displayDateInTimezone(timeZone, getStartDate()); + String endDateString = DateUtil.displayDateInTimezone(timeZone, getEndDate()); + return String.format("%s,\"startDate\":\"%s\",\"endDate\":\"%s\"}", StringUtils.chop(this.toString()), startDateString, endDateString); } } diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java index 68f57329d779..23c26ea07180 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDao.java @@ -44,6 +44,8 @@ public interface NicDao extends GenericDao { NicVO findByNetworkIdAndType(long networkId, VirtualMachine.Type vmType); + NicVO findByNetworkIdAndTypeIncludingRemoved(long networkId, VirtualMachine.Type vmType); + NicVO findByIp4AddressAndNetworkId(String ip4Address, long networkId); NicVO findByNetworkIdAndMacAddress(long networkId, String mac); diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java index 59d2417b073c..3eee1d4e749d 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/NicDaoImpl.java @@ -176,12 +176,21 @@ public NicVO findByInstanceIdAndNetworkIdIncludingRemoved(long networkId, long i return findOneIncludingRemovedBy(sc); } - @Override - public NicVO findByNetworkIdAndType(long networkId, VirtualMachine.Type vmType) { + private NicVO findByNetworkIdAndTypeInternal(long networkId, VirtualMachine.Type vmType, boolean includingRemoved) { SearchCriteria sc = AllFieldsSearch.create(); sc.setParameters("network", networkId); sc.setParameters("vmType", vmType); - return findOneBy(sc); + return includingRemoved ? findOneIncludingRemovedBy(sc) : findOneBy(sc); + } + + @Override + public NicVO findByNetworkIdAndType(long networkId, VirtualMachine.Type vmType) { + return findByNetworkIdAndTypeInternal(networkId, vmType, false); + } + + @Override + public NicVO findByNetworkIdAndTypeIncludingRemoved(long networkId, VirtualMachine.Type vmType) { + return findByNetworkIdAndTypeInternal(networkId, vmType, true); } @Override diff --git a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java index f4ce01afef34..536779125e24 100644 --- a/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java +++ b/engine/schema/src/main/java/com/cloud/vm/dao/UserVmDaoImpl.java @@ -26,10 +26,16 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.configuration.Resource; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import org.apache.cloudstack.reservation.ReservationVO; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.commons.collections.CollectionUtils; import com.cloud.network.Network; @@ -91,6 +97,8 @@ public class UserVmDaoImpl extends GenericDaoBase implements Use NetworkDao networkDao; @Inject NetworkOfferingServiceMapDao networkOfferingServiceMapDao; + @Inject + ReservationDao reservationDao; private static final String LIST_PODS_HAVING_VMS_FOR_ACCOUNT = "SELECT pod_id FROM cloud.vm_instance WHERE data_center_id = ? AND account_id = ? AND pod_id IS NOT NULL AND (state = 'Running' OR state = 'Stopped') " @@ -198,6 +206,7 @@ void init() { CountByAccount.and("type", CountByAccount.entity().getType(), SearchCriteria.Op.EQ); CountByAccount.and("state", CountByAccount.entity().getState(), SearchCriteria.Op.NIN); CountByAccount.and("displayVm", CountByAccount.entity().isDisplayVm(), SearchCriteria.Op.EQ); + CountByAccount.and("idNIN", CountByAccount.entity().getId(), SearchCriteria.Op.NIN); CountByAccount.done(); CountActiveAccount = createSearchBuilder(Long.class); @@ -697,6 +706,9 @@ public String getQueryBatchAppender(int count) { @Override public Long countAllocatedVMsForAccount(long accountId, boolean runningVMsonly) { + List reservations = reservationDao.getReservationsForAccount(accountId, Resource.ResourceType.user_vm, null); + List reservedResourceIds = reservations.stream().filter(reservation -> reservation.getReservedAmount() > 0).map(ReservationVO::getResourceId).collect(Collectors.toList()); + SearchCriteria sc = CountByAccount.create(); sc.setParameters("account", accountId); sc.setParameters("type", VirtualMachine.Type.User); @@ -705,6 +717,11 @@ public Long countAllocatedVMsForAccount(long accountId, boolean runningVMsonly) else sc.setParameters("state", new Object[] {State.Destroyed, State.Error, State.Expunging}); sc.setParameters("displayVm", 1); + + if (CollectionUtils.isNotEmpty(reservedResourceIds)) { + sc.setParameters("idNIN", reservedResourceIds.toArray()); + } + return customSearch(sc, null).get(0); } @@ -792,4 +809,15 @@ public List listByIds(List ids) { sc.setParameters("ids", ids.toArray()); return listBy(sc); } + + @Override + public UserVmVO persist(UserVmVO entity) { + return Transaction.execute((TransactionCallback) status -> { + UserVmVO userVM = super.persist(entity); + reservationDao.setResourceId(Resource.ResourceType.user_vm, userVM.getId()); + reservationDao.setResourceId(Resource.ResourceType.cpu, userVM.getId()); + reservationDao.setResourceId(Resource.ResourceType.memory, userVM.getId()); + return userVM; + }); + } } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/reservation/ReservationVO.java b/engine/schema/src/main/java/org/apache/cloudstack/reservation/ReservationVO.java index 9ebdfb8042be..df888312a92e 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/reservation/ReservationVO.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/reservation/ReservationVO.java @@ -51,6 +51,9 @@ public class ReservationVO implements ResourceReservation { @Column(name = "tag") String tag; + @Column(name = "resource_id") + Long resourceId; + @Column(name = "amount") long amount; @@ -58,8 +61,8 @@ protected ReservationVO() { } public ReservationVO(Long accountId, Long domainId, Resource.ResourceType resourceType, String tag, Long delta) { - if (delta == null || delta <= 0) { - throw new CloudRuntimeException("resource reservations can not be made for no resources"); + if (delta == null) { + throw new CloudRuntimeException("resource reservations can not be made for null resources"); } this.accountId = accountId; this.domainId = domainId; @@ -101,4 +104,14 @@ public String getTag() { public Long getReservedAmount() { return amount; } + + @Override + public Long getResourceId() { + return resourceId; + } + + public void setResourceId(long resourceId) { + this.resourceId = resourceId; + } + } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDao.java b/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDao.java index 478a9087c44c..4b87c71e2e21 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDao.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDao.java @@ -23,7 +23,12 @@ import com.cloud.configuration.Resource; import com.cloud.utils.db.GenericDao; +import java.util.List; + public interface ReservationDao extends GenericDao { long getAccountReservation(Long account, Resource.ResourceType resourceType, String tag); long getDomainReservation(Long domain, Resource.ResourceType resourceType, String tag); + void setResourceId(Resource.ResourceType type, Long resourceId); + List getResourceIds(long accountId, Resource.ResourceType type); + List getReservationsForAccount(long accountId, Resource.ResourceType type, String tag); } diff --git a/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDaoImpl.java b/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDaoImpl.java index 011262afe392..af0bd22619fc 100644 --- a/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDaoImpl.java +++ b/engine/schema/src/main/java/org/apache/cloudstack/reservation/dao/ReservationDaoImpl.java @@ -19,27 +19,51 @@ package org.apache.cloudstack.reservation.dao; import java.util.List; +import java.util.stream.Collectors; +import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.reservation.ReservationVO; import com.cloud.configuration.Resource; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; +import org.apache.cloudstack.user.ResourceReservation; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; public class ReservationDaoImpl extends GenericDaoBase implements ReservationDao { + protected transient Logger logger = LogManager.getLogger(getClass()); private static final String RESOURCE_TYPE = "resourceType"; private static final String RESOURCE_TAG = "resourceTag"; + private static final String RESOURCE_ID = "resourceId"; private static final String ACCOUNT_ID = "accountId"; private static final String DOMAIN_ID = "domainId"; + private final SearchBuilder listResourceByAccountAndTypeSearch; private final SearchBuilder listAccountAndTypeSearch; private final SearchBuilder listAccountAndTypeAndNoTagSearch; private final SearchBuilder listDomainAndTypeSearch; private final SearchBuilder listDomainAndTypeAndNoTagSearch; + private final SearchBuilder listResourceByAccountAndTypeAndNoTagSearch; public ReservationDaoImpl() { + + listResourceByAccountAndTypeSearch = createSearchBuilder(); + listResourceByAccountAndTypeSearch.and(ACCOUNT_ID, listResourceByAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + listResourceByAccountAndTypeSearch.and(RESOURCE_TYPE, listResourceByAccountAndTypeSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + listResourceByAccountAndTypeSearch.and(RESOURCE_ID, listResourceByAccountAndTypeSearch.entity().getResourceId(), SearchCriteria.Op.NNULL); + listResourceByAccountAndTypeSearch.and(RESOURCE_TAG, listResourceByAccountAndTypeSearch.entity().getTag(), SearchCriteria.Op.EQ); + listResourceByAccountAndTypeSearch.done(); + + listResourceByAccountAndTypeAndNoTagSearch = createSearchBuilder(); + listResourceByAccountAndTypeAndNoTagSearch.and(ACCOUNT_ID, listResourceByAccountAndTypeAndNoTagSearch.entity().getAccountId(), SearchCriteria.Op.EQ); + listResourceByAccountAndTypeAndNoTagSearch.and(RESOURCE_TYPE, listResourceByAccountAndTypeAndNoTagSearch.entity().getResourceType(), SearchCriteria.Op.EQ); + listResourceByAccountAndTypeAndNoTagSearch.and(RESOURCE_ID, listResourceByAccountAndTypeAndNoTagSearch.entity().getResourceId(), SearchCriteria.Op.NNULL); + listResourceByAccountAndTypeAndNoTagSearch.and(RESOURCE_TAG, listResourceByAccountAndTypeAndNoTagSearch.entity().getTag(), SearchCriteria.Op.NULL); + listResourceByAccountAndTypeAndNoTagSearch.done(); + listAccountAndTypeSearch = createSearchBuilder(); listAccountAndTypeSearch.and(ACCOUNT_ID, listAccountAndTypeSearch.entity().getAccountId(), SearchCriteria.Op.EQ); listAccountAndTypeSearch.and(RESOURCE_TYPE, listAccountAndTypeSearch.entity().getResourceType(), SearchCriteria.Op.EQ); @@ -98,4 +122,43 @@ public long getDomainReservation(Long domainId, Resource.ResourceType resourceTy } return total; } + + @Override + public void setResourceId(Resource.ResourceType type, Long resourceId) { + Object obj = CallContext.current().getContextParameter(String.format("%s-%s", ResourceReservation.class.getSimpleName(), type.getName())); + if (obj instanceof List) { + try { + List reservationIds = (List)obj; + for (Long reservationId : reservationIds) { + ReservationVO reservation = findById(reservationId); + if (reservation != null) { + reservation.setResourceId(resourceId); + persist(reservation); + } + } + } catch (Exception e) { + logger.warn("Failed to persist reservation for resource type " + type.getName() + " for resource id " + resourceId, e); + } + } + } + + @Override + public List getResourceIds(long accountId, Resource.ResourceType type) { + SearchCriteria sc = listResourceByAccountAndTypeSearch.create(); + sc.setParameters(ACCOUNT_ID, accountId); + sc.setParameters(RESOURCE_TYPE, type); + return listBy(sc).stream().map(ReservationVO::getResourceId).collect(Collectors.toList()); + } + + @Override + public List getReservationsForAccount(long accountId, Resource.ResourceType type, String tag) { + SearchCriteria sc = tag == null ? + listResourceByAccountAndTypeAndNoTagSearch.create() : listResourceByAccountAndTypeSearch.create(); + sc.setParameters(ACCOUNT_ID, accountId); + sc.setParameters(RESOURCE_TYPE, type); + if (tag != null) { + sc.setParameters(RESOURCE_TAG, tag); + } + return listBy(sc); + } } diff --git a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml index 5d9583831610..9fa01c28f5f0 100644 --- a/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml +++ b/engine/schema/src/main/resources/META-INF/cloudstack/core/spring-engine-schema-core-daos-context.xml @@ -136,6 +136,7 @@ + diff --git a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql index cc54e637d885..3ebab6b15f24 100644 --- a/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql +++ b/engine/schema/src/main/resources/META-INF/db/schema-41900to42000.sql @@ -29,8 +29,43 @@ DROP INDEX `i_resource_count__type_domaintId`, ADD UNIQUE INDEX `i_resource_count__type_tag_accountId` (`type`,`tag`,`account_id`), ADD UNIQUE INDEX `i_resource_count__type_tag_domaintId` (`type`,`tag`,`domain_id`); + +ALTER TABLE `cloud`.`resource_reservation` + ADD COLUMN `resource_id` bigint unsigned NULL; + +ALTER TABLE `cloud`.`resource_reservation` + MODIFY COLUMN `amount` bigint NOT NULL; + + -- Update Default System offering for Router to 512MiB UPDATE `cloud`.`service_offering` SET ram_size = 512 WHERE unique_name IN ("Cloud.Com-SoftwareRouter", "Cloud.Com-SoftwareRouter-Local", "Cloud.Com-InternalLBVm", "Cloud.Com-InternalLBVm-Local", "Cloud.Com-ElasticLBVm", "Cloud.Com-ElasticLBVm-Local") - AND system_use = 1 AND ram_size < 512; + AND system_use = 1 AND ram_size < 512; + +-- NSX Plugin -- +CREATE TABLE `cloud`.`nsx_providers` ( + `id` bigint unsigned NOT NULL auto_increment COMMENT 'id', + `uuid` varchar(40), + `zone_id` bigint unsigned NOT NULL COMMENT 'Zone ID', + `host_id` bigint unsigned NOT NULL COMMENT 'Host ID', + `provider_name` varchar(40), + `hostname` varchar(255) NOT NULL, + `port` varchar(255), + `username` varchar(255) NOT NULL, + `password` varchar(255) NOT NULL, + `tier0_gateway` varchar(255), + `edge_cluster` varchar(255), + `transport_zone` varchar(255), + `created` datetime NOT NULL COMMENT 'date created', + `removed` datetime COMMENT 'date removed if not null', + PRIMARY KEY (`id`), + CONSTRAINT `fk_nsx_providers__zone_id` FOREIGN KEY `fk_nsx_providers__zone_id` (`zone_id`) REFERENCES `data_center`(`id`) ON DELETE CASCADE, + INDEX `i_nsx_providers__zone_id`(`zone_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- NSX Plugin -- +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.network_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','for_nsx', 'int(1) unsigned DEFAULT "0" COMMENT "is nsx enabled for the resource"'); +CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc_offerings','nsx_mode', 'varchar(32) COMMENT "mode in which the network would route traffic"'); diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql index 8ba291e154cd..bae73deda329 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.network_offering_view.sql @@ -60,6 +60,8 @@ SELECT `network_offerings`.`supports_vm_autoscaling` AS `supports_vm_autoscaling`, `network_offerings`.`for_vpc` AS `for_vpc`, `network_offerings`.`for_tungsten` AS `for_tungsten`, + `network_offerings`.`for_nsx` AS `for_nsx`, + `network_offerings`.`nsx_mode` AS `nsx_mode`, `network_offerings`.`service_package_id` AS `service_package_id`, GROUP_CONCAT(DISTINCT(domain.id)) AS domain_id, GROUP_CONCAT(DISTINCT(domain.uuid)) AS domain_uuid, diff --git a/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql index cb762a578839..9aca869b510f 100644 --- a/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql +++ b/engine/schema/src/main/resources/META-INF/db/views/cloud.vpc_offering_view.sql @@ -17,7 +17,6 @@ -- cloud.vpc_offering_view source - DROP VIEW IF EXISTS `cloud`.`vpc_offering_view`; CREATE VIEW `cloud`.`vpc_offering_view` AS @@ -29,6 +28,8 @@ select `vpc_offerings`.`display_text` AS `display_text`, `vpc_offerings`.`state` AS `state`, `vpc_offerings`.`default` AS `default`, + `vpc_offerings`.`for_nsx` AS `for_nsx`, + `vpc_offerings`.`nsx_mode` AS `nsx_mode`, `vpc_offerings`.`created` AS `created`, `vpc_offerings`.`removed` AS `removed`, `vpc_offerings`.`service_offering_id` AS `service_offering_id`, diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java index 8d35bb9f4342..ff41a8141d76 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaAlertManagerImpl.java @@ -29,6 +29,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.utils.DateUtil; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.quota.constant.QuotaConfig; import org.apache.cloudstack.quota.constant.QuotaConfig.QuotaEmailTemplateTypes; @@ -156,9 +157,9 @@ public void checkAndSendQuotaAlertEmails() { if (account == null) { continue; // the account is removed } - if (logger.isDebugEnabled()) { - logger.debug("checkAndSendQuotaAlertEmails: Check id=" + account.getId() + " bal=" + accountBalance + ", alertDate=" + alertDate + ", lockable=" + lockable); - } + logger.debug("checkAndSendQuotaAlertEmails: Check id={} bal={}, alertDate={}, lockable={}", account.getId(), + accountBalance, DateUtil.displayDateInTimezone(QuotaManagerImpl.getUsageAggregationTimeZone(), alertDate), + lockable); if (accountBalance.compareTo(zeroBalance) < 0) { if (_lockAccountEnforcement && (lockable == 1)) { if (_quotaManager.isLockable(account)) { diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java index 4293415755a7..9c15a47444ae 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/QuotaManagerImpl.java @@ -49,9 +49,9 @@ import org.apache.cloudstack.utils.bytescale.ByteScaleUtils; import org.apache.cloudstack.utils.jsinterpreter.JsInterpreter; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; -import org.apache.cloudstack.utils.usage.UsageUtils; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.springframework.stereotype.Component; @@ -85,8 +85,7 @@ public class QuotaManagerImpl extends ManagerBase implements QuotaManager { @Inject protected PresetVariableHelper presetVariableHelper; - private TimeZone _usageTimezone; - private int _aggregationDuration = 0; + private static TimeZone usageAggregationTimeZone = TimeZone.getTimeZone("GMT"); static final BigDecimal GiB_DECIMAL = BigDecimal.valueOf(ByteScaleUtils.GiB); List lockablesAccountTypes = Arrays.asList(Account.Type.NORMAL, Account.Type.DOMAIN_ADMIN); @@ -112,24 +111,16 @@ public boolean configure(String name, Map params) throws Configu mergeConfigs(configs, params); } - String aggregationRange = configs.get("usage.stats.job.aggregation.range"); - String timeZoneStr = configs.get("usage.aggregation.timezone"); - - if (timeZoneStr == null) { - timeZoneStr = "GMT"; - } - _usageTimezone = TimeZone.getTimeZone(timeZoneStr); - - _aggregationDuration = Integer.parseInt(aggregationRange); - if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) { - logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN); - _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN; - } - logger.info("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration); + String usageAggregationTimeZoneStr = ObjectUtils.defaultIfNull(configs.get("usage.aggregation.timezone"), "GMT"); + usageAggregationTimeZone = TimeZone.getTimeZone(usageAggregationTimeZoneStr); return true; } + public static TimeZone getUsageAggregationTimeZone() { + return usageAggregationTimeZone; + } + @Override public boolean start() { if (logger.isInfoEnabled()) { @@ -158,8 +149,9 @@ protected void processQuotaBalanceForAccount(AccountVO accountVo, List creditsReceived = _quotaBalanceDao.findCreditBalance(accountId, domainId, startDate, endDate); - logger.debug(String.format("Account [%s] has [%s] credit entries before [%s].", accountToString, creditsReceived.size(), endDate)); + logger.debug("Account [{}] has [{}] credit entries before [{}].", accountToString, creditsReceived.size(), + DateUtil.displayDateInTimezone(usageAggregationTimeZone, endDate)); BigDecimal aggregatedUsage = BigDecimal.ZERO; - logger.debug(String.format("Aggregating the account [%s] credit entries before [%s].", accountToString, endDate)); + logger.debug("Aggregating the account [{}] credit entries before [{}].", accountToString, + DateUtil.displayDateInTimezone(usageAggregationTimeZone, endDate)); for (QuotaBalanceVO credit : creditsReceived) { aggregatedUsage = aggregatedUsage.add(credit.getCreditBalance()); } - logger.debug(String.format("The aggregation of the account [%s] credit entries before [%s] resulted in the value [%s].", accountToString, endDate, aggregatedUsage)); + logger.debug("The aggregation of the account [{}] credit entries before [{}] resulted in the value [{}].", + accountToString, DateUtil.displayDateInTimezone(usageAggregationTimeZone, endDate), aggregatedUsage); return aggregatedUsage; } @@ -306,7 +301,7 @@ protected List getPendingUsageRecordsForQuotaAggregation(AccountVO acco protected List createQuotaUsagesAccordingToQuotaTariffs(AccountVO account, List usageRecords, Map, Boolean>> mapQuotaTariffsPerUsageType) { String accountToString = account.reflectionToString(); - logger.info(String.format("Calculating quota usage of [%s] usage records for account [%s].", usageRecords.size(), accountToString)); + logger.info("Calculating quota usage of [{}] usage records for account [{}].", usageRecords.size(), accountToString); List> pairsUsageAndQuotaUsage = new ArrayList<>(); @@ -314,7 +309,7 @@ protected List createQuotaUsagesAccordingToQuotaTariffs(AccountVO for (UsageVO usageRecord : usageRecords) { int usageType = usageRecord.getUsageType(); - if (Boolean.FALSE.equals(shouldCalculateUsageRecord(account,usageRecord))) { + if (!shouldCalculateUsageRecord(account, usageRecord)) { pairsUsageAndQuotaUsage.add(new Pair<>(usageRecord, null)); continue; } @@ -339,8 +334,8 @@ protected List createQuotaUsagesAccordingToQuotaTariffs(AccountVO protected boolean shouldCalculateUsageRecord(AccountVO accountVO, UsageVO usageRecord) { if (Boolean.FALSE.equals(QuotaConfig.QuotaAccountEnabled.valueIn(accountVO.getAccountId()))) { - logger.debug(String.format("Considering usage record [%s] as calculated and skipping it because account [%s] has the quota plugin disabled.", - usageRecord, accountVO.reflectionToString())); + logger.debug("Considering usage record [{}] as calculated and skipping it because account [{}] has the quota plugin disabled.", + usageRecord.toString(usageAggregationTimeZone), accountVO.reflectionToString()); return false; } return true; @@ -366,9 +361,8 @@ protected List persistUsagesAndQuotaUsagesAndRetrievePersistedQuot protected BigDecimal aggregateQuotaTariffsValues(UsageVO usageRecord, List quotaTariffs, boolean hasAnyQuotaTariffWithActivationRule, JsInterpreter jsInterpreter, String accountToString) { - String usageRecordToString = usageRecord.toString(); - logger.debug(String.format("Validating usage record [%s] for account [%s] against [%s] quota tariffs.", usageRecordToString, accountToString, - quotaTariffs.size())); + String usageRecordToString = usageRecord.toString(usageAggregationTimeZone); + logger.debug("Validating usage record [{}] for account [{}] against [{}] quota tariffs.", usageRecordToString, accountToString, quotaTariffs.size()); PresetVariables presetVariables = getPresetVariables(hasAnyQuotaTariffWithActivationRule, usageRecord); BigDecimal aggregatedQuotaTariffsValue = BigDecimal.ZERO; @@ -406,7 +400,7 @@ protected PresetVariables getPresetVariables(boolean hasAnyQuotaTariffWithActiva protected BigDecimal getQuotaTariffValueToBeApplied(QuotaTariffVO quotaTariff, JsInterpreter jsInterpreter, PresetVariables presetVariables) { String activationRule = quotaTariff.getActivationRule(); BigDecimal quotaTariffValue = quotaTariff.getCurrencyValue(); - String quotaTariffToString = quotaTariff.toString(); + String quotaTariffToString = quotaTariff.toString(usageAggregationTimeZone); if (StringUtils.isEmpty(activationRule)) { logger.debug(String.format("Quota tariff [%s] does not have an activation rule, therefore we will use the quota tariff value [%s] in the calculation.", @@ -468,10 +462,11 @@ protected boolean isQuotaTariffInPeriodToBeApplied(UsageVO usageRecord, QuotaTar Date quotaTariffEndDate = quotaTariff.getEndDate(); if ((quotaTariffEndDate != null && usageRecordStartDate.after(quotaTariffEndDate)) || usageRecordEndDate.before(quotaTariffStartDate)) { - logger.debug(String.format("Not applying quota tariff [%s] in usage record [%s] of account [%s] due to it is out of the period to be applied. Period of the usage" - + " record [startDate: %s, endDate: %s], period of the quota tariff [startDate: %s, endDate: %s].", quotaTariff, usageRecord.toString(), accountToString, - DateUtil.getOutputString(usageRecordStartDate), DateUtil.getOutputString(usageRecordEndDate), DateUtil.getOutputString(quotaTariffStartDate), - DateUtil.getOutputString(quotaTariffEndDate))); + logger.debug("Not applying quota tariff [{}] in usage record [{}] of account [{}] due to it is out of the period to be applied. Period of the usage" + + " record [startDate: {}, endDate: {}], period of the quota tariff [startDate: {}, endDate: {}].", quotaTariff.toString(usageAggregationTimeZone), + usageRecord.toString(usageAggregationTimeZone), accountToString, DateUtil.displayDateInTimezone(usageAggregationTimeZone, usageRecordStartDate), + DateUtil.displayDateInTimezone(usageAggregationTimeZone, usageRecordEndDate), DateUtil.displayDateInTimezone(usageAggregationTimeZone, quotaTariffStartDate), + DateUtil.displayDateInTimezone(usageAggregationTimeZone, quotaTariffEndDate)); return false; } @@ -497,11 +492,11 @@ protected Map, Boolean>> createMapQuotaTariffs } protected QuotaUsageVO createQuotaUsageAccordingToUsageUnit(UsageVO usageRecord, BigDecimal aggregatedQuotaTariffsValue, String accountToString) { - String usageRecordToString = usageRecord.toString(); + String usageRecordToString = usageRecord.toString(usageAggregationTimeZone); if (aggregatedQuotaTariffsValue.equals(BigDecimal.ZERO)) { - logger.debug(String.format("Usage record [%s] for account [%s] does not have quota tariffs to be calculated, therefore we will mark it as calculated.", - usageRecordToString, accountToString)); + logger.debug("No tariffs were applied to usage record [{}] of account [{}] or they resulted in 0; We will only mark the usage record as calculated.", + usageRecordToString, accountToString); return null; } diff --git a/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffVO.java b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffVO.java index 7bc2870e36a1..5b07cadb94ae 100644 --- a/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffVO.java +++ b/framework/quota/src/main/java/org/apache/cloudstack/quota/vo/QuotaTariffVO.java @@ -16,11 +16,13 @@ //under the License. package org.apache.cloudstack.quota.vo; +import com.cloud.utils.DateUtil; import org.apache.cloudstack.api.InternalIdentity; import org.apache.cloudstack.quota.constant.QuotaTypes; import org.apache.cloudstack.utils.reflectiontostringbuilderutils.ReflectionToStringBuilderUtils; import com.cloud.utils.db.GenericDao; +import org.apache.commons.lang3.StringUtils; import javax.persistence.Column; import javax.persistence.Entity; @@ -33,6 +35,7 @@ import java.math.BigDecimal; import java.util.Date; +import java.util.TimeZone; import java.util.UUID; @Entity @@ -261,6 +264,12 @@ public boolean setUsageTypeData(int usageType) { @Override public String toString() { - return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "effectiveOn", "endDate"); - }; + return ReflectionToStringBuilderUtils.reflectOnlySelectedFields(this, "uuid", "name", "usageName"); + } + + public String toString(TimeZone timeZone) { + String startDateString = DateUtil.displayDateInTimezone(timeZone, getEffectiveOn()); + String endDateString = DateUtil.displayDateInTimezone(timeZone, getEndDate()); + return String.format("%s,\"startDate\":\"%s\",\"endDate\":\"%s\"}", StringUtils.chop(this.toString()), startDateString, endDateString); + } } diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java index eb667b00c3bc..d7f2832831ab 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/api/response/QuotaResponseBuilderImpl.java @@ -34,6 +34,7 @@ import javax.inject.Inject; +import com.cloud.utils.DateUtil; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.QuotaBalanceCmd; @@ -44,6 +45,7 @@ import org.apache.cloudstack.api.command.QuotaTariffListCmd; import org.apache.cloudstack.api.command.QuotaTariffUpdateCmd; import org.apache.cloudstack.quota.QuotaManager; +import org.apache.cloudstack.quota.QuotaManagerImpl; import org.apache.cloudstack.quota.QuotaService; import org.apache.cloudstack.quota.QuotaStatement; import org.apache.cloudstack.quota.constant.QuotaConfig; @@ -470,12 +472,14 @@ protected void validateEndDateOnCreatingNewQuotaTariff(QuotaTariffVO newQuotaTar } if (endDate.compareTo(startDate) < 0) { - throw new InvalidParameterValueException(String.format("The quota tariff's end date [%s] cannot be less than the start date [%s]", endDate, startDate)); + throw new InvalidParameterValueException(String.format("The quota tariff's end date [%s] cannot be less than the start date [%s].", + endDate, startDate)); } Date now = _quotaService.computeAdjustedTime(new Date()); if (endDate.compareTo(now) < 0) { - throw new InvalidParameterValueException(String.format("The quota tariff's end date [%s] cannot be less than now [%s].", endDate, now)); + throw new InvalidParameterValueException(String.format("The quota tariff's end date [%s] cannot be less than now [%s].", + endDate, now)); } newQuotaTariff.setEndDate(endDate); @@ -487,7 +491,8 @@ public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Doubl QuotaBalanceVO qb = _quotaBalanceDao.findLaterBalanceEntry(accountId, domainId, despositedOn); if (qb != null) { - throw new InvalidParameterValueException("Incorrect deposit date: " + despositedOn + " there are balance entries after this date"); + throw new InvalidParameterValueException(String.format("Incorrect deposit date [%s], as there are balance entries after this date.", + despositedOn)); } QuotaCreditsVO credits = new QuotaCreditsVO(accountId, domainId, new BigDecimal(amount), updatedBy); @@ -500,9 +505,8 @@ public QuotaCreditsResponse addQuotaCredits(Long accountId, Long domainId, Doubl } final boolean lockAccountEnforcement = "true".equalsIgnoreCase(QuotaConfig.QuotaEnableEnforcement.value()); final BigDecimal currentAccountBalance = _quotaBalanceDao.lastQuotaBalance(accountId, domainId, startOfNextDay(new Date(despositedOn.getTime()))); - if (logger.isDebugEnabled()) { - logger.debug("AddQuotaCredits: Depositing " + amount + " on adjusted date " + despositedOn + ", current balance " + currentAccountBalance); - } + logger.debug("Depositing [{}] credits on adjusted date [{}]; current balance is [{}].", amount, + DateUtil.displayDateInTimezone(QuotaManagerImpl.getUsageAggregationTimeZone(), despositedOn), currentAccountBalance); // update quota account with the balance _quotaService.saveQuotaAccount(account, currentAccountBalance, despositedOn); if (lockAccountEnforcement) { @@ -581,9 +585,10 @@ public QuotaBalanceResponse createQuotaLastBalanceResponse(List QuotaBalanceResponse resp = new QuotaBalanceResponse(); BigDecimal lastCredits = new BigDecimal(0); for (QuotaBalanceVO entry : quotaBalance) { - if (logger.isDebugEnabled()) { - logger.debug("createQuotaLastBalanceResponse Date=" + entry.getUpdatedOn() + " balance=" + entry.getCreditBalance() + " credit=" + entry.getCreditsId()); - } + logger.debug("createQuotaLastBalanceResponse Date={} balance={} credit={}", + DateUtil.displayDateInTimezone(QuotaManagerImpl.getUsageAggregationTimeZone(), entry.getUpdatedOn()), + entry.getCreditBalance(), entry.getCreditsId()); + lastCredits = lastCredits.add(entry.getCreditBalance()); } resp.setStartQuota(lastCredits); @@ -638,7 +643,8 @@ public QuotaTariffVO createQuotaTariff(QuotaTariffCreateCmd cmd) { } if (startDate.compareTo(now) < 0) { - throw new InvalidParameterValueException(String.format("The quota tariff's start date [%s] cannot be less than now [%s]", startDate, now)); + throw new InvalidParameterValueException(String.format("The value passed as Quota tariff's start date is in the past: [%s]. " + + "Please, inform a date in the future or do not pass the parameter to use the current date and time.", startDate)); } return persistNewQuotaTariff(null, name, usageType, startDate, cmd.getEntityOwnerId(), endDate, value, description, activationRule); diff --git a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java index bd08f6afd7ef..88a69c47e055 100644 --- a/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java +++ b/plugins/database/quota/src/main/java/org/apache/cloudstack/quota/QuotaServiceImpl.java @@ -51,7 +51,7 @@ import org.apache.cloudstack.quota.vo.QuotaAccountVO; import org.apache.cloudstack.quota.vo.QuotaBalanceVO; import org.apache.cloudstack.quota.vo.QuotaUsageVO; -import org.apache.cloudstack.utils.usage.UsageUtils; +import org.apache.commons.lang3.ObjectUtils; import org.springframework.stereotype.Component; import com.cloud.configuration.Config; @@ -83,7 +83,6 @@ public class QuotaServiceImpl extends ManagerBase implements QuotaService, Confi private QuotaResponseBuilder _respBldr; private TimeZone _usageTimezone; - private int _aggregationDuration = 0; public QuotaServiceImpl() { super(); @@ -92,21 +91,10 @@ public QuotaServiceImpl() { @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); - String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString()); - String aggregationRange = _configDao.getValue(Config.UsageStatsJobAggregationRange.toString()); - if (timeZoneStr == null) { - timeZoneStr = "GMT"; - } + + String timeZoneStr = ObjectUtils.defaultIfNull(_configDao.getValue(Config.UsageAggregationTimezone.toString()), "GMT"); _usageTimezone = TimeZone.getTimeZone(timeZoneStr); - _aggregationDuration = Integer.parseInt(aggregationRange); - if (_aggregationDuration < UsageUtils.USAGE_AGGREGATION_RANGE_MIN) { - logger.warn("Usage stats job aggregation range is to small, using the minimum value of " + UsageUtils.USAGE_AGGREGATION_RANGE_MIN); - _aggregationDuration = UsageUtils.USAGE_AGGREGATION_RANGE_MIN; - } - if (logger.isDebugEnabled()) { - logger.debug("Usage timezone = " + _usageTimezone + " AggregationDuration=" + _aggregationDuration); - } return true; } diff --git a/plugins/database/quota/src/test/java/org/apache/cloudstack/quota/QuotaServiceImplTest.java b/plugins/database/quota/src/test/java/org/apache/cloudstack/quota/QuotaServiceImplTest.java index 0b9d34daa709..fa58c35ea5d5 100644 --- a/plugins/database/quota/src/test/java/org/apache/cloudstack/quota/QuotaServiceImplTest.java +++ b/plugins/database/quota/src/test/java/org/apache/cloudstack/quota/QuotaServiceImplTest.java @@ -99,7 +99,6 @@ public void setup() throws IllegalAccessException, NoSuchFieldException, Configu QuotaResponseBuilderField.set(quotaService, respBldr); Mockito.when(configDao.getValue(Mockito.eq(Config.UsageAggregationTimezone.toString()))).thenReturn("IST"); - Mockito.when(configDao.getValue(Mockito.eq(Config.UsageStatsJobAggregationRange.toString()))).thenReturn("1"); quotaService.configure("randomName", null); } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java index 2c570f64a44c..826c613798a9 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/manager/VmwareManagerImpl.java @@ -462,7 +462,7 @@ private void prepareHost(HostMO hostMo, String privateTrafficLabel) throws Excep } } HypervisorHostHelper.prepareNetwork(vSwitchName, "cloud.private", hostMo, vlanId, null, null, null, 180000, - vsType, portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan, null, null); + vsType, portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan, null, null, null); } } diff --git a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java index 10ad8f2176a1..3a551fce7ebb 100644 --- a/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java +++ b/plugins/hypervisors/vmware/src/main/java/com/cloud/hypervisor/vmware/resource/VmwareResource.java @@ -730,7 +730,7 @@ private Answer execute(SetupPersistentNetworkCommand cmd) { HostMO hostMO = new HostMO(context, host.getMor()); try { - prepareNetworkFromNicInfo(hostMO, cmd.getNic(), false, null); + prepareNetworkFromNicInfo(hostMO, cmd.getNic(), false, null, null); hostname = host.getHyperHostName(); } catch (Exception e) { return new SetupPersistentNetworkAnswer(cmd, false, "failed to setup port-group due to: "+ e.getLocalizedMessage()); @@ -1472,7 +1472,7 @@ private void plugNicCommandInternal(String vmName, VirtualEthernetCardType nicDe deviceNumber++; VirtualDevice nic; - Pair networkInfo = prepareNetworkFromNicInfo(vmMo.getRunningHost(), nicTo, false, vmType); + Pair networkInfo = prepareNetworkFromNicInfo(vmMo.getRunningHost(), nicTo, false, nicTo.getNetworkSegmentName(), vmType); String dvSwitchUuid = null; if (VmwareHelper.isDvPortGroup(networkInfo.first())) { ManagedObjectReference dcMor = hyperHost.getHyperHostDatacenter(); @@ -1534,7 +1534,7 @@ private ReplugNicAnswer execute(ReplugNicCommand cmd) { return new ReplugNicAnswer(cmd, false, "Nic to replug not found"); } - Pair networkInfo = prepareNetworkFromNicInfo(vmMo.getRunningHost(), nicTo, false, cmd.getVMType()); + Pair networkInfo = prepareNetworkFromNicInfo(vmMo.getRunningHost(), nicTo, false, null, cmd.getVMType()); String dvSwitchUuid = null; if (VmwareHelper.isDvPortGroup(networkInfo.first())) { ManagedObjectReference dcMor = hyperHost.getHyperHostDatacenter(); @@ -1616,7 +1616,7 @@ private void plugPublicNic(VirtualMachineMO vmMo, final String vlanId, final IpA } else { networkInfo = HypervisorHostHelper.prepareNetwork(_publicTrafficInfo.getVirtualSwitchName(), "cloud.public", vmMo.getRunningHost(), vlanId, null, ipAddressTO.getNetworkRate(), null, - _opsTimeout, vSwitchType, _portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan, _vsmCredentials, null); + _opsTimeout, vSwitchType, _portsPerDvPortGroup, null, false, BroadcastDomainType.Vlan, _vsmCredentials, null, null); } int nicIndex = allocPublicNicIndex(vmMo); @@ -2525,7 +2525,8 @@ protected StartAnswer execute(StartCommand cmd) { } boolean configureVServiceInNexus = (nicTo.getType() == TrafficType.Guest) && (vmSpec.getDetails().containsKey("ConfigureVServiceInNexus")); VirtualMachine.Type vmType = cmd.getVirtualMachine().getType(); - Pair networkInfo = prepareNetworkFromNicInfo(vmMo.getRunningHost(), nicTo, configureVServiceInNexus, vmType); + Pair networkInfo = prepareNetworkFromNicInfo(vmMo.getRunningHost(), nicTo, configureVServiceInNexus, + vmSpec.getNetworkIdToNetworkNameMap().getOrDefault(nicTo.getNetworkId(), null), vmType); if ((nicTo.getBroadcastType() != BroadcastDomainType.Lswitch) || (nicTo.getBroadcastType() == BroadcastDomainType.Lswitch && NiciraNvpApiVersion.isApiVersionLowerThan("4.2"))) { if (VmwareHelper.isDvPortGroup(networkInfo.first())) { @@ -3981,7 +3982,7 @@ private String getVlanInfo(NicTO nicTo, String defaultVlan) { return defaultVlan; } - private Pair prepareNetworkFromNicInfo(HostMO hostMo, NicTO nicTo, boolean configureVServiceInNexus, VirtualMachine.Type vmType) + private Pair prepareNetworkFromNicInfo(HostMO hostMo, NicTO nicTo, boolean configureVServiceInNexus, String networkName, VirtualMachine.Type vmType) throws Exception { Ternary switchDetails = getTargetSwitch(nicTo); @@ -4011,7 +4012,7 @@ private Pair prepareNetworkFromNicInfo(HostMO ho } networkInfo = HypervisorHostHelper.prepareNetwork(switchName, namePrefix, hostMo, vlanId, svlanId, nicTo.getNetworkRateMbps(), nicTo.getNetworkRateMulticastMbps(), _opsTimeout, switchType, - _portsPerDvPortGroup, nicTo.getGateway(), configureVServiceInNexus, nicTo.getBroadcastType(), _vsmCredentials, nicTo.getDetails()); + _portsPerDvPortGroup, nicTo.getGateway(), configureVServiceInNexus, nicTo.getBroadcastType(), _vsmCredentials, nicTo.getDetails(), networkName); } return networkInfo; @@ -4602,7 +4603,8 @@ protected Answer execute(PrepareForMigrationCommand cmd) { NicTO[] nics = vm.getNics(); for (NicTO nic : nics) { // prepare network on the host - prepareNetworkFromNicInfo(new HostMO(getServiceContext(), _morHyperHost), nic, false, cmd.getVirtualMachine().getType()); + prepareNetworkFromNicInfo(new HostMO(getServiceContext(), _morHyperHost), nic, false, + vm.getNetworkIdToNetworkNameMap().getOrDefault(nic.getNetworkId(), null), cmd.getVirtualMachine().getType()); } List> secStoreUrlAndIdList = mgr.getSecondaryStorageStoresUrlAndIdList(Long.parseLong(_dcId)); @@ -5680,7 +5682,7 @@ protected Answer execute(SetupCommand cmd) { } protected Answer execute(MaintainCommand cmd) { - return new MaintainAnswer(cmd, "Put host in maintaince"); + return new MaintainAnswer(cmd, "Put host in maintenance"); } protected Answer execute(PingTestCommand cmd) { @@ -7326,7 +7328,8 @@ private List relocateVirtualMachine(final VmwareHypervisorHost h NicTO[] nics = vmTo.getNics(); for (NicTO nic : nics) { // prepare network on the host - prepareNetworkFromNicInfo((HostMO)targetHyperHost, nic, false, vmTo.getType()); + prepareNetworkFromNicInfo((HostMO)targetHyperHost, nic, false, + vmTo.getNetworkIdToNetworkNameMap().get(nic.getNetworkId()), vmTo.getType()); } if (targetHyperHost == null) { diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java index 0ef916ab9591..60bd81c7c5a5 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/KubernetesClusterHelperImpl.java @@ -17,6 +17,7 @@ package com.cloud.kubernetes.cluster; import com.cloud.kubernetes.cluster.dao.KubernetesClusterDao; +import com.cloud.kubernetes.cluster.dao.KubernetesClusterVmMapDao; import com.cloud.utils.component.AdapterBase; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.framework.config.ConfigKey; @@ -24,18 +25,30 @@ import org.springframework.stereotype.Component; import javax.inject.Inject; +import java.util.Objects; @Component public class KubernetesClusterHelperImpl extends AdapterBase implements KubernetesClusterHelper, Configurable { @Inject private KubernetesClusterDao kubernetesClusterDao; + @Inject + private KubernetesClusterVmMapDao kubernetesClusterVmMapDao; @Override public ControlledEntity findByUuid(String uuid) { return kubernetesClusterDao.findByUuid(uuid); } + @Override + public ControlledEntity findByVmId(long vmId) { + KubernetesClusterVmMapVO clusterVmMapVO = kubernetesClusterVmMapDao.getClusterMapFromVmId(vmId); + if (Objects.isNull(clusterVmMapVO)) { + return null; + } + return kubernetesClusterDao.findById(clusterVmMapVO.getClusterId()); + } + @Override public String getConfigComponentName() { return KubernetesClusterHelper.class.getSimpleName(); 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 fb95a35a6feb..834d6d333305 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 @@ -182,6 +182,11 @@ public class KubernetesClusterManagerImpl extends ManagerBase implements KubernetesClusterService { private static final String DEFAULT_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME = "DefaultNetworkOfferingforKubernetesService"; + private static final String DEFAULT_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_DISPLAY_TEXT = "Network Offering used for CloudStack Kubernetes service"; + private static final String DEFAULT_NSX_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME = "DefaultNSXNetworkOfferingforKubernetesService"; + private static final String DEFAULT_NSX_VPC_TIER_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME = "DefaultNSXVPCNetworkOfferingforKubernetesService"; + private static final String DEFAULT_NSX_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_DISPLAY_TEXT = "Network Offering for NSX CloudStack Kubernetes Service"; + private static final String DEFAULT_NSX_VPC_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_DISPLAY_TEXT = "Network Offering for NSX CloudStack Kubernetes service on VPC"; protected StateMachine2 _stateMachine = KubernetesCluster.State.getStateMachine(); @@ -1893,26 +1898,54 @@ boolean isClusterVMsInDesiredState(KubernetesCluster kubernetesCluster, VirtualM @Override public boolean start() { + createNetworkOfferingForKubernetes(DEFAULT_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME, + DEFAULT_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_DISPLAY_TEXT, false, false); + + createNetworkOfferingForKubernetes(DEFAULT_NSX_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME, + DEFAULT_NSX_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_DISPLAY_TEXT, true, false); + + createNetworkOfferingForKubernetes(DEFAULT_NSX_VPC_TIER_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME, + DEFAULT_NSX_VPC_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_DISPLAY_TEXT , true, true); + + _gcExecutor.scheduleWithFixedDelay(new KubernetesClusterGarbageCollector(), 300, 300, TimeUnit.SECONDS); + _stateScanner.scheduleWithFixedDelay(new KubernetesClusterStatusScanner(), 300, 30, TimeUnit.SECONDS); + + return true; + } + + private void createNetworkOfferingForKubernetes(String offeringName, String offeringDesc, boolean forNsx, boolean forVpc) { final Map defaultKubernetesServiceNetworkOfferingProviders = new HashMap(); - defaultKubernetesServiceNetworkOfferingProviders.put(Service.Dhcp, Network.Provider.VirtualRouter); - defaultKubernetesServiceNetworkOfferingProviders.put(Service.Dns, Network.Provider.VirtualRouter); - defaultKubernetesServiceNetworkOfferingProviders.put(Service.UserData, Network.Provider.VirtualRouter); - defaultKubernetesServiceNetworkOfferingProviders.put(Service.Firewall, Network.Provider.VirtualRouter); - defaultKubernetesServiceNetworkOfferingProviders.put(Service.Gateway, Network.Provider.VirtualRouter); - defaultKubernetesServiceNetworkOfferingProviders.put(Service.Lb, Network.Provider.VirtualRouter); - defaultKubernetesServiceNetworkOfferingProviders.put(Service.SourceNat, Network.Provider.VirtualRouter); - defaultKubernetesServiceNetworkOfferingProviders.put(Service.StaticNat, Network.Provider.VirtualRouter); - defaultKubernetesServiceNetworkOfferingProviders.put(Service.PortForwarding, Network.Provider.VirtualRouter); - defaultKubernetesServiceNetworkOfferingProviders.put(Service.Vpn, Network.Provider.VirtualRouter); + Network.Provider provider = forVpc ? Network.Provider.VPCVirtualRouter : Network.Provider.VirtualRouter; + defaultKubernetesServiceNetworkOfferingProviders.put(Service.Dhcp, provider); + defaultKubernetesServiceNetworkOfferingProviders.put(Service.Dns, provider); + defaultKubernetesServiceNetworkOfferingProviders.put(Service.UserData, provider); + if (forVpc) { + defaultKubernetesServiceNetworkOfferingProviders.put(Service.NetworkACL, forNsx ? Network.Provider.Nsx : provider); + } else { + defaultKubernetesServiceNetworkOfferingProviders.put(Service.Firewall, forNsx ? Network.Provider.Nsx : provider); + } + defaultKubernetesServiceNetworkOfferingProviders.put(Service.Lb, forNsx ? Network.Provider.Nsx : provider); + defaultKubernetesServiceNetworkOfferingProviders.put(Service.SourceNat, forNsx ? Network.Provider.Nsx : provider); + defaultKubernetesServiceNetworkOfferingProviders.put(Service.StaticNat, forNsx ? Network.Provider.Nsx : provider); + defaultKubernetesServiceNetworkOfferingProviders.put(Service.PortForwarding, forNsx ? Network.Provider.Nsx : provider); + + if (!forNsx) { + defaultKubernetesServiceNetworkOfferingProviders.put(Service.Gateway, Network.Provider.VirtualRouter); + defaultKubernetesServiceNetworkOfferingProviders.put(Service.Vpn, Network.Provider.VirtualRouter); + } NetworkOfferingVO defaultKubernetesServiceNetworkOffering = - new NetworkOfferingVO(DEFAULT_NETWORK_OFFERING_FOR_KUBERNETES_SERVICE_NAME, - "Network Offering used for CloudStack Kubernetes service", Networks.TrafficType.Guest, + new NetworkOfferingVO(offeringName, + offeringDesc, Networks.TrafficType.Guest, false, false, null, null, true, NetworkOffering.Availability.Required, null, Network.GuestType.Isolated, true, true, false, false, false, false, false, false, false, true, true, false, - false, true, false, false); + forVpc, true, false, false); + if (forNsx) { + defaultKubernetesServiceNetworkOffering.setNsxMode(NetworkOffering.NsxMode.NATTED.name()); + defaultKubernetesServiceNetworkOffering.setForNsx(true); + } defaultKubernetesServiceNetworkOffering.setSupportsVmAutoScaling(true); defaultKubernetesServiceNetworkOffering.setState(NetworkOffering.State.Enabled); defaultKubernetesServiceNetworkOffering = networkOfferingDao.persistDefaultNetworkOffering(defaultKubernetesServiceNetworkOffering); @@ -1924,11 +1957,6 @@ public boolean start() { networkOfferingServiceMapDao.persist(offService); logger.trace("Added service for the network offering: " + offService); } - - _gcExecutor.scheduleWithFixedDelay(new KubernetesClusterGarbageCollector(), 300, 300, TimeUnit.SECONDS); - _stateScanner.scheduleWithFixedDelay(new KubernetesClusterStatusScanner(), 300, 30, TimeUnit.SECONDS); - - return true; } @Override diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java index c7451adf502d..e8bc8e2851ed 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/actionworkers/KubernetesClusterResourceModifierActionWorker.java @@ -31,6 +31,8 @@ import javax.inject.Inject; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingDao; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.api.BaseCmd; import org.apache.cloudstack.api.command.user.firewall.CreateFirewallRuleCmd; @@ -149,6 +151,8 @@ public class KubernetesClusterResourceModifierActionWorker extends KubernetesClu protected VolumeApiService volumeService; @Inject protected VolumeDao volumeDao; + @Inject + protected NetworkOfferingDao networkOfferingDao; protected String kubernetesClusterNodeNamePrefix; @@ -738,12 +742,24 @@ protected void removeVpcTierAclRules(Network network) throws ManagementServerExc protected void setupKubernetesClusterVpcTierRules(IpAddress publicIp, Network network, List clusterVMIds) throws ManagementServerException { // Create ACL rules createVpcTierAclRules(network); - // Add port forwarding for API access - try { - provisionPublicIpPortForwardingRule(publicIp, network, owner, clusterVMIds.get(0), CLUSTER_API_PORT, CLUSTER_API_PORT); - } catch (ResourceUnavailableException | NetworkRuleConflictException e) { - throw new ManagementServerException(String.format("Failed to activate API port forwarding rules for the Kubernetes cluster : %s", kubernetesCluster.getName()), e); + + NetworkOffering offering = networkOfferingDao.findById(network.getNetworkOfferingId()); + if (offering.isConserveMode()) { + // Add load balancing for API access + try { + provisionLoadBalancerRule(publicIp, network, owner, clusterVMIds, CLUSTER_API_PORT); + } catch (InsufficientAddressCapacityException e) { + throw new ManagementServerException(String.format("Failed to activate API load balancing rules for the Kubernetes cluster : %s", kubernetesCluster.getName()), e); + } + } else { + // Add port forwarding for API access + try { + provisionPublicIpPortForwardingRule(publicIp, network, owner, clusterVMIds.get(0), CLUSTER_API_PORT, CLUSTER_API_PORT); + } catch (ResourceUnavailableException | NetworkRuleConflictException e) { + throw new ManagementServerException(String.format("Failed to activate API port forwarding rules for the Kubernetes cluster : %s", kubernetesCluster.getName()), e); + } } + // Add port forwarding rule for SSH access on each node VM try { provisionSshPortForwardingRules(publicIp, network, owner, clusterVMIds); diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java index 688a611ac998..45c0b79485c0 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDao.java @@ -23,6 +23,8 @@ public interface KubernetesClusterVmMapDao extends GenericDao { public List listByClusterId(long clusterId); + + public KubernetesClusterVmMapVO getClusterMapFromVmId(long vmId); public List listByClusterIdAndVmIdsIn(long clusterId, List vmIds); int removeByClusterIdAndVmIdsIn(long clusterId, List vmIds); diff --git a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java index b9f2ec917b2b..0d90a4cdaca9 100644 --- a/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java +++ b/plugins/integrations/kubernetes-service/src/main/java/com/cloud/kubernetes/cluster/dao/KubernetesClusterVmMapDaoImpl.java @@ -31,12 +31,17 @@ public class KubernetesClusterVmMapDaoImpl extends GenericDaoBase implements KubernetesClusterVmMapDao { private final SearchBuilder clusterIdSearch; + private final SearchBuilder vmIdSearch; public KubernetesClusterVmMapDaoImpl() { clusterIdSearch = createSearchBuilder(); clusterIdSearch.and("clusterId", clusterIdSearch.entity().getClusterId(), SearchCriteria.Op.EQ); clusterIdSearch.and("vmIdsIN", clusterIdSearch.entity().getVmId(), SearchCriteria.Op.IN); clusterIdSearch.done(); + + vmIdSearch = createSearchBuilder(); + vmIdSearch.and("vmId", vmIdSearch.entity().getVmId(), SearchCriteria.Op.EQ); + vmIdSearch.done(); } @Override @@ -47,6 +52,13 @@ public List listByClusterId(long clusterId) { return listBy(sc, filter); } + @Override + public KubernetesClusterVmMapVO getClusterMapFromVmId(long vmId) { + SearchCriteria sc = vmIdSearch.create(); + sc.setParameters("vmId", vmId); + return findOneBy(sc); + } + @Override public List listByClusterIdAndVmIdsIn(long clusterId, List vmIds) { SearchCriteria sc = clusterIdSearch.create(); diff --git a/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java b/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java index 95513df9c86e..5fc9480c6102 100644 --- a/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java +++ b/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/element/BigSwitchBcfElement.java @@ -698,6 +698,11 @@ public boolean applyNetworkACLs(Network network, return true; } + @Override + public boolean reorderAclRules(Vpc vpc, List networks, List networkACLItems) { + return true; + } + @Override public boolean applyFWRules(Network network, List rules) diff --git a/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/guru/BigSwitchBcfGuestNetworkGuru.java b/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/guru/BigSwitchBcfGuestNetworkGuru.java index e23395bcf65c..f9c11e507483 100644 --- a/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/guru/BigSwitchBcfGuestNetworkGuru.java +++ b/plugins/network-elements/bigswitch/src/main/java/com/cloud/network/guru/BigSwitchBcfGuestNetworkGuru.java @@ -143,7 +143,7 @@ protected boolean canHandle(NetworkOffering offering, NetworkType networkType, P } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { // Check if the isolation type of the physical network is BCF_SEGMENT, then delegate GuestNetworkGuru to design PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); if (physnet == null || physnet.getIsolationMethods() == null || !physnet.getIsolationMethods().contains("BCF_SEGMENT")) { @@ -162,7 +162,7 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network use } logger.debug("Physical isolation type is BCF_SEGMENT, asking GuestNetworkGuru to design this network"); - NetworkVO networkObject = (NetworkVO)super.design(offering, plan, userSpecified, owner); + NetworkVO networkObject = (NetworkVO)super.design(offering, plan, userSpecified, name, vpcId, owner); if (networkObject == null) { return null; } diff --git a/plugins/network-elements/brocade-vcs/src/main/java/com/cloud/network/guru/BrocadeVcsGuestNetworkGuru.java b/plugins/network-elements/brocade-vcs/src/main/java/com/cloud/network/guru/BrocadeVcsGuestNetworkGuru.java index 1e13aaaa326b..8d2125d70eb0 100644 --- a/plugins/network-elements/brocade-vcs/src/main/java/com/cloud/network/guru/BrocadeVcsGuestNetworkGuru.java +++ b/plugins/network-elements/brocade-vcs/src/main/java/com/cloud/network/guru/BrocadeVcsGuestNetworkGuru.java @@ -95,7 +95,7 @@ && isMyIsolationMethod(physicalNetwork) && _ntwkOfferingSrvcDao.areServicesSuppo } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { // Check of the isolation type of the related physical network is VLAN PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); DataCenter dc = _dcDao.findById(plan.getDataCenterId()); @@ -103,8 +103,9 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network use logger.debug("Refusing to design this network"); return null; } + logger.debug("Physical isolation type is VCS, asking GuestNetworkGuru to design this network"); - NetworkVO networkObject = (NetworkVO)super.design(offering, plan, userSpecified, owner); + NetworkVO networkObject = (NetworkVO)super.design(offering, plan, userSpecified, name, vpcId, owner); if (networkObject == null) { return null; } diff --git a/plugins/network-elements/brocade-vcs/src/test/java/com/cloud/network/guru/BrocadeVcsGuestNetworkGuruTest.java b/plugins/network-elements/brocade-vcs/src/test/java/com/cloud/network/guru/BrocadeVcsGuestNetworkGuruTest.java index 9fbb96f046df..3f5a047b9115 100644 --- a/plugins/network-elements/brocade-vcs/src/test/java/com/cloud/network/guru/BrocadeVcsGuestNetworkGuruTest.java +++ b/plugins/network-elements/brocade-vcs/src/test/java/com/cloud/network/guru/BrocadeVcsGuestNetworkGuruTest.java @@ -170,7 +170,7 @@ public void testDesign() { final Network network = mock(Network.class); final Account account = mock(Account.class); - final Network designednetwork = guru.design(offering, plan, network, account); + final Network designednetwork = guru.design(offering, plan, network, "", 1L, account); assertTrue(designednetwork != null); assertTrue(designednetwork.getBroadcastDomainType() == BroadcastDomainType.Vcs); } @@ -191,7 +191,7 @@ public void testDesignNoIsolationMethodVCS() { final Network network = mock(Network.class); final Account account = mock(Account.class); - final Network designednetwork = guru.design(offering, plan, network, account); + final Network designednetwork = guru.design(offering, plan, network, "", 1L, account); assertTrue(designednetwork == null); } @@ -213,7 +213,7 @@ public void testDesignNoConnectivityInOffering() { final Network network = mock(Network.class); final Account account = mock(Account.class); - final Network designednetwork = guru.design(offering, plan, network, account); + final Network designednetwork = guru.design(offering, plan, network, "", 1L, account); assertTrue(designednetwork == null); } diff --git a/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java b/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java index 0bbc3e678d3e..0a9b4a7131a3 100644 --- a/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java +++ b/plugins/network-elements/internal-loadbalancer/src/main/java/org/apache/cloudstack/network/element/InternalLoadBalancerElement.java @@ -83,7 +83,9 @@ import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.network.router.NetworkHelper; +import org.springframework.stereotype.Component; +@Component public class InternalLoadBalancerElement extends AdapterBase implements LoadBalancingServiceProvider, InternalLoadBalancerElementService, IpDeployer { protected static final Map> capabilities = setCapabilities(); private static InternalLoadBalancerElement internalLbElement = null; @@ -112,14 +114,7 @@ public class InternalLoadBalancerElement extends AdapterBase implements LoadBala @Qualifier("networkHelper") protected NetworkHelper _networkHelper; - protected InternalLoadBalancerElement() { - } - - public static InternalLoadBalancerElement getInstance() { - if (internalLbElement == null) { - internalLbElement = new InternalLoadBalancerElement(); - } - return internalLbElement; + public InternalLoadBalancerElement() { } private boolean canHandle(Network config, Scheme lbScheme) { @@ -517,6 +512,11 @@ public List searchForInternalLoadBalancerElemen return sc.list(); } + @Override + public Type getProviderType() { + return Type.InternalLbVm; + } + @Override public boolean applyIps(Network network, List ipAddress, Set services) throws ResourceUnavailableException { //do nothing here; this element just has to extend the ip deployer diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailGuru.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailGuru.java index 6fb4c3eff972..345cdc1e6c6c 100644 --- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailGuru.java +++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailGuru.java @@ -117,7 +117,7 @@ public String getName() { } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { // Check of the isolation type of the related physical network is L3VPN PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); DataCenter dc = _dcDao.findById(plan.getDataCenterId()); @@ -136,6 +136,11 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network use return network; } + @Override + public void setup(Network network, long networkId) { + // do nothing + } + @Override public Network implement(Network network, NetworkOffering offering, DeployDestination destination, ReservationContext context) throws InsufficientVirtualNetworkCapacityException { diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java index 1261a23b5692..791e245b17ce 100644 --- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java +++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailManagerImpl.java @@ -217,7 +217,7 @@ private NetworkOffering locatePublicNetworkOffering(String offeringName, ConfigurationManager configMgr = (ConfigurationManager) _configService; NetworkOfferingVO voffer = configMgr.createNetworkOffering(offeringName, offeringDisplayText, TrafficType.Public, null, true, Availability.Optional, null, serviceProviderMap, true, - Network.GuestType.Shared, false, null, false, null, true, false, null, true, null, false, false, false, null, null, true, null); + Network.GuestType.Shared, false, null, false, null, true, false, null, true, null, false, false, false, false, null, null, null, true, null); long id = voffer.getId(); _networkOfferingDao.update(id, voffer); return _networkOfferingDao.findById(id); @@ -252,7 +252,7 @@ private NetworkOffering locateNetworkOffering(String offeringName, ConfigurationManager configMgr = (ConfigurationManager)_configService; NetworkOfferingVO voffer = configMgr.createNetworkOffering(offeringName, offeringDisplayText, TrafficType.Guest, null, false, Availability.Optional, null, serviceProviderMap, true, - Network.GuestType.Isolated, false, null, false, null, false, true, null, true, null, false, offeringName.equals(vpcRouterOfferingName), false, null, null, true, null); + Network.GuestType.Isolated, false, null, false, null, false, true, null, true, null, false, offeringName.equals(vpcRouterOfferingName), false, false, null, null, null, true, null); if (offeringName.equals(vpcRouterOfferingName)) { voffer.setInternalLb(true); } @@ -293,7 +293,7 @@ private VpcOffering locateVpcOffering() { } serviceProviderMap.put(svc, providerSet); } - vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null, null, null, null, VpcOffering.State.Enabled); + vpcOffer = _vpcProvSvc.createVpcOffering(juniperVPCOfferingName, juniperVPCOfferingDisplayText, services, serviceProviderMap, null, null, null, false, null, null, null, VpcOffering.State.Enabled); long id = vpcOffer.getId(); _vpcOffDao.update(id, (VpcOfferingVO)vpcOffer); return _vpcOffDao.findById(id); diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailVpcElementImpl.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailVpcElementImpl.java index b73ed7feb608..85125bf3af60 100644 --- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailVpcElementImpl.java +++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ContrailVpcElementImpl.java @@ -182,6 +182,11 @@ public boolean applyNetworkACLs(Network net, return true; } + @Override + public boolean reorderAclRules(Vpc vpc, List networks, List networkACLItems) { + return true; + } + @Override public boolean applyACLItemsToPrivateGw(PrivateGateway privateGateway, List rules) diff --git a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ManagementNetworkGuru.java b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ManagementNetworkGuru.java index 0959eabd832d..dc453f75c150 100644 --- a/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ManagementNetworkGuru.java +++ b/plugins/network-elements/juniper-contrail/src/main/java/org/apache/cloudstack/network/contrail/management/ManagementNetworkGuru.java @@ -109,7 +109,7 @@ private boolean canHandle(NetworkOffering offering) { } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { if (!canHandle(offering)) { return null; diff --git a/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/guru/NiciraNvpGuestNetworkGuru.java b/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/guru/NiciraNvpGuestNetworkGuru.java index 61dcf914e499..daf2420b528c 100644 --- a/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/guru/NiciraNvpGuestNetworkGuru.java +++ b/plugins/network-elements/nicira-nvp/src/main/java/com/cloud/network/guru/NiciraNvpGuestNetworkGuru.java @@ -136,7 +136,7 @@ private boolean supportedGuestTypes(NetworkOffering offering, GuestType... types } @Override - public Network design(final NetworkOffering offering, final DeploymentPlan plan, final Network userSpecified, final Account owner) { + public Network design(final NetworkOffering offering, final DeploymentPlan plan, final Network userSpecified, String name, Long vpcId, final Account owner) { // Check of the isolation type of the related physical network is supported final PhysicalNetworkVO physnet = physicalNetworkDao.findById(plan.getPhysicalNetworkId()); final DataCenter dc = _dcDao.findById(plan.getDataCenterId()); @@ -153,7 +153,7 @@ public Network design(final NetworkOffering offering, final DeploymentPlan plan, logger.debug("Nicira Nvp " + devices.get(0).getUuid() + " found on physical network " + physnet.getId()); logger.debug("Physical isolation type is supported, asking GuestNetworkGuru to design this network"); - final NetworkVO networkObject = (NetworkVO) super.design(offering, plan, userSpecified, owner); + final NetworkVO networkObject = (NetworkVO) super.design(offering, plan, userSpecified, name, vpcId, owner); if (networkObject == null) { return null; } diff --git a/plugins/network-elements/nicira-nvp/src/test/java/com/cloud/network/guru/NiciraNvpGuestNetworkGuruTest.java b/plugins/network-elements/nicira-nvp/src/test/java/com/cloud/network/guru/NiciraNvpGuestNetworkGuruTest.java index 5925fd981491..c6ad8a6d9070 100644 --- a/plugins/network-elements/nicira-nvp/src/test/java/com/cloud/network/guru/NiciraNvpGuestNetworkGuruTest.java +++ b/plugins/network-elements/nicira-nvp/src/test/java/com/cloud/network/guru/NiciraNvpGuestNetworkGuruTest.java @@ -168,7 +168,7 @@ public void testDesign() { final Network network = mock(Network.class); final Account account = mock(Account.class); - final Network designednetwork = guru.design(offering, plan, network, account); + final Network designednetwork = guru.design(offering, plan, network, "", 1L, account); assertTrue(designednetwork != null); assertTrue(designednetwork.getBroadcastDomainType() == BroadcastDomainType.Lswitch); } @@ -192,7 +192,7 @@ public void testDesignNoElementOnPhysicalNetwork() { final Network network = mock(Network.class); final Account account = mock(Account.class); - final Network designednetwork = guru.design(offering, plan, network, account); + final Network designednetwork = guru.design(offering, plan, network, "", 1L, account); assertTrue(designednetwork == null); } @@ -215,7 +215,7 @@ public void testDesignNoIsolationMethodSTT() { final Network network = mock(Network.class); final Account account = mock(Account.class); - final Network designednetwork = guru.design(offering, plan, network, account); + final Network designednetwork = guru.design(offering, plan, network, "", 1L, account); assertTrue(designednetwork == null); } @@ -241,7 +241,7 @@ public void testDesignNoConnectivityInOffering() { final Network network = mock(Network.class); final Account account = mock(Account.class); - final Network designednetwork = guru.design(offering, plan, network, account); + final Network designednetwork = guru.design(offering, plan, network, "", 1L, account); assertTrue(designednetwork == null); } diff --git a/plugins/network-elements/nsx/pom.xml b/plugins/network-elements/nsx/pom.xml new file mode 100644 index 000000000000..bed5731feee8 --- /dev/null +++ b/plugins/network-elements/nsx/pom.xml @@ -0,0 +1,59 @@ + + + 4.0.0 + cloud-plugin-network-nsx + Apache CloudStack Plugin - NSX Network + + + org.apache.cloudstack + cloudstack-plugins + 4.20.0.0-SNAPSHOT + ../../pom.xml + + + + com.vmware + nsx-java-sdk + 4.1.0.2.0 + + + com.vmware + nsx-gpm-java-sdk + 4.1.0.2.0 + + + com.vmware + nsx-policy-java-sdk + 4.1.0.2.0 + + + com.vmware.vapi + vapi-authentication + 2.40.0 + + + com.vmware.vapi + vapi-runtime + 2.40.0 + + + diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/NsxAnswer.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/NsxAnswer.java new file mode 100644 index 000000000000..0820465a6b68 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/NsxAnswer.java @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack; + +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; + +public class NsxAnswer extends Answer { + public NsxAnswer(final Command command, final boolean success, final String details) { + super(command, success, details); + } + + public NsxAnswer(final Command command, final Exception e) { + super(command, e); + } + +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/StartupNsxCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/StartupNsxCommand.java new file mode 100644 index 000000000000..8a5ac35e57e9 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/StartupNsxCommand.java @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack; + +import com.cloud.agent.api.StartupCommand; +import com.cloud.host.Host; + +public class StartupNsxCommand extends StartupCommand { + public StartupNsxCommand() { + super(Host.Type.L2Networking); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxDhcpRelayConfigCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxDhcpRelayConfigCommand.java new file mode 100644 index 000000000000..6ef75b21e309 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxDhcpRelayConfigCommand.java @@ -0,0 +1,77 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import java.util.List; +import java.util.Objects; + +public class CreateNsxDhcpRelayConfigCommand extends NsxCommand { + + private Long vpcId; + private String vpcName; + private long networkId; + private String networkName; + private List addresses; + + public CreateNsxDhcpRelayConfigCommand(long domainId, long accountId, long zoneId, + Long vpcId, String vpcName, long networkId, String networkName, + List addresses) { + super(domainId, accountId, zoneId); + this.vpcId = vpcId; + this.vpcName = vpcName; + this.networkId = networkId; + this.networkName = networkName; + this.addresses = addresses; + } + + public Long getVpcId() { + return vpcId; + } + + public String getVpcName() { + return vpcName; + } + + public long getNetworkId() { + return networkId; + } + + public String getNetworkName() { + return networkName; + } + + public List getAddresses() { + return addresses; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + CreateNsxDhcpRelayConfigCommand that = (CreateNsxDhcpRelayConfigCommand) o; + return networkId == that.networkId && Objects.equals(vpcId, that.vpcId) && Objects.equals(vpcName, that.vpcName) && Objects.equals(networkName, that.networkName) && Objects.equals(addresses, that.addresses); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), vpcId, vpcName, networkId, networkName, addresses); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxDistributedFirewallRulesCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxDistributedFirewallRulesCommand.java new file mode 100644 index 000000000000..f598a2014298 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxDistributedFirewallRulesCommand.java @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import org.apache.cloudstack.resource.NsxNetworkRule; + +import java.util.List; +import java.util.Objects; + +public class CreateNsxDistributedFirewallRulesCommand extends NsxCommand { + + private Long vpcId; + private long networkId; + private List rules; + + public CreateNsxDistributedFirewallRulesCommand(long domainId, long accountId, long zoneId, + Long vpcId, long networkId, + List rules) { + super(domainId, accountId, zoneId); + this.vpcId = vpcId; + this.networkId = networkId; + this.rules = rules; + } + + public Long getVpcId() { + return vpcId; + } + + public long getNetworkId() { + return networkId; + } + + public List getRules() { + return rules; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + CreateNsxDistributedFirewallRulesCommand that = (CreateNsxDistributedFirewallRulesCommand) o; + return networkId == that.networkId && Objects.equals(vpcId, that.vpcId) && Objects.equals(rules, that.rules); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), vpcId, networkId, rules); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxLoadBalancerRuleCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxLoadBalancerRuleCommand.java new file mode 100644 index 000000000000..92acc83064d7 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxLoadBalancerRuleCommand.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import org.apache.cloudstack.resource.NsxLoadBalancerMember; + +import java.util.List; +import java.util.Objects; + +public class CreateNsxLoadBalancerRuleCommand extends NsxNetworkCommand { + + private final String publicPort; + private final String privatePort; + private final String algorithm; + private final String protocol; + List memberList; + + private final long lbId; + public CreateNsxLoadBalancerRuleCommand(long domainId, long accountId, long zoneId, Long networkResourceId, + String networkResourceName, boolean isResourceVpc, + List memberList, long lbId, String publicPort, + String privatePort, String algorithm, String protocol) { + super(domainId, accountId, zoneId, networkResourceId, networkResourceName, isResourceVpc); + this.lbId = lbId; + this.memberList = memberList; + this.publicPort = publicPort; + this.privatePort = privatePort; + this.algorithm = algorithm; + this.protocol = protocol; + } + + + public long getLbId() { + return lbId; + } + + public String getPublicPort() { + return publicPort; + } + + public String getPrivatePort() { + return privatePort; + } + + public List getMemberList() { + return memberList; + } + + public String getAlgorithm() { + return algorithm; + } + + public String getProtocol() { + return protocol; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + CreateNsxLoadBalancerRuleCommand command = (CreateNsxLoadBalancerRuleCommand) o; + return lbId == command.lbId && Objects.equals(publicPort, command.publicPort) && Objects.equals(privatePort, command.privatePort) && Objects.equals(algorithm, command.algorithm) && Objects.equals(protocol, command.protocol) && Objects.equals(memberList, command.memberList); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), publicPort, privatePort, algorithm, protocol, memberList, lbId); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxPortForwardRuleCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxPortForwardRuleCommand.java new file mode 100644 index 000000000000..d72295563c7e --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxPortForwardRuleCommand.java @@ -0,0 +1,71 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import java.util.Objects; + +public class CreateNsxPortForwardRuleCommand extends NsxNetworkCommand { + private final String publicPort; + private final String privatePort; + private final String protocol; + private final long ruleId; + + + public CreateNsxPortForwardRuleCommand(long domainId, long accountId, long zoneId, Long networkResourceId, + String networkResourceName, boolean isResourceVpc, Long vmId, + long ruleId, String publicIp, String vmIp, String publicPort, String privatePort, String protocol) { + super(domainId, accountId, zoneId, networkResourceId, networkResourceName, isResourceVpc, vmId, publicIp, vmIp); + this.publicPort = publicPort; + this.privatePort = privatePort; + this.ruleId = ruleId; + this.protocol = protocol; + + } + + public String getPublicPort() { + return publicPort; + } + + public String getPrivatePort() { + return privatePort; + } + + public long getRuleId() { + return ruleId; + } + + public String getProtocol() { + return protocol; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + CreateNsxPortForwardRuleCommand that = (CreateNsxPortForwardRuleCommand) o; + return ruleId == that.ruleId && Objects.equals(publicPort, that.publicPort) && Objects.equals(privatePort, that.privatePort) && Objects.equals(protocol, that.protocol); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), publicPort, privatePort, protocol, ruleId); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxSegmentCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxSegmentCommand.java new file mode 100644 index 000000000000..b4b86bd640a6 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxSegmentCommand.java @@ -0,0 +1,79 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import java.util.Objects; + +public class CreateNsxSegmentCommand extends NsxCommand { + + private Long vpcId; + private String vpcName; + private long networkId; + private String networkName; + private String networkGateway; + private String networkCidr; + + public CreateNsxSegmentCommand(long domainId, long accountId, long zoneId, + Long vpcId, String vpcName, long networkId, String networkName, + String networkGateway, String networkCidr) { + super(domainId, accountId, zoneId); + this.vpcId = vpcId; + this.vpcName = vpcName; + this.networkId = networkId; + this.networkName = networkName; + this.networkGateway = networkGateway; + this.networkCidr = networkCidr; + } + + public Long getVpcId() { + return vpcId; + } + + public String getVpcName() { + return vpcName; + } + + public long getNetworkId() { + return networkId; + } + + public String getNetworkName() { + return networkName; + } + + public String getNetworkGateway() { + return networkGateway; + } + + public String getNetworkCidr() { + return networkCidr; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + CreateNsxSegmentCommand command = (CreateNsxSegmentCommand) o; + return Objects.equals(networkName, command.networkName); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), networkName); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxStaticNatCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxStaticNatCommand.java new file mode 100644 index 000000000000..08c13420d1d3 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxStaticNatCommand.java @@ -0,0 +1,25 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +public class CreateNsxStaticNatCommand extends NsxNetworkCommand { + + public CreateNsxStaticNatCommand(long domainId, long accountId, long zoneId, Long networkResourceId, String networkResourceName, + boolean isResourceVpc, Long vmId, String publicIp, String vmIp) { + super(domainId, accountId, zoneId, networkResourceId, networkResourceName, isResourceVpc, vmId, publicIp, vmIp); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxTier1GatewayCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxTier1GatewayCommand.java new file mode 100644 index 000000000000..90e4b3a25bdd --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxTier1GatewayCommand.java @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import java.util.Objects; + +public class CreateNsxTier1GatewayCommand extends NsxCommand { + + private Long networkResourceId; + private String networkResourceName; + private boolean isResourceVpc; + private boolean sourceNatEnabled; + + public CreateNsxTier1GatewayCommand(long domainId, long accountId, long zoneId, + Long networkResourceId, String networkResourceName, boolean isResourceVpc, + boolean sourceNatEnabled) { + super(domainId, accountId, zoneId); + this.networkResourceId = networkResourceId; + this.networkResourceName = networkResourceName; + this.isResourceVpc = isResourceVpc; + this.sourceNatEnabled = sourceNatEnabled; + } + + public Long getNetworkResourceId() { + return networkResourceId; + } + + public boolean isResourceVpc() { + return isResourceVpc; + } + + public String getNetworkResourceName() { + return networkResourceName; + } + + public boolean isSourceNatEnabled() { + return sourceNatEnabled; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + CreateNsxTier1GatewayCommand that = (CreateNsxTier1GatewayCommand) o; + return Objects.equals(networkResourceName, that.networkResourceName); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), networkResourceName); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNsxTier1NatRuleCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNsxTier1NatRuleCommand.java new file mode 100644 index 000000000000..c14be743ea08 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateOrUpdateNsxTier1NatRuleCommand.java @@ -0,0 +1,69 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import java.util.Objects; + +public class CreateOrUpdateNsxTier1NatRuleCommand extends NsxCommand { + + private String tier1GatewayName; + private String action; + private String translatedIpAddress; + private String natRuleId; + + public CreateOrUpdateNsxTier1NatRuleCommand(long domainId, long accountId, long zoneId, + String tier1GatewayName, String action, String translatedIpAddress, String natRuleId) { + super(domainId, accountId, zoneId); + this.tier1GatewayName = tier1GatewayName; + this.action = action; + this.translatedIpAddress = translatedIpAddress; + this.natRuleId = natRuleId; + } + + public String getTier1GatewayName() { + return tier1GatewayName; + } + + public String getAction() { + return action; + } + + public String getTranslatedIpAddress() { + return translatedIpAddress; + } + + public String getNatRuleId() { + return natRuleId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + CreateOrUpdateNsxTier1NatRuleCommand that = (CreateOrUpdateNsxTier1NatRuleCommand) o; + return Objects.equals(tier1GatewayName, that.tier1GatewayName) && Objects.equals(action, that.action) && Objects.equals(translatedIpAddress, that.translatedIpAddress) && Objects.equals(natRuleId, that.natRuleId); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), tier1GatewayName, action, translatedIpAddress, natRuleId); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxDistributedFirewallRulesCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxDistributedFirewallRulesCommand.java new file mode 100644 index 000000000000..ad88f23b3b18 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxDistributedFirewallRulesCommand.java @@ -0,0 +1,27 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import org.apache.cloudstack.resource.NsxNetworkRule; + +import java.util.List; + +public class DeleteNsxDistributedFirewallRulesCommand extends CreateNsxDistributedFirewallRulesCommand { + public DeleteNsxDistributedFirewallRulesCommand(long domainId, long accountId, long zoneId, Long vpcId, long networkId, List rules) { + super(domainId, accountId, zoneId, vpcId, networkId, rules); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxLoadBalancerRuleCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxLoadBalancerRuleCommand.java new file mode 100644 index 000000000000..72aa61fdca37 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxLoadBalancerRuleCommand.java @@ -0,0 +1,58 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import org.apache.cloudstack.resource.NsxLoadBalancerMember; + +import java.util.List; +import java.util.Objects; + +public class DeleteNsxLoadBalancerRuleCommand extends NsxNetworkCommand { + private long lbId; + List memberList; + + public DeleteNsxLoadBalancerRuleCommand(long domainId, long accountId, long zoneId, Long networkResourceId, + String networkResourceName, boolean isResourceVpc, + List memberList, long lbId, long vmId) { + super(domainId, accountId, zoneId, networkResourceId, networkResourceName, isResourceVpc, vmId); + this.lbId = lbId; + this.memberList = memberList; + } + + public long getLbId() { + return lbId; + } + + public List getMemberList() { return memberList; } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + DeleteNsxLoadBalancerRuleCommand that = (DeleteNsxLoadBalancerRuleCommand) o; + return lbId == that.lbId && Objects.equals(memberList, that.memberList); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), lbId, memberList); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxNatRuleCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxNatRuleCommand.java new file mode 100644 index 000000000000..c5231b19ac40 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxNatRuleCommand.java @@ -0,0 +1,73 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import com.cloud.network.Network; + +import java.util.Objects; + +public class DeleteNsxNatRuleCommand extends NsxNetworkCommand { + private Long ruleId; + private Network.Service service; + + private String privatePort; + private String protocol; + public DeleteNsxNatRuleCommand(long domainId, long accountId, long zoneId, Long networkResourceId, String networkResourceName, + boolean isResourceVpc, Long vmId, Long ruleId, String privatePort, String protocol) { + super(domainId, accountId, zoneId, networkResourceId, networkResourceName, isResourceVpc, vmId); + this.ruleId = ruleId; + this.privatePort = privatePort; + this.protocol = protocol; + } + + public Long getRuleId() { + return ruleId; + } + + public Network.Service getService() { + return service; + } + + public void setService(Network.Service service) { + this.service = service; + } + + public String getPrivatePort() { + return privatePort; + } + + public String getProtocol() { + return protocol; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + DeleteNsxNatRuleCommand that = (DeleteNsxNatRuleCommand) o; + return Objects.equals(ruleId, that.ruleId) && Objects.equals(service, that.service) && Objects.equals(privatePort, that.privatePort) && Objects.equals(protocol, that.protocol); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), ruleId, service, privatePort, protocol); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxSegmentCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxSegmentCommand.java new file mode 100644 index 000000000000..882b55388ff6 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxSegmentCommand.java @@ -0,0 +1,70 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import java.util.Objects; + +public class DeleteNsxSegmentCommand extends NsxCommand { + + private Long vpcId; + private String vpcName; + + private long networkId; + private String networkName; + + public DeleteNsxSegmentCommand(long domainId, long accountId, long zoneId, Long vpcId, + String vpcName, long networkId, String networkName) { + super(domainId, accountId, zoneId); + this.vpcId = vpcId; + this.vpcName = vpcName; + this.networkId = networkId; + this.networkName = networkName; + } + + public Long getVpcId() { + return vpcId; + } + + public String getVpcName() { + return vpcName; + } + + public long getNetworkId() { + return networkId; + } + + public String getNetworkName() { + return networkName; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + DeleteNsxSegmentCommand command = (DeleteNsxSegmentCommand) o; + return networkId == command.networkId && Objects.equals(vpcId, command.vpcId) && Objects.equals(vpcName, command.vpcName) && Objects.equals(networkName, command.networkName); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), vpcId, vpcName, networkId, networkName); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxTier1GatewayCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxTier1GatewayCommand.java new file mode 100644 index 000000000000..d05acc180026 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxTier1GatewayCommand.java @@ -0,0 +1,63 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import java.util.Objects; + +public class DeleteNsxTier1GatewayCommand extends NsxCommand { + + private Long networkResourceId; + private String networkResourceName; + private boolean isResourceVpc; + + public DeleteNsxTier1GatewayCommand(long domainId, long accountId, long zoneId, + Long networkResourceId, String networkResourceName, boolean isResourceVpc) { + super(domainId, accountId, zoneId); + this.networkResourceId = networkResourceId; + this.networkResourceName = networkResourceName; + this.isResourceVpc = isResourceVpc; + } + + public Long getNetworkResourceId() { + return networkResourceId; + } + + public String getNetworkResourceName() { + return networkResourceName; + } + + public boolean isResourceVpc() { + return isResourceVpc; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass() || !super.equals(o)) { + return false; + } + DeleteNsxTier1GatewayCommand that = (DeleteNsxTier1GatewayCommand) o; + return isResourceVpc == that.isResourceVpc && Objects.equals(networkResourceId, that.networkResourceId) && Objects.equals(networkResourceName, that.networkResourceName); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), networkResourceId, networkResourceName, isResourceVpc); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxCommand.java new file mode 100644 index 000000000000..7c5e3a1d9fa8 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxCommand.java @@ -0,0 +1,67 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import com.cloud.agent.api.Command; + +import java.util.Objects; + +public class NsxCommand extends Command { + private long zoneId; + private long accountId; + private long domainId; + + public NsxCommand() { + } + + public NsxCommand(long domainId, long accountId, long zoneId) { + this.zoneId = zoneId; + this.accountId = accountId; + this.domainId = domainId; + } + + public long getZoneId() { + return zoneId; + } + + public long getAccountId() { + return accountId; + } + + public long getDomainId() { + return domainId; + } + + @Override + public boolean executeInSequence() { + return false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + NsxCommand that = (NsxCommand) o; + return Objects.equals(zoneId, that.zoneId) && Objects.equals(accountId, that.accountId) && Objects.equals(domainId, that.domainId); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), zoneId, accountId, domainId); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxNetworkCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxNetworkCommand.java new file mode 100644 index 000000000000..4cad50db356b --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxNetworkCommand.java @@ -0,0 +1,117 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import java.util.Objects; + +public class NsxNetworkCommand extends NsxCommand { + private Long networkResourceId; + private String networkResourceName; + private boolean isResourceVpc; + private Long vmId; + private String publicIp; + private String vmIp; + + public NsxNetworkCommand(long domainId, long accountId, long zoneId, Long networkResourceId, String networkResourceName, + boolean isResourceVpc, Long vmId, String publicIp, String vmIp) { + super(domainId, accountId, zoneId); + this.networkResourceId = networkResourceId; + this.networkResourceName = networkResourceName; + this.isResourceVpc = isResourceVpc; + this.vmId = vmId; + this.publicIp = publicIp; + this.vmIp = vmIp; + } + + public NsxNetworkCommand(long domainId, long accountId, long zoneId, Long networkResourceId, String networkResourceName, + boolean isResourceVpc) { + super(domainId, accountId, zoneId); + this.networkResourceId = networkResourceId; + this.networkResourceName = networkResourceName; + this.isResourceVpc = isResourceVpc; + } + + public NsxNetworkCommand(long domainId, long accountId, long zoneId, Long networkResourceId, String networkResourceName, + boolean isResourceVpc, Long vmId) { + this(domainId, accountId, zoneId, networkResourceId, networkResourceName, isResourceVpc); + this.vmId = vmId; + } + + public Long getNetworkResourceId() { + return networkResourceId; + } + + public void setNetworkResourceId(long networkResourceId) { + this.networkResourceId = networkResourceId; + } + + public String getNetworkResourceName() { + return networkResourceName; + } + + public void setNetworkResourceName(String networkResourceName) { + this.networkResourceName = networkResourceName; + } + + public boolean isResourceVpc() { + return isResourceVpc; + } + + public void setResourceVpc(boolean resourceVpc) { + isResourceVpc = resourceVpc; + } + + public Long getVmId() { + return vmId; + } + + public void setVmId(Long vmId) { + this.vmId = vmId; + } + + public String getPublicIp() { + return publicIp; + } + + public void setPublicIp(String publicIp) { + this.publicIp = publicIp; + } + + public String getVmIp() { + return vmIp; + } + + public void setVmIp(String vmIp) { + this.vmIp = vmIp; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + NsxNetworkCommand that = (NsxNetworkCommand) o; + return networkResourceId == that.networkResourceId && vmId == that.vmId && + Objects.equals(networkResourceName, that.networkResourceName) && Objects.equals(publicIp, that.publicIp) + && Objects.equals(vmIp, that.vmIp); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), networkResourceId, networkResourceName, vmId, publicIp, vmIp); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/StartupNsxCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/StartupNsxCommand.java new file mode 100644 index 000000000000..22deacca3544 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/StartupNsxCommand.java @@ -0,0 +1,27 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.agent.api; + +import com.cloud.agent.api.StartupCommand; +import com.cloud.host.Host; + +public class StartupNsxCommand extends StartupCommand { + + public StartupNsxCommand() { + super(Host.Type.L2Networking); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/AddNsxControllerCmd.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/AddNsxControllerCmd.java new file mode 100644 index 000000000000..8e36599bf32a --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/AddNsxControllerCmd.java @@ -0,0 +1,130 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command; + +import com.cloud.network.nsx.NsxProvider; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.response.NsxControllerResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.service.NsxProviderService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; + + +@APICommand(name = AddNsxControllerCmd.APINAME, description = "Add NSX Controller to CloudStack", + responseObject = NsxControllerResponse.class, requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, since = "4.19.0") +public class AddNsxControllerCmd extends BaseCmd { + public static final String APINAME = "addNsxController"; + public static final Logger LOGGER = LoggerFactory.getLogger(AddNsxControllerCmd.class.getName()); + + @Inject + NsxProviderService nsxProviderService; + + @Parameter(name = ApiConstants.ZONE_ID, type = CommandType.UUID, entityType = ZoneResponse.class, required = true, + description = "the ID of zone") + private Long zoneId; + + @Parameter(name = ApiConstants.NAME, type = CommandType.STRING, required = true, description = "NSX controller / provider name") + private String name; + + @Parameter(name = ApiConstants.NSX_PROVIDER_HOSTNAME, type = CommandType.STRING, required = true, description = "NSX controller hostname / IP address") + private String hostname; + + @Parameter(name = ApiConstants.NSX_PROVIDER_PORT, type = CommandType.STRING, description = "NSX controller port") + private String port; + @Parameter(name = ApiConstants.USERNAME, type = CommandType.STRING, required = true, description = "Username to log into NSX controller") + private String username; + @Parameter(name = ApiConstants.PASSWORD, type = CommandType.STRING, required = true, description = "Password to login into NSX controller") + private String password; + + @Parameter(name = ApiConstants.TIER0_GATEWAY, type = CommandType.STRING, required = true, description = "Tier-0 Gateway address") + private String tier0Gateway; + + @Parameter(name = ApiConstants.EDGE_CLUSTER, type = CommandType.STRING, required = true, description = "Edge Cluster name") + private String edgeCluster; + + @Parameter(name = ApiConstants.TRANSPORT_ZONE, type = CommandType.STRING, required = true, description = "Transport Zone controls to which hosts a logical switch can reach") + private String transportZone; + + public NsxProviderService getNsxProviderService() { + return nsxProviderService; + } + + public Long getZoneId() { + return zoneId; + } + + public String getName() { + return name; + } + + public String getHostname() { + return hostname; + } + + public String getPort() { + return port; + } + + public String getUsername() { + return username; + } + + public String getPassword() { + return password; + } + + public String getTier0Gateway() { + return tier0Gateway; + } + + public String getEdgeCluster() { + return edgeCluster; + } + + public String getTransportZone() { + return transportZone; + } + + @Override + public void execute() throws ServerApiException { + NsxProvider nsxProvider = nsxProviderService.addProvider(this); + NsxControllerResponse nsxControllerResponse = + nsxProviderService.createNsxControllerResponse( + nsxProvider); + if (nsxControllerResponse == null) + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to add NSX controller"); + else { + nsxControllerResponse.setResponseName(getCommandName()); + setResponseObject(nsxControllerResponse); + } + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/DeleteNsxControllerCmd.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/DeleteNsxControllerCmd.java new file mode 100644 index 000000000000..5a3e55864008 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/DeleteNsxControllerCmd.java @@ -0,0 +1,87 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command; + +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.BaseCmd; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.response.NsxControllerResponse; +import org.apache.cloudstack.api.response.SuccessResponse; +import org.apache.cloudstack.service.NsxProviderService; + +import javax.inject.Inject; + +import static org.apache.cloudstack.api.command.DeleteNsxControllerCmd.APINAME; + +@APICommand(name = APINAME, description = "delete NSX Controller to CloudStack", + responseObject = NsxControllerResponse.class, requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, since = "4.19.0") +public class DeleteNsxControllerCmd extends BaseCmd { + public static final String APINAME = "deleteNsxController"; + + @Inject + protected NsxProviderService nsxProviderService; +///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + @Parameter(name = ApiConstants.NSX_CONTROLLER_ID, type = CommandType.UUID, entityType = NsxControllerResponse.class, + required = true, description = "NSX Controller ID") + private Long nsxControllerId; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + public Long getNsxControllerId() { + return nsxControllerId; + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + + @Override + public void execute() throws ServerApiException, ConcurrentOperationException { + try { + boolean deleted = nsxProviderService.deleteNsxController(getNsxControllerId()); + if (deleted) { + SuccessResponse response = new SuccessResponse(getCommandName()); + response.setResponseName(getCommandName()); + setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to remove NSX Controller from Zone"); + } + } catch (InvalidParameterValueException e) { + throw new ServerApiException(ApiErrorCode.PARAM_ERROR, e.getMessage()); + } catch (CloudRuntimeException e) { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, e.getMessage()); + } + } + + @Override + public long getEntityOwnerId() { + return 0; + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/ListNsxControllersCmd.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/ListNsxControllersCmd.java new file mode 100644 index 000000000000..94b5855e7830 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/command/ListNsxControllersCmd.java @@ -0,0 +1,68 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.command; + +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.utils.StringUtils; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.BaseListCmd; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.ListResponse; +import org.apache.cloudstack.api.response.NsxControllerResponse; +import org.apache.cloudstack.api.response.ZoneResponse; +import org.apache.cloudstack.context.CallContext; +import org.apache.cloudstack.service.NsxProviderService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.inject.Inject; +import java.util.List; + +import static org.apache.cloudstack.api.command.ListNsxControllersCmd.APINAME; + +@APICommand(name = APINAME, description = "list all NSX controllers added to CloudStack", + responseObject = NsxControllerResponse.class, requestHasSensitiveInfo = false, + responseHasSensitiveInfo = false, since = "4.19.0") +public class ListNsxControllersCmd extends BaseListCmd { + public static final String APINAME = "listNsxControllers"; + public static final Logger LOGGER = LoggerFactory.getLogger(ListNsxControllersCmd.class.getName()); + + @Inject + private NsxProviderService nsxProviderService; + + @Parameter(name = ApiConstants.ZONE_ID, description = "NSX controller added to the specific zone", + type = CommandType.UUID, entityType = ZoneResponse.class) + Long zoneId; + + @Override + public void execute() throws ServerApiException, ConcurrentOperationException { + List baseResponseList = nsxProviderService.listNsxProviders(zoneId); + List pagingList = StringUtils.applyPagination(baseResponseList, this.getStartIndex(), this.getPageSizeVal()); + ListResponse listResponse = new ListResponse<>(); + listResponse.setResponses(pagingList); + listResponse.setResponseName(getCommandName()); + setResponseObject(listResponse); + } + + @Override + public long getEntityOwnerId() { + return CallContext.current().getCallingAccount().getId(); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/response/NsxControllerResponse.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/response/NsxControllerResponse.java new file mode 100644 index 000000000000..910c5e115bf5 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/api/response/NsxControllerResponse.java @@ -0,0 +1,136 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.api.response; + +import com.cloud.network.nsx.NsxProvider; +import com.cloud.serializer.Param; +import com.google.gson.annotations.SerializedName; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.EntityReference; + +@EntityReference(value = {NsxProvider.class}) +public class NsxControllerResponse extends BaseResponse { + @SerializedName(ApiConstants.NSX_PROVIDER_UUID) + @Param(description = "NSX controller ID") + private String uuid; + @SerializedName(ApiConstants.NAME) + @Param(description = "NSX controller name") + private String name; + + @SerializedName(ApiConstants.ZONE_ID) + @Param(description = "Zone ID to which the NSX controller is associated with") + private String zoneId; + + @SerializedName(ApiConstants.ZONE_NAME) + @Param(description = "Zone name to which the NSX controller is associated with") + private String zoneName; + + @SerializedName(ApiConstants.HOST_NAME) + @Param(description = "NSX controller hostname or IP address") + private String hostname; + + @SerializedName(ApiConstants.PORT) + @Param(description = "NSX controller port") + private String port; + + @SerializedName(ApiConstants.TIER0_GATEWAY) + @Param(description = "The tier-0 gateway network. Tier-0 gateway is responsible for handling" + + " traffic between logical and physical networks" + ) + private String tier0Gateway; + + @SerializedName(ApiConstants.EDGE_CLUSTER) + @Param(description = "The name of the edge cluster. An edge cluster is a logical grouping of edge nodes in NSX") + private String edgeCluster; + + @SerializedName(ApiConstants.TRANSPORT_ZONE) + @Param(description = "The name of the transport zone. A transport zone controls to which hosts a logical switch can reach") + private String transportZone; + + public String getUuid() { + return uuid; + } + + public void setUuid(String uuid) { + this.uuid = uuid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getZoneId() { + return zoneId; + } + + public void setZoneId(String zoneId) { + this.zoneId = zoneId; + } + + public String getZoneName() { + return zoneName; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public String getHostname() { + return hostname; + } + + public void setHostname(String hostname) { + this.hostname = hostname; + } + + public String getPort() { + return port; + } + + public void setPort(String port) { + this.port = port; + } + + public String getTier0Gateway() { + return tier0Gateway; + } + + public void setTier0Gateway(String tier0Gateway) { + this.tier0Gateway = tier0Gateway; + } + + public String getEdgeCluster() { + return edgeCluster; + } + + public void setEdgeCluster(String edgeCluster) { + this.edgeCluster = edgeCluster; + } + + public String getTransportZone() { + return transportZone; + } + + public void setTransportZone(String transportZone) { + this.transportZone = transportZone; + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxLoadBalancerMember.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxLoadBalancerMember.java new file mode 100644 index 000000000000..00960ddb78a5 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxLoadBalancerMember.java @@ -0,0 +1,41 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.resource; + +public class NsxLoadBalancerMember { + private long vmId; + private String vmIp; + private int port; + + public NsxLoadBalancerMember(long vmId, String vmIp, int port) { + this.vmId = vmId; + this.vmIp = vmIp; + this.port = port; + } + + public long getVmId() { + return vmId; + } + + public String getVmIp() { + return vmIp; + } + + public int getPort() { + return port; + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java new file mode 100644 index 000000000000..c11141db9d42 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java @@ -0,0 +1,397 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.resource; + +import com.cloud.network.Network; + +import java.util.List; + +public class NsxNetworkRule { + + public enum NsxRuleAction { + ALLOW, DROP + } + + private long domainId; + private long accountId; + private long zoneId; + private Long networkResourceId; + private String networkResourceName; + private boolean isVpcResource; + private long vmId; + private long ruleId; + private String publicIp; + private String vmIp; + private String publicPort; + private String privatePort; + private String protocol; + private String algorithm; + private List memberList; + private NsxRuleAction aclAction; + private List sourceCidrList; + private List destinationCidrList; + private Integer icmpCode; + + private Integer icmpType; + private String trafficType; + private Network.Service service; + + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public Long getNetworkResourceId() { + return networkResourceId; + } + + public void setNetworkResourceId(Long networkResourceId) { + this.networkResourceId = networkResourceId; + } + + public String getNetworkResourceName() { + return networkResourceName; + } + + public void setNetworkResourceName(String networkResourceName) { + this.networkResourceName = networkResourceName; + } + + public boolean isVpcResource() { + return isVpcResource; + } + + public void setVpcResource(boolean vpcResource) { + isVpcResource = vpcResource; + } + + public long getVmId() { + return vmId; + } + + public void setVmId(long vmId) { + this.vmId = vmId; + } + + public long getRuleId() { + return ruleId; + } + + public void setRuleId(long ruleId) { + this.ruleId = ruleId; + } + + public String getPublicIp() { + return publicIp; + } + + public void setPublicIp(String publicIp) { + this.publicIp = publicIp; + } + + public String getVmIp() { + return vmIp; + } + + public void setVmIp(String vmIp) { + this.vmIp = vmIp; + } + + public String getPublicPort() { + return publicPort; + } + + public void setPublicPort(String publicPort) { + this.publicPort = publicPort; + } + + public String getPrivatePort() { + return privatePort; + } + + public void setPrivatePort(String privatePort) { + this.privatePort = privatePort; + } + + public String getProtocol() { + return protocol; + } + + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + public void setAlgorithm(String algorithm) { + this.algorithm = algorithm; + } + + public String getAlgorithm() { + return algorithm; + } + + public List getMemberList() { + return memberList; + } + + public void setMemberList(List memberList) { + this.memberList = memberList; + } + + public NsxRuleAction getAclAction() { + return aclAction; + } + + public void setAclAction(NsxRuleAction aclAction) { + this.aclAction = aclAction; + } + + public Network.Service getService() { + return service; + } + + public void setService(Network.Service service) { + this.service = service; + } + + public Integer getIcmpCode() { + return icmpCode; + } + + public void setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + } + + public Integer getIcmpType() { + return icmpType; + } + + public void setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + } + + public List getSourceCidrList() { + return sourceCidrList; + } + + public void setSourceCidrList(List sourceCidrList) { + this.sourceCidrList = sourceCidrList; + } + + public List getDestinationCidrList() { + return destinationCidrList; + } + + public void setDestinationCidrList(List destinationCidrList) { + this.destinationCidrList = destinationCidrList; + } + + public String getTrafficType() { + return trafficType; + } + + public void setTrafficType(String trafficType) { + this.trafficType = trafficType; + } + + public static final class Builder { + private long domainId; + private long accountId; + private long zoneId; + private Long networkResourceId; + private String networkResourceName; + private boolean isVpcResource; + private long vmId; + + private long ruleId; + private String publicIp; + private String vmIp; + private String publicPort; + private String privatePort; + private String protocol; + private String algorithm; + private List memberList; + private NsxRuleAction aclAction; + private List sourceCidrList; + private List destinationidrList; + private String trafficType; + private Integer icmpType; + private Integer icmpCode; + private Network.Service service; + + public Builder() { + // Default constructor + } + + public Builder setDomainId(long domainId) { + this.domainId = domainId; + return this; + } + + public Builder setAccountId(long accountId) { + this.accountId = accountId; + return this; + } + + public Builder setZoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public Builder setNetworkResourceId(Long networkResourceId) { + this.networkResourceId = networkResourceId; + return this; + } + + public Builder setNetworkResourceName(String networkResourceName) { + this.networkResourceName = networkResourceName; + return this; + } + + public Builder setVpcResource(boolean isVpcResource) { + this.isVpcResource = isVpcResource; + return this; + } + + + public Builder setVmId(long vmId) { + this.vmId = vmId; + return this; + } + + public Builder setRuleId(long ruleId) { + this.ruleId = ruleId; + return this; + } + + public Builder setPublicIp(String publicIp) { + this.publicIp = publicIp; + return this; + } + + public Builder setVmIp(String vmIp) { + this.vmIp = vmIp; + return this; + } + + public Builder setPublicPort(String publicPort) { + this.publicPort = publicPort; + return this; + } + + public Builder setPrivatePort(String privatePort) { + this.privatePort = privatePort; + return this; + } + + public Builder setProtocol(String protocol) { + this.protocol = protocol; + return this; + } + + public Builder setAlgorithm(String algorithm) { + this.algorithm = algorithm; + return this; + } + + public Builder setMemberList(List memberList) { + this.memberList = memberList; + return this; + } + + + public Builder setAclAction(NsxRuleAction aclAction) { + this.aclAction = aclAction; + return this; + } + + public Builder setTrafficType(String trafficType) { + this.trafficType = trafficType; + return this; + } + + public Builder setIcmpType(Integer icmpType) { + this.icmpType = icmpType; + return this; + } + + public Builder setIcmpCode(Integer icmpCode) { + this.icmpCode = icmpCode; + return this; + } + + public Builder setSourceCidrList(List sourceCidrList) { + this.sourceCidrList = sourceCidrList; + return this; + } + + public Builder setDestinationCidrList(List destinationCidrList) { + this.destinationidrList = destinationCidrList; + return this; + } + + public Builder setService(Network.Service service) { + this.service = service; + return this; + } + + public NsxNetworkRule build() { + NsxNetworkRule rule = new NsxNetworkRule(); + rule.setDomainId(this.domainId); + rule.setAccountId(this.accountId); + rule.setZoneId(this.zoneId); + rule.setNetworkResourceId(this.networkResourceId); + rule.setNetworkResourceName(this.networkResourceName); + rule.setVpcResource(this.isVpcResource); + rule.setVmId(this.vmId); + rule.setVmIp(this.vmIp); + rule.setPublicIp(this.publicIp); + rule.setPublicPort(this.publicPort); + rule.setPrivatePort(this.privatePort); + rule.setProtocol(this.protocol); + rule.setRuleId(this.ruleId); + rule.setAlgorithm(this.algorithm); + rule.setMemberList(this.memberList); + rule.setAclAction(this.aclAction); + rule.setIcmpType(this.icmpType); + rule.setIcmpCode(this.icmpCode); + rule.setSourceCidrList(this.sourceCidrList); + rule.setDestinationCidrList(this.destinationidrList); + rule.setTrafficType(this.trafficType); + rule.setService(service); + return rule; + } + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxOpObject.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxOpObject.java new file mode 100644 index 000000000000..bb411249b886 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxOpObject.java @@ -0,0 +1,129 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.resource; + +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.vpc.VpcVO; + +import java.util.Objects; + +public class NsxOpObject { + VpcVO vpcVO; + NetworkVO networkVO; + long accountId; + long domainId; + long zoneId; + + public VpcVO getVpcVO() { + return vpcVO; + } + + public void setVpcVO(VpcVO vpcVO) { + this.vpcVO = vpcVO; + } + + public NetworkVO getNetworkVO() { + return networkVO; + } + + public void setNetworkVO(NetworkVO networkVO) { + this.networkVO = networkVO; + } + + public long getAccountId() { + return accountId; + } + + public void setAccountId(long accountId) { + this.accountId = accountId; + } + + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public String getNetworkResourceName() { + return Objects.nonNull(vpcVO) ? vpcVO.getName() : networkVO.getName(); + } + + public boolean isVpcResource() { + return Objects.nonNull(vpcVO); + } + + public long getNetworkResourceId() { + return Objects.nonNull(vpcVO) ? vpcVO.getId() : networkVO.getId(); + } + + public static final class Builder { + VpcVO vpcVO; + NetworkVO networkVO; + long accountId; + long domainId; + long zoneId; + + public Builder() { + // Default constructor + } + + public Builder vpcVO(VpcVO vpcVO) { + this.vpcVO = vpcVO; + return this; + } + + public Builder networkVO(NetworkVO networkVO) { + this.networkVO = networkVO; + return this; + } + + public Builder domainId(long domainId) { + this.domainId = domainId; + return this; + } + + public Builder accountId(long accountId) { + this.accountId = accountId; + return this; + } + + public Builder zoneId(long zoneId) { + this.zoneId = zoneId; + return this; + } + + public NsxOpObject build() { + NsxOpObject object = new NsxOpObject(); + object.setVpcVO(this.vpcVO); + object.setNetworkVO(this.networkVO); + object.setDomainId(this.domainId); + object.setAccountId(this.accountId); + object.setZoneId(this.zoneId); + return object; + } + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java new file mode 100644 index 000000000000..cd1d481b9f8d --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxResource.java @@ -0,0 +1,486 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.resource; + +import com.cloud.agent.IAgentControl; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.PingCommand; +import com.cloud.agent.api.ReadyAnswer; +import com.cloud.agent.api.ReadyCommand; +import com.cloud.agent.api.StartupCommand; +import com.cloud.host.Host; +import com.cloud.network.Network; +import com.cloud.resource.ServerResource; +import com.cloud.utils.exception.CloudRuntimeException; + +import com.vmware.nsx.model.TransportZone; +import com.vmware.nsx.model.TransportZoneListResult; +import com.vmware.nsx_policy.model.Segment; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.StartupNsxCommand; +import org.apache.cloudstack.agent.api.CreateNsxDhcpRelayConfigCommand; +import org.apache.cloudstack.agent.api.CreateNsxDistributedFirewallRulesCommand; +import org.apache.cloudstack.agent.api.CreateNsxLoadBalancerRuleCommand; +import org.apache.cloudstack.agent.api.CreateNsxPortForwardRuleCommand; +import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand; +import org.apache.cloudstack.agent.api.CreateNsxStaticNatCommand; +import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand; +import org.apache.cloudstack.agent.api.DeleteNsxDistributedFirewallRulesCommand; +import org.apache.cloudstack.agent.api.DeleteNsxLoadBalancerRuleCommand; +import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand; +import org.apache.cloudstack.agent.api.DeleteNsxNatRuleCommand; +import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand; +import org.apache.cloudstack.service.NsxApiClient; +import org.apache.cloudstack.utils.NsxControllerUtils; +import org.apache.commons.collections.CollectionUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.naming.ConfigurationException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +public class NsxResource implements ServerResource { + protected Logger logger = LogManager.getLogger(getClass()); + private static final String DHCP_RELAY_CONFIGS_PATH_PREFIX = "/infra/dhcp-relay-configs"; + + private String name; + protected String hostname; + protected String username; + protected String password; + protected String guid; + protected String port; + protected String tier0Gateway; + protected String edgeCluster; + protected String transportZone; + protected String zoneId; + + protected NsxApiClient nsxApiClient; + + @Override + public Host.Type getType() { + return Host.Type.Routing; + } + @Override + public StartupCommand[] initialize() { + StartupNsxCommand sc = new StartupNsxCommand(); + sc.setGuid(guid); + sc.setName(name); + sc.setDataCenter(zoneId); + sc.setPod(""); + sc.setPrivateIpAddress(""); + sc.setStorageIpAddress(""); + sc.setVersion(""); + return new StartupCommand[] {sc}; + } + + @Override + public PingCommand getCurrentStatus(long id) { + return null; + } + + @Override + public Answer executeRequest(Command cmd) { + if (cmd instanceof ReadyCommand) { + return executeRequest((ReadyCommand) cmd); + } else if (cmd instanceof DeleteNsxTier1GatewayCommand) { + return executeRequest((DeleteNsxTier1GatewayCommand) cmd); + } else if (cmd instanceof DeleteNsxSegmentCommand) { + return executeRequest((DeleteNsxSegmentCommand) cmd); + } else if (cmd instanceof CreateNsxSegmentCommand) { + return executeRequest((CreateNsxSegmentCommand) cmd); + } else if (cmd instanceof CreateNsxTier1GatewayCommand) { + return executeRequest((CreateNsxTier1GatewayCommand) cmd); + } else if (cmd instanceof CreateNsxDhcpRelayConfigCommand) { + return executeRequest((CreateNsxDhcpRelayConfigCommand) cmd); + } else if (cmd instanceof CreateOrUpdateNsxTier1NatRuleCommand) { + return executeRequest((CreateOrUpdateNsxTier1NatRuleCommand) cmd); + } else if (cmd instanceof CreateNsxStaticNatCommand) { + return executeRequest((CreateNsxStaticNatCommand) cmd); + } else if (cmd instanceof DeleteNsxNatRuleCommand) { + return executeRequest((DeleteNsxNatRuleCommand) cmd); + } else if (cmd instanceof CreateNsxPortForwardRuleCommand) { + return executeRequest((CreateNsxPortForwardRuleCommand) cmd); + } else if (cmd instanceof CreateNsxLoadBalancerRuleCommand) { + return executeRequest((CreateNsxLoadBalancerRuleCommand) cmd); + } else if (cmd instanceof DeleteNsxLoadBalancerRuleCommand) { + return executeRequest((DeleteNsxLoadBalancerRuleCommand) cmd); + } else if (cmd instanceof DeleteNsxDistributedFirewallRulesCommand) { + return executeRequest((DeleteNsxDistributedFirewallRulesCommand) cmd); + } else if (cmd instanceof CreateNsxDistributedFirewallRulesCommand) { + return executeRequest((CreateNsxDistributedFirewallRulesCommand) cmd); + } else { + return Answer.createUnsupportedCommandAnswer(cmd); + } + } + + @Override + public void disconnected() { + // Do nothing + } + + @Override + public IAgentControl getAgentControl() { + return null; + } + + @Override + public void setAgentControl(IAgentControl agentControl) { + // Do nothing + } + + @Override + public String getName() { + return name; + } + + @Override + public void setName(String name) { + this.name = name; + } + + @Override + public void setConfigParams(Map params) { + // Do nothing + } + + @Override + public Map getConfigParams() { + return new HashMap<>(); + } + + @Override + public int getRunLevel() { + return 0; + } + + @Override + public void setRunLevel(int level) { + // Do nothing + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + hostname = (String) params.get("hostname"); + if (hostname == null) { + throw new ConfigurationException("Missing NSX hostname from params: " + params); + } + + port = (String) params.get("port"); + if (port == null) { + throw new ConfigurationException("Missing NSX port from params: " + params); + } + + username = (String) params.get("username"); + if (username == null) { + throw new ConfigurationException("Missing NSX username from params: " + params); + } + + password = (String) params.get("password"); + if (password == null) { + throw new ConfigurationException("Missing NSX password from params: " + params); + } + + this.name = (String) params.get("name"); + if (this.name == null) { + throw new ConfigurationException("Unable to find name"); + } + + guid = (String) params.get("guid"); + if (guid == null) { + throw new ConfigurationException("Unable to find the guid"); + } + + zoneId = (String) params.get("zoneId"); + if (zoneId == null) { + throw new ConfigurationException("Unable to find zone"); + } + + tier0Gateway = (String) params.get("tier0Gateway"); + if (tier0Gateway == null) { + throw new ConfigurationException("Missing NSX tier0 gateway"); + } + + edgeCluster = (String) params.get("edgeCluster"); + if (edgeCluster == null) { + throw new ConfigurationException("Missing NSX edgeCluster"); + } + + transportZone = (String) params.get("transportZone"); + if (transportZone == null) { + throw new ConfigurationException("Missing NSX transportZone"); + } + + nsxApiClient = new NsxApiClient(hostname, port, username, password.toCharArray()); + return true; + } + + private Answer executeRequest(CreateOrUpdateNsxTier1NatRuleCommand cmd) { + String tier1GatewayName = cmd.getTier1GatewayName(); + String action = cmd.getAction(); + String translatedIpAddress = cmd.getTranslatedIpAddress(); + String natRuleId = cmd.getNatRuleId(); + String natId = "USER"; + try { + nsxApiClient.createTier1NatRule(tier1GatewayName, natId, natRuleId, action, translatedIpAddress); + } catch (CloudRuntimeException e) { + String msg = String.format("Error creating the NAT rule with ID %s on Tier1 Gateway %s: %s", natRuleId, tier1GatewayName, e.getMessage()); + logger.error(msg, e); + return new NsxAnswer(cmd, e); + } + return new NsxAnswer(cmd, true, ""); + } + + private Answer executeRequest(CreateNsxDhcpRelayConfigCommand cmd) { + long datacenterId = cmd.getZoneId(); + long domainId = cmd.getDomainId(); + long accountId = cmd.getAccountId(); + Long vpcId = cmd.getVpcId(); + long networkId = cmd.getNetworkId(); + String vpcName = cmd.getVpcName(); + String networkName = cmd.getNetworkName(); + List addresses = cmd.getAddresses(); + + String dhcpRelayConfigName = NsxControllerUtils.getNsxDhcpRelayConfigId(datacenterId, domainId, accountId, vpcId, networkId); + + String msg = String.format("Creating DHCP relay config with name %s on network %s of VPC %s", + dhcpRelayConfigName, networkName, vpcName); + logger.debug(msg); + + try { + nsxApiClient.createDhcpRelayConfig(dhcpRelayConfigName, addresses); + } catch (CloudRuntimeException e) { + msg = String.format("Error creating the DHCP relay config with name %s: %s", dhcpRelayConfigName, e.getMessage()); + logger.error(msg, e); + return new NsxAnswer(cmd, e); + } + + String segmentName = NsxControllerUtils.getNsxSegmentId(domainId, accountId, datacenterId, vpcId, networkId); + String dhcpConfigPath = String.format("%s/%s", DHCP_RELAY_CONFIGS_PATH_PREFIX, dhcpRelayConfigName); + try { + Segment segment = nsxApiClient.getSegmentById(segmentName); + segment.setDhcpConfigPath(dhcpConfigPath); + nsxApiClient.updateSegment(segmentName, segment); + } catch (CloudRuntimeException e) { + msg = String.format("Error adding the DHCP relay config with name %s to the segment %s: %s", dhcpRelayConfigName, segmentName, e.getMessage()); + logger.error(msg); + return new NsxAnswer(cmd, e); + } + + return new NsxAnswer(cmd, true, ""); + } + + private Answer executeRequest(ReadyCommand cmd) { + return new ReadyAnswer(cmd); + } + + private Answer executeRequest(CreateNsxTier1GatewayCommand cmd) { + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), cmd.getNetworkResourceId(), cmd.isResourceVpc()); + boolean sourceNatEnabled = cmd.isSourceNatEnabled(); + try { + nsxApiClient.createTier1Gateway(tier1GatewayName, tier0Gateway, edgeCluster, sourceNatEnabled); + return new NsxAnswer(cmd, true, ""); + } catch (CloudRuntimeException e) { + String msg = String.format("Cannot create tier 1 gateway %s (%s: %s): %s", tier1GatewayName, + (cmd.isResourceVpc() ? "VPC" : "NETWORK"), cmd.getNetworkResourceName(), e.getMessage()); + logger.error(msg); + return new NsxAnswer(cmd, e); + } + } + + private Answer executeRequest(DeleteNsxTier1GatewayCommand cmd) { + String tier1Id = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), cmd.getNetworkResourceId(), cmd.isResourceVpc()); + String lbName = NsxControllerUtils.getLoadBalancerName(tier1Id); + try { + nsxApiClient.deleteLoadBalancer(lbName); + nsxApiClient.deleteTier1Gateway(tier1Id); + } catch (Exception e) { + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + private Answer executeRequest(CreateNsxSegmentCommand cmd) { + try { + String siteId = nsxApiClient.getDefaultSiteId(); + String enforcementPointPath = nsxApiClient.getDefaultEnforcementPointPath(siteId); + TransportZoneListResult transportZoneListResult = nsxApiClient.getTransportZones(); + if (CollectionUtils.isEmpty(transportZoneListResult.getResults())) { + String errorMsg = String.format("Failed to create network: %s as no transport zones were found in the linked NSX infrastructure", cmd.getNetworkName()); + logger.error(errorMsg); + return new NsxAnswer(cmd, new CloudRuntimeException(errorMsg)); + } + List transportZones = transportZoneListResult.getResults().stream().filter(tz -> tz.getDisplayName().equals(transportZone)).collect(Collectors.toList()); + if (CollectionUtils.isEmpty(transportZones)) { + String errorMsg = String.format("Failed to create network: %s as no transport zone of name %s was found in the linked NSX infrastructure", cmd.getNetworkName(), transportZone); + logger.error(errorMsg); + return new NsxAnswer(cmd, new CloudRuntimeException(errorMsg)); + } + + String segmentName = NsxControllerUtils.getNsxSegmentId(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), cmd.getVpcId(), cmd.getNetworkId()); + String gatewayAddress = cmd.getNetworkGateway() + "/" + cmd.getNetworkCidr().split("/")[1]; + + Long networkResourceId = Objects.isNull(cmd.getVpcId()) ? cmd.getNetworkId() : cmd.getVpcId(); + boolean isResourceVpc = !Objects.isNull(cmd.getVpcId()); + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), + cmd.getZoneId(), networkResourceId, isResourceVpc); + nsxApiClient.createSegment(segmentName, tier1GatewayName, gatewayAddress, enforcementPointPath, transportZones); + nsxApiClient.createGroupForSegment(segmentName); + } catch (Exception e) { + logger.error(String.format("Failed to create network: %s", cmd.getNetworkName())); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + private NsxAnswer executeRequest(DeleteNsxSegmentCommand cmd) { + String segmentName = NsxControllerUtils.getNsxSegmentId(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), + cmd.getVpcId(), cmd.getNetworkId()); + try { + nsxApiClient.deleteSegment(cmd.getZoneId(), cmd.getDomainId(), cmd.getAccountId(), cmd.getVpcId(), cmd.getNetworkId(), segmentName); + } catch (Exception e) { + logger.error(String.format("Failed to delete NSX segment %s: %s", segmentName, e.getMessage())); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + private NsxAnswer executeRequest(CreateNsxStaticNatCommand cmd) { + String staticNatRuleName = NsxControllerUtils.getStaticNatRuleName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), + cmd.getNetworkResourceId(), cmd.isResourceVpc()); + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), + cmd.getNetworkResourceId(), cmd.isResourceVpc()); + try { + nsxApiClient.createStaticNatRule(cmd.getNetworkResourceName(), tier1GatewayName, staticNatRuleName, cmd.getPublicIp(), cmd.getVmIp()); + } catch (Exception e) { + logger.error(String.format("Failed to add NSX static NAT rule %s for network: %s", staticNatRuleName, cmd.getNetworkResourceName())); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + private NsxAnswer executeRequest(CreateNsxPortForwardRuleCommand cmd) { + String ruleName = NsxControllerUtils.getPortForwardRuleName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), + cmd.getNetworkResourceId(), cmd.getRuleId(), cmd.isResourceVpc()); + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), + cmd.getNetworkResourceId(), cmd.isResourceVpc()); + try { + String privatePort = cmd.getPrivatePort(); + String service = privatePort.contains("-") ? nsxApiClient.getServicePath(ruleName, privatePort, cmd.getProtocol(), null, null) : + nsxApiClient.getNsxInfraServices(ruleName, privatePort, cmd.getProtocol(), null, null); + if (nsxApiClient.doesPfRuleExist(ruleName, tier1GatewayName)) { + logger.debug(String.format("Port forward rule for port: %s exits on NSX, not adding it again", privatePort)); + return new NsxAnswer(cmd, true, null); + } + nsxApiClient.createPortForwardingRule(ruleName, tier1GatewayName, cmd.getNetworkResourceName(), cmd.getPublicIp(), + cmd.getVmIp(), cmd.getPublicPort(), service); + } catch (Exception e) { + logger.error(String.format("Failed to add NSX port forward rule %s for network: %s", ruleName, cmd.getNetworkResourceName())); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + private NsxAnswer executeRequest(DeleteNsxNatRuleCommand cmd) { + String ruleName = null; + if (cmd.getService() == Network.Service.StaticNat) { + ruleName = NsxControllerUtils.getStaticNatRuleName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), + cmd.getNetworkResourceId(), cmd.isResourceVpc()); + } else if (cmd.getService() == Network.Service.PortForwarding) { + ruleName = NsxControllerUtils.getPortForwardRuleName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), + cmd.getNetworkResourceId(), cmd.getRuleId(), cmd.isResourceVpc()); + } + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), + cmd.getNetworkResourceId(), cmd.isResourceVpc()); + try { + nsxApiClient.deleteNatRule(cmd.getService(), cmd.getPrivatePort(), cmd.getProtocol(), + cmd.getNetworkResourceName(), tier1GatewayName, ruleName); + } catch (Exception e) { + logger.error(String.format("Failed to add NSX static NAT rule %s for network: %s", ruleName, cmd.getNetworkResourceName())); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + private NsxAnswer executeRequest(CreateNsxLoadBalancerRuleCommand cmd) { + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), + cmd.getNetworkResourceId(), cmd.isResourceVpc()); + String ruleName = NsxControllerUtils.getLoadBalancerRuleName(tier1GatewayName, cmd.getLbId()); + try { + nsxApiClient.createAndAddNsxLbVirtualServer(tier1GatewayName, cmd.getLbId(), cmd.getPublicIp(), cmd.getPublicPort(), + cmd.getMemberList(), cmd.getAlgorithm(), cmd.getProtocol(), cmd.getPrivatePort()); + } catch (Exception e) { + logger.error(String.format("Failed to add NSX load balancer rule %s for network: %s", ruleName, cmd.getNetworkResourceName())); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + private NsxAnswer executeRequest(DeleteNsxLoadBalancerRuleCommand cmd) { + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), + cmd.getZoneId(), cmd.getNetworkResourceId(), cmd.isResourceVpc()); + String ruleName = NsxControllerUtils.getLoadBalancerRuleName(tier1GatewayName, cmd.getLbId()); + try { + nsxApiClient.deleteNsxLbResources(tier1GatewayName, cmd.getLbId()); + } catch (Exception e) { + logger.error(String.format("Failed to add NSX load balancer rule %s for network: %s", ruleName, cmd.getNetworkResourceName())); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + private NsxAnswer executeRequest(CreateNsxDistributedFirewallRulesCommand cmd) { + String segmentName = NsxControllerUtils.getNsxSegmentId(cmd.getDomainId(), cmd.getAccountId(), + cmd.getZoneId(), cmd.getVpcId(), cmd.getNetworkId()); + List rules = cmd.getRules(); + try { + nsxApiClient.createSegmentDistributedFirewall(segmentName, rules); + } catch (Exception e) { + logger.error(String.format("Failed to create NSX distributed firewall %s: %s", segmentName, e.getMessage()), e); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + private NsxAnswer executeRequest(DeleteNsxDistributedFirewallRulesCommand cmd) { + String segmentName = NsxControllerUtils.getNsxSegmentId(cmd.getDomainId(), cmd.getAccountId(), + cmd.getZoneId(), cmd.getVpcId(), cmd.getNetworkId()); + List rules = cmd.getRules(); + try { + nsxApiClient.deleteDistributedFirewallRules(segmentName, rules); + } catch (Exception e) { + logger.error(String.format("Failed to delete NSX distributed firewall %s: %s", segmentName, e.getMessage()), e); + return new NsxAnswer(cmd, new CloudRuntimeException(e.getMessage())); + } + return new NsxAnswer(cmd, true, null); + } + + @Override + public boolean start() { + return true; + } + + @Override + public boolean stop() { + return true; + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java new file mode 100644 index 000000000000..d443b0e14e79 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxApiClient.java @@ -0,0 +1,1067 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.network.Network; +import com.cloud.utils.exception.CloudRuntimeException; +import com.vmware.nsx.model.TransportZone; +import com.vmware.nsx.model.TransportZoneListResult; +import com.vmware.nsx_policy.infra.DhcpRelayConfigs; +import com.vmware.nsx_policy.infra.LbAppProfiles; +import com.vmware.nsx_policy.infra.LbMonitorProfiles; +import com.vmware.nsx_policy.infra.LbPools; +import com.vmware.nsx_policy.infra.LbServices; +import com.vmware.nsx_policy.infra.LbVirtualServers; +import com.vmware.nsx_policy.infra.Segments; +import com.vmware.nsx_policy.infra.Services; +import com.vmware.nsx_policy.infra.Sites; +import com.vmware.nsx_policy.infra.Tier1s; +import com.vmware.nsx_policy.infra.domains.Groups; +import com.vmware.nsx_policy.infra.domains.SecurityPolicies; +import com.vmware.nsx_policy.infra.domains.groups.members.SegmentPorts; +import com.vmware.nsx_policy.infra.domains.security_policies.Rules; +import com.vmware.nsx_policy.infra.sites.EnforcementPoints; +import com.vmware.nsx_policy.infra.tier_0s.LocaleServices; +import com.vmware.nsx_policy.infra.tier_1s.nat.NatRules; +import com.vmware.nsx_policy.model.ApiError; +import com.vmware.nsx_policy.model.DhcpRelayConfig; +import com.vmware.nsx_policy.model.EnforcementPointListResult; +import com.vmware.nsx_policy.model.Group; +import com.vmware.nsx_policy.model.GroupListResult; +import com.vmware.nsx_policy.model.ICMPTypeServiceEntry; +import com.vmware.nsx_policy.model.L4PortSetServiceEntry; +import com.vmware.nsx_policy.model.LBAppProfileListResult; +import com.vmware.nsx_policy.model.LBMonitorProfileListResult; +import com.vmware.nsx_policy.model.LBPool; +import com.vmware.nsx_policy.model.LBPoolListResult; +import com.vmware.nsx_policy.model.LBPoolMember; +import com.vmware.nsx_policy.model.LBService; +import com.vmware.nsx_policy.model.LBTcpMonitorProfile; +import com.vmware.nsx_policy.model.LBUdpMonitorProfile; +import com.vmware.nsx_policy.model.LBVirtualServer; +import com.vmware.nsx_policy.model.LBVirtualServerListResult; +import com.vmware.nsx_policy.model.LocaleServicesListResult; +import com.vmware.nsx_policy.model.PathExpression; +import com.vmware.nsx_policy.model.PolicyGroupMembersListResult; +import com.vmware.nsx_policy.model.PolicyNatRule; +import com.vmware.nsx_policy.model.PolicyNatRuleListResult; +import com.vmware.nsx_policy.model.Rule; +import com.vmware.nsx_policy.model.SecurityPolicy; +import com.vmware.nsx_policy.model.Segment; +import com.vmware.nsx_policy.model.SegmentSubnet; +import com.vmware.nsx_policy.model.ServiceListResult; +import com.vmware.nsx_policy.model.SiteListResult; +import com.vmware.nsx_policy.model.Tier1; +import com.vmware.vapi.bindings.Service; +import com.vmware.vapi.bindings.Structure; +import com.vmware.vapi.bindings.StubConfiguration; +import com.vmware.vapi.cis.authn.SecurityContextFactory; +import com.vmware.vapi.client.ApiClient; +import com.vmware.vapi.client.ApiClients; +import com.vmware.vapi.client.Configuration; +import com.vmware.vapi.core.ExecutionContext; +import com.vmware.vapi.internal.protocol.RestProtocol; +import com.vmware.vapi.internal.protocol.client.rest.authn.BasicAuthenticationAppender; +import com.vmware.vapi.protocol.HttpConfiguration; +import com.vmware.vapi.std.errors.Error; +import org.apache.cloudstack.resource.NsxLoadBalancerMember; +import org.apache.cloudstack.resource.NsxNetworkRule; +import org.apache.cloudstack.utils.NsxControllerUtils; +import org.apache.commons.collections.CollectionUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static org.apache.cloudstack.utils.NsxControllerUtils.getServerPoolMemberName; +import static org.apache.cloudstack.utils.NsxControllerUtils.getServerPoolName; +import static org.apache.cloudstack.utils.NsxControllerUtils.getServiceName; +import static org.apache.cloudstack.utils.NsxControllerUtils.getVirtualServerName; +import static org.apache.cloudstack.utils.NsxControllerUtils.getServiceEntryName; +import static org.apache.cloudstack.utils.NsxControllerUtils.getLoadBalancerName; +import static org.apache.cloudstack.utils.NsxControllerUtils.getLoadBalancerAlgorithm; +import static org.apache.cloudstack.utils.NsxControllerUtils.getActiveMonitorProfileName; +import static org.apache.cloudstack.utils.NsxControllerUtils.getTier1GatewayName; + +public class NsxApiClient { + + protected ApiClient apiClient; + protected Function, Service> nsxService; + + public static final int RESPONSE_TIMEOUT_SECONDS = 60; + protected Logger logger = LogManager.getLogger(getClass()); + + // Constants + private static final String TIER_1_RESOURCE_TYPE = "Tier1"; + private static final String TIER_1_LOCALE_SERVICE_ID = "default"; + private static final String SEGMENT_RESOURCE_TYPE = "Segment"; + private static final String TIER_0_GATEWAY_PATH_PREFIX = "/infra/tier-0s/"; + private static final String TIER_1_GATEWAY_PATH_PREFIX = "/infra/tier-1s/"; + protected static final String SEGMENTS_PATH = "/infra/segments"; + protected static final String DEFAULT_DOMAIN = "default"; + protected static final String GROUPS_PATH_PREFIX = "/infra/domains/default/groups"; + // TODO: Pass as global / zone-level setting? + protected static final String NSX_LB_PASSIVE_MONITOR = "/infra/lb-monitor-profiles/default-passive-lb-monitor"; + protected static final String TCP_MONITOR_PROFILE = "LBTcpMonitorProfile"; + protected static final String UDP_MONITOR_PROFILE = "LBUdpMonitorProfile"; + protected static final String NAT_ID = "USER"; + + private enum PoolAllocation { ROUTING, LB_SMALL, LB_MEDIUM, LB_LARGE, LB_XLARGE } + + private enum HAMode { ACTIVE_STANDBY, ACTIVE_ACTIVE } + + private enum FailoverMode { PREEMPTIVE, NON_PREEMPTIVE } + + private enum AdminState { UP, DOWN } + + private enum TransportType { OVERLAY, VLAN } + + private enum NatId { USER, INTERNAL, DEFAULT } + + private enum NatAction {SNAT, DNAT, REFLEXIVE} + + private enum FirewallMatch { + MATCH_INTERNAL_ADDRESS, + MATCH_EXTERNAL_ADDRESS, + BYPASS + } + + public enum LBAlgorithm { + ROUND_ROBIN, + LEAST_CONNECTION, + IP_HASH + } + + private enum LBSize { + SMALL, + MEDIUM, + LARGE, + XLARGE + } + + private enum FirewallActions { + ALLOW, + DROP, + REJECT, + JUMP_TO_APPLICATION + } + + public enum RouteAdvertisementType { TIER1_STATIC_ROUTES, TIER1_CONNECTED, TIER1_NAT, + TIER1_LB_VIP, TIER1_LB_SNAT, TIER1_DNS_FORWARDER_IP, TIER1_IPSEC_LOCAL_ENDPOINT + } + + protected NsxApiClient() { + } + + public NsxApiClient(String hostname, String port, String username, char[] password) { + String controllerUrl = String.format("https://%s:%s", hostname, port); + HttpConfiguration.SslConfiguration.Builder sslConfigBuilder = new HttpConfiguration.SslConfiguration.Builder(); + sslConfigBuilder + .disableCertificateValidation() + .disableHostnameVerification(); + HttpConfiguration.SslConfiguration sslConfig = sslConfigBuilder.getConfig(); + + HttpConfiguration httpConfig = new HttpConfiguration.Builder() + .setSoTimeout(RESPONSE_TIMEOUT_SECONDS * 1000) + .setSslConfiguration(sslConfig).getConfig(); + + StubConfiguration stubConfig = new StubConfiguration(); + ExecutionContext.SecurityContext securityContext = SecurityContextFactory + .createUserPassSecurityContext(username, password); + stubConfig.setSecurityContext(securityContext); + + Configuration.Builder configBuilder = new Configuration.Builder() + .register(Configuration.HTTP_CONFIG_CFG, httpConfig) + .register(Configuration.STUB_CONFIG_CFG, stubConfig) + .register(RestProtocol.REST_REQUEST_AUTHENTICATOR_CFG, new BasicAuthenticationAppender()); + Configuration config = configBuilder.build(); + apiClient = ApiClients.newRestClient(controllerUrl, config); + nsxService = apiClient::createStub; + } + + public void createTier1NatRule(String tier1GatewayName, String natId, String natRuleId, + String action, String translatedIp) { + NatRules natRulesService = (NatRules) nsxService.apply(NatRules.class); + PolicyNatRule natPolicy = new PolicyNatRule.Builder() + .setAction(action) + .setTranslatedNetwork(translatedIp) + .build(); + natRulesService.patch(tier1GatewayName, natId, natRuleId, natPolicy); + } + + public void createDhcpRelayConfig(String dhcpRelayConfigName, List addresses) { + try { + DhcpRelayConfigs service = (DhcpRelayConfigs) nsxService.apply(DhcpRelayConfigs.class); + DhcpRelayConfig config = new DhcpRelayConfig.Builder() + .setServerAddresses(addresses) + .setId(dhcpRelayConfigName) + .setDisplayName(dhcpRelayConfigName) + .build(); + service.patch(dhcpRelayConfigName, config); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Error creating the DHCP relay config with name %s: %s", dhcpRelayConfigName, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(ae.getErrorMessage()); + } + } + + public Segment getSegmentById(String segmentName) { + try { + Segments segmentService = (Segments) nsxService.apply(Segments.class); + return segmentService.get(segmentName); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Error obtaining the segment with name %s: %s", segmentName, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(ae.getErrorMessage()); + } + } + + public void updateSegment(String segmentName, Segment segment) { + try { + Segments segmentService = (Segments) nsxService.apply(Segments.class); + segmentService.patch(segmentName, segment); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Error updating the segment with name %s: %s", segmentName, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(ae.getErrorMessage()); + } + } + + private Tier1 getTier1Gateway(String tier1GatewayId) { + try { + Tier1s tier1service = (Tier1s) nsxService.apply(Tier1s.class); + return tier1service.get(tier1GatewayId); + } catch (Exception e) { + logger.debug(String.format("NSX Tier-1 gateway with name: %s not found", tier1GatewayId)); + } + return null; + } + + private List getTier0LocalServices(String tier0Gateway) { + try { + LocaleServices tier0LocaleServices = (LocaleServices) nsxService.apply(LocaleServices.class); + LocaleServicesListResult result = tier0LocaleServices.list(tier0Gateway, null, false, null, null, null, null); + return result.getResults(); + } catch (Exception e) { + throw new CloudRuntimeException(String.format("Failed to fetch locale services for tier gateway %s due to %s", tier0Gateway, e.getMessage())); + } + } + + /** + * To instantiate Tier-1 in Edge Cluster + */ + private void createTier1LocaleServices(String tier1Id, String edgeCluster, String tier0Gateway) { + try { + List localeServices = getTier0LocalServices(tier0Gateway); + com.vmware.nsx_policy.infra.tier_1s.LocaleServices tier1LocalService = (com.vmware.nsx_policy.infra.tier_1s.LocaleServices) nsxService.apply(com.vmware.nsx_policy.infra.tier_1s.LocaleServices.class); + com.vmware.nsx_policy.model.LocaleServices localeService = new com.vmware.nsx_policy.model.LocaleServices.Builder() + .setEdgeClusterPath(localeServices.get(0).getEdgeClusterPath()).build(); + tier1LocalService.patch(tier1Id, TIER_1_LOCALE_SERVICE_ID, localeService); + } catch (Error error) { + throw new CloudRuntimeException(String.format("Failed to instantiate tier-1 gateway %s in edge cluster %s", tier1Id, edgeCluster)); + } + } + + private List getRouterAdvertisementTypeList(boolean sourceNatEnabled) { + List types = new ArrayList<>(); + types.add(RouteAdvertisementType.TIER1_IPSEC_LOCAL_ENDPOINT.name()); + types.add(RouteAdvertisementType.TIER1_LB_VIP.name()); + types.add(RouteAdvertisementType.TIER1_NAT.name()); + if (!sourceNatEnabled) { + types.add(RouteAdvertisementType.TIER1_CONNECTED.name()); + } + return types; + } + + public void createTier1Gateway(String name, String tier0Gateway, String edgeCluster, boolean sourceNatEnabled) throws CloudRuntimeException { + String tier0GatewayPath = TIER_0_GATEWAY_PATH_PREFIX + tier0Gateway; + Tier1 tier1 = getTier1Gateway(name); + if (tier1 != null) { + logger.info(String.format("VPC network with name %s exists in NSX zone", name)); + return; + } + + List routeAdvertisementTypes = getRouterAdvertisementTypeList(sourceNatEnabled); + + Tier1s tier1service = (Tier1s) nsxService.apply(Tier1s.class); + tier1 = new Tier1.Builder() + .setTier0Path(tier0GatewayPath) + .setResourceType(TIER_1_RESOURCE_TYPE) + .setPoolAllocation(PoolAllocation.ROUTING.name()) + .setHaMode(HAMode.ACTIVE_STANDBY.name()) + .setFailoverMode(FailoverMode.PREEMPTIVE.name()) + .setRouteAdvertisementTypes(routeAdvertisementTypes) + .setId(name) + .setDisplayName(name) + .build(); + try { + tier1service.patch(name, tier1); + createTier1LocaleServices(name, edgeCluster, tier0Gateway); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Error creating tier 1 gateway %s: %s", name, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public void deleteTier1Gateway(String tier1Id) { + com.vmware.nsx_policy.infra.tier_1s.LocaleServices localeService = (com.vmware.nsx_policy.infra.tier_1s.LocaleServices) + nsxService.apply(com.vmware.nsx_policy.infra.tier_1s.LocaleServices.class); + if (getTier1Gateway(tier1Id) == null) { + logger.warn(String.format("The Tier 1 Gateway %s does not exist, cannot be removed", tier1Id)); + return; + } + removeTier1GatewayNatRules(tier1Id); + localeService.delete(tier1Id, TIER_1_LOCALE_SERVICE_ID); + Tier1s tier1service = (Tier1s) nsxService.apply(Tier1s.class); + tier1service.delete(tier1Id); + } + + private void removeTier1GatewayNatRules(String tier1Id) { + NatRules natRulesService = (NatRules) nsxService.apply(NatRules.class); + PolicyNatRuleListResult result = natRulesService.list(tier1Id, NAT_ID, null, false, null, null, null, null); + List natRules = result.getResults(); + if (CollectionUtils.isEmpty(natRules)) { + logger.debug(String.format("Didn't find any NAT rule to remove on the Tier 1 Gateway %s", tier1Id)); + } else { + for (PolicyNatRule natRule : natRules) { + logger.debug(String.format("Removing NAT rule %s from Tier 1 Gateway %s", natRule.getId(), tier1Id)); + natRulesService.delete(tier1Id, NAT_ID, natRule.getId()); + } + } + + } + + public String getDefaultSiteId() { + SiteListResult sites = getSites(); + if (CollectionUtils.isEmpty(sites.getResults())) { + String errorMsg = "No sites are found in the linked NSX infrastructure"; + logger.error(errorMsg); + throw new CloudRuntimeException(errorMsg); + } + return sites.getResults().get(0).getId(); + } + + protected SiteListResult getSites() { + try { + Sites sites = (Sites) nsxService.apply(Sites.class); + return sites.list(null, false, null, null, null, null); + } catch (Exception e) { + throw new CloudRuntimeException(String.format("Failed to fetch sites list due to %s", e.getMessage())); + } + } + + public String getDefaultEnforcementPointPath(String siteId) { + EnforcementPointListResult epList = getEnforcementPoints(siteId); + if (CollectionUtils.isEmpty(epList.getResults())) { + String errorMsg = String.format("No enforcement points are found in the linked NSX infrastructure for site ID %s", siteId); + logger.error(errorMsg); + throw new CloudRuntimeException(errorMsg); + } + return epList.getResults().get(0).getPath(); + } + + protected EnforcementPointListResult getEnforcementPoints(String siteId) { + try { + EnforcementPoints enforcementPoints = (EnforcementPoints) nsxService.apply(EnforcementPoints.class); + return enforcementPoints.list(siteId, null, false, null, null, null, null); + } catch (Exception e) { + throw new CloudRuntimeException(String.format("Failed to fetch enforcement points due to %s", e.getMessage())); + } + } + + public TransportZoneListResult getTransportZones() { + try { + com.vmware.nsx.TransportZones transportZones = (com.vmware.nsx.TransportZones) nsxService.apply(com.vmware.nsx.TransportZones.class); + return transportZones.list(null, null, true, null, null, null, null, null, TransportType.OVERLAY.name(), null); + } catch (Exception e) { + throw new CloudRuntimeException(String.format("Failed to fetch transport zones due to %s", e.getMessage())); + } + } + + public void createSegment(String segmentName, String tier1GatewayName, String gatewayAddress, String enforcementPointPath, + List transportZones) { + try { + Segments segmentService = (Segments) nsxService.apply(Segments.class); + SegmentSubnet subnet = new SegmentSubnet.Builder() + .setGatewayAddress(gatewayAddress) + .build(); + Segment segment = new Segment.Builder() + .setResourceType(SEGMENT_RESOURCE_TYPE) + .setId(segmentName) + .setDisplayName(segmentName) + .setConnectivityPath(TIER_1_GATEWAY_PATH_PREFIX + tier1GatewayName) + .setAdminState(AdminState.UP.name()) + .setSubnets(List.of(subnet)) + .setTransportZonePath(enforcementPointPath + "/transport-zones/" + transportZones.get(0).getId()) + .build(); + segmentService.patch(segmentName, segment); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Error creating segment %s: %s", segmentName, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public void deleteSegment(long zoneId, long domainId, long accountId, Long vpcId, long networkId, String segmentName) { + try { + removeSegmentDistributedFirewallRules(segmentName); + if (Objects.isNull(vpcId)) { + String t1GatewayName = getTier1GatewayName(domainId, accountId, zoneId, networkId, false); + deleteLoadBalancer(getLoadBalancerName(t1GatewayName)); + } + removeSegment(segmentName); + DhcpRelayConfigs dhcpRelayConfig = (DhcpRelayConfigs) nsxService.apply(DhcpRelayConfigs.class); + String dhcpRelayConfigId = NsxControllerUtils.getNsxDhcpRelayConfigId(zoneId, domainId, accountId, vpcId, networkId); + logger.debug(String.format("Removing the DHCP relay config with ID %s", dhcpRelayConfigId)); + dhcpRelayConfig.delete(dhcpRelayConfigId); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Error deleting segment %s: %s", segmentName, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + protected void removeSegment(String segmentName) { + logger.debug(String.format("Removing the segment with ID %s", segmentName)); + Segments segmentService = (Segments) nsxService.apply(Segments.class); + String errMsg = String.format("The segment with ID %s is not found, skipping removal", segmentName); + try { + Segment segment = segmentService.get(segmentName); + if (segment == null) { + logger.warn(errMsg); + return; + } + } catch (Exception e) { + logger.warn(errMsg); + return; + } + String siteId = getDefaultSiteId(); + String enforcementPointPath = getDefaultEnforcementPointPath(siteId); + SegmentPorts segmentPortsService = (SegmentPorts) nsxService.apply(SegmentPorts.class); + PolicyGroupMembersListResult segmentPortsList = getSegmentPortList(segmentPortsService, segmentName, enforcementPointPath); + Long portCount = segmentPortsList.getResultCount(); + portCount = retrySegmentDeletion(segmentPortsService, portCount, segmentName, enforcementPointPath); + logger.info("Port count: " + portCount); + if (portCount == 0L) { + logger.debug(String.format("Removing the segment with ID %s", segmentName)); + removeGroupForSegment(segmentName); + segmentService.delete(segmentName); + } else { + String msg = String.format("Cannot remove the NSX segment %s because there are still %s port group(s) attached to it", segmentName, portCount); + logger.debug(msg); + throw new CloudRuntimeException(msg); + } + } + + private PolicyGroupMembersListResult getSegmentPortList(SegmentPorts segmentPortsService, String segmentName, String enforcementPointPath) { + return segmentPortsService.list(DEFAULT_DOMAIN, segmentName, null, enforcementPointPath, + false, null, 50L, false, null); + } + + private Long retrySegmentDeletion(SegmentPorts segmentPortsService, Long portCount, String segmentName, String enforcementPointPath) { + int retries = 20; + int count = 1; + do { + try { + logger.info("Waiting for all port groups to be unlinked from the segment - Attempt: " + count++ + " Waiting for 5 secs"); + Thread.sleep(5000); + portCount = getSegmentPortList(segmentPortsService, segmentName, enforcementPointPath).getResultCount(); + retries--; + } catch (InterruptedException e) { + throw new CloudRuntimeException(String.format("Unable to delete segment %s due to: %s", segmentName, e.getLocalizedMessage())); + } + } while (retries > 0 && portCount > 0); + return portCount; + } + + public void createStaticNatRule(String vpcName, String tier1GatewayName, + String ruleName, String publicIp, String vmIp) { + try { + NatRules natService = (NatRules) nsxService.apply(NatRules.class); + PolicyNatRule rule = new PolicyNatRule.Builder() + .setId(ruleName) + .setDisplayName(ruleName) + .setAction(NatAction.DNAT.name()) + .setFirewallMatch(FirewallMatch.MATCH_INTERNAL_ADDRESS.name()) + .setDestinationNetwork(publicIp) + .setTranslatedNetwork(vmIp) + .setEnabled(true) + .build(); + + logger.debug(String.format("Creating NSX static NAT rule %s for tier-1 gateway %s (VPC: %s)", ruleName, tier1GatewayName, vpcName)); + natService.patch(tier1GatewayName, NatId.USER.name(), ruleName, rule); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Error creating NSX Static NAT rule %s for tier-1 gateway %s (VPC: %s), due to %s", + ruleName, tier1GatewayName, vpcName, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public void deleteNatRule(Network.Service service, String privatePort, String protocol, String networkName, String tier1GatewayName, String ruleName) { + try { + NatRules natService = (NatRules) nsxService.apply(NatRules.class); + logger.debug(String.format("Deleting NSX static NAT rule %s for tier-1 gateway %s (network: %s)", ruleName, tier1GatewayName, networkName)); + // delete NAT rule + natService.delete(tier1GatewayName, NatId.USER.name(), ruleName); + if (service == Network.Service.PortForwarding) { + String svcName = getServiceName(ruleName, privatePort, protocol, null, null); + // Delete service + Services services = (Services) nsxService.apply(Services.class); + services.delete(svcName); + } + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to delete NSX Static NAT rule %s for tier-1 gateway %s (VPC: %s), due to %s", + ruleName, tier1GatewayName, networkName, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public void createPortForwardingRule(String ruleName, String tier1GatewayName, String networkName, String publicIp, + String vmIp, String publicPort, String service) { + try { + NatRules natService = (NatRules) nsxService.apply(NatRules.class); + logger.debug(String.format("Creating NSX Port-Forwarding NAT %s for network %s", ruleName, networkName)); + PolicyNatRule rule = new PolicyNatRule.Builder() + .setId(ruleName) + .setDisplayName(ruleName) + .setAction(NatAction.DNAT.name()) + .setFirewallMatch(FirewallMatch.MATCH_INTERNAL_ADDRESS.name()) + .setDestinationNetwork(publicIp) + .setTranslatedNetwork(vmIp) + .setTranslatedPorts(String.valueOf(publicPort)) + .setService(service) + .setEnabled(true) + .build(); + natService.patch(tier1GatewayName, NatId.USER.name(), ruleName, rule); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to add NSX Port-forward rule %s for network: %s, due to %s", + ruleName, networkName, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public boolean doesPfRuleExist(String ruleName, String tier1GatewayName) { + try { + NatRules natService = (NatRules) nsxService.apply(NatRules.class); + PolicyNatRule rule = natService.get(tier1GatewayName, NAT_ID, ruleName); + return !Objects.isNull(rule); + } catch (Error error) { + logger.debug(String.format("Found a port forward rule named: %s on NSX", ruleName)); + return false; + } + } + + List getLbPoolMembers(List memberList, String tier1GatewayName) { + List members = new ArrayList<>(); + for (NsxLoadBalancerMember member : memberList) { + try { + String serverPoolMemberName = getServerPoolMemberName(tier1GatewayName, member.getVmId()); + LBPoolMember lbPoolMember = new LBPoolMember.Builder() + .setDisplayName(serverPoolMemberName) + .setIpAddress(member.getVmIp()) + .setPort(String.valueOf(member.getPort())) + .build(); + members.add(lbPoolMember); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to create NSX LB pool members, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + return members; + } + public void createNsxLbServerPool(List memberList, String tier1GatewayName, String lbServerPoolName, + String algorithm, String privatePort, String protocol) { + try { + String activeMonitorPath = getLbActiveMonitorPath(lbServerPoolName, privatePort, protocol); + List members = getLbPoolMembers(memberList, tier1GatewayName); + LbPools lbPools = (LbPools) nsxService.apply(LbPools.class); + LBPool lbPool = new LBPool.Builder() + .setId(lbServerPoolName) + .setDisplayName(lbServerPoolName) + .setAlgorithm(getLoadBalancerAlgorithm(algorithm)) + .setMembers(members) + .setPassiveMonitorPath(NSX_LB_PASSIVE_MONITOR) + .setActiveMonitorPaths(List.of(activeMonitorPath)) + .build(); + lbPools.patch(lbServerPoolName, lbPool); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to create NSX LB server pool, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + private String getLbActiveMonitorPath(String lbServerPoolName, String port, String protocol) { + LbMonitorProfiles lbActiveMonitor = (LbMonitorProfiles) nsxService.apply(LbMonitorProfiles.class); + String lbMonitorProfileId = getActiveMonitorProfileName(lbServerPoolName, port, protocol); + if ("TCP".equals(protocol.toUpperCase(Locale.ROOT))) { + LBTcpMonitorProfile lbTcpMonitorProfile = new LBTcpMonitorProfile.Builder(TCP_MONITOR_PROFILE) + .setDisplayName(lbMonitorProfileId) + .setMonitorPort(Long.parseLong(port)) + .build(); + lbActiveMonitor.patch(lbMonitorProfileId, lbTcpMonitorProfile); + } else if ("UDP".equals(protocol.toUpperCase(Locale.ROOT))) { + LBUdpMonitorProfile lbUdpMonitorProfile = new LBUdpMonitorProfile.Builder(UDP_MONITOR_PROFILE) + .setDisplayName(lbMonitorProfileId) + .setMonitorPort(Long.parseLong(port)) + .setSend("") + .setReceive("") + .build(); + lbActiveMonitor.patch(lbMonitorProfileId, lbUdpMonitorProfile); + } + + LBMonitorProfileListResult listResult = listLBActiveMonitors(lbActiveMonitor); + Optional monitorProfile = listResult.getResults().stream().filter(profile -> profile._getDataValue().getField("id").toString().equals(lbMonitorProfileId)).findFirst(); + return monitorProfile.map(structure -> structure._getDataValue().getField("path").toString()).orElse(null); + } + + LBMonitorProfileListResult listLBActiveMonitors(LbMonitorProfiles lbActiveMonitor) { + return lbActiveMonitor.list(null, false, null, null, null, null); + } + + public void createNsxLoadBalancer(String tier1GatewayName) { + try { + String lbName = getLoadBalancerName(tier1GatewayName); + LbServices lbServices = (LbServices) nsxService.apply(LbServices.class); + LBService lbService = getLbService(lbName); + if (Objects.nonNull(lbService)) { + return; + } + lbService = new LBService.Builder() + .setId(lbName) + .setDisplayName(lbName) + .setEnabled(true) + .setSize(LBSize.SMALL.name()) + .setConnectivityPath(TIER_1_GATEWAY_PATH_PREFIX + tier1GatewayName) + .build(); + lbServices.patch(lbName, lbService); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to create NSX load balancer, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public void createAndAddNsxLbVirtualServer(String tier1GatewayName, long lbId, String publicIp, String publicPort, + List memberList, String algorithm, String protocol, String privatePort) { + try { + String lbServerPoolName = getServerPoolName(tier1GatewayName, lbId); + createNsxLbServerPool(memberList, tier1GatewayName, lbServerPoolName, algorithm, privatePort, protocol); + createNsxLoadBalancer(tier1GatewayName); + + String lbVirtualServerName = getVirtualServerName(tier1GatewayName, lbId); + String lbServiceName = getLoadBalancerName(tier1GatewayName); + LbVirtualServers lbVirtualServers = (LbVirtualServers) nsxService.apply(LbVirtualServers.class); + if (Objects.nonNull(getLbVirtualServerService(lbVirtualServers, lbServiceName))) { + return; + } + LBVirtualServer lbVirtualServer = new LBVirtualServer.Builder() + .setId(lbVirtualServerName) + .setDisplayName(lbVirtualServerName) + .setApplicationProfilePath(getLbProfileForProtocol(protocol)) + .setIpAddress(publicIp) + .setLbServicePath(getLbPath(lbServiceName)) + .setPoolPath(getLbPoolPath(lbServerPoolName)) + .setPorts(List.of(publicPort)) + .build(); + lbVirtualServers.patch(lbVirtualServerName, lbVirtualServer); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to create and add NSX virtual server to the Load Balancer, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + private LBVirtualServer getLbVirtualServerService(LbVirtualServers lbVirtualServers, String lbVSName) { + try { + LBVirtualServer lbVirtualServer = lbVirtualServers.get(lbVSName); + if (Objects.nonNull(lbVirtualServer)) { + return lbVirtualServer; + } + } catch (Exception e) { + logger.debug(String.format("Found an LB virtual server named: %s on NSX", lbVSName)); + return null; + } + return null; + } + + public void deleteNsxLbResources(String tier1GatewayName, long lbId) { + try { + // Delete associated Virtual servers + LbVirtualServers lbVirtualServers = (LbVirtualServers) nsxService.apply(LbVirtualServers.class); + String lbVirtualServerName = getVirtualServerName(tier1GatewayName, lbId); + lbVirtualServers.delete(lbVirtualServerName, false); + + // Delete LB pool + LbPools lbPools = (LbPools) nsxService.apply(LbPools.class); + String lbServerPoolName = getServerPoolName(tier1GatewayName, lbId); + lbPools.delete(lbServerPoolName, false); + + // delete associated LB Active monitor profile + LbMonitorProfiles lbActiveMonitor = (LbMonitorProfiles) nsxService.apply(LbMonitorProfiles.class); + LBMonitorProfileListResult listResult = listLBActiveMonitors(lbActiveMonitor); + List profileIds = listResult.getResults().stream().filter(profile -> profile._getDataValue().getField("id").toString().contains(lbServerPoolName)) + .map(profile -> profile._getDataValue().getField("id").toString()).collect(Collectors.toList()); + for(String profileId : profileIds) { + lbActiveMonitor.delete(profileId, true); + } + // Delete load balancer + LBVirtualServerListResult lbVsListResult = lbVirtualServers.list(null, null, null, null, null, null); + LBPoolListResult lbPoolListResult = lbPools.list(null, null, null, null, null, null); + if (CollectionUtils.isEmpty(lbVsListResult.getResults()) && CollectionUtils.isEmpty(lbPoolListResult.getResults())) { + String lbName = getLoadBalancerName(tier1GatewayName); + deleteLoadBalancer(lbName); + } + + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to delete NSX Load Balancer resources, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public void deleteLoadBalancer(String lbName) { + LbServices lbServices = (LbServices) nsxService.apply(LbServices.class); + lbServices.delete(lbName, true); + } + + private String getLbPoolPath(String lbPoolName) { + try { + LbPools lbPools = (LbPools) nsxService.apply(LbPools.class); + LBPool lbPool = lbPools.get(lbPoolName); + return Objects.nonNull(lbPool) ? lbPool.getPath() : null; + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to get NSX LB server pool, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + private LBService getLbService(String lbName) { + try { + LbServices lbServices = (LbServices) nsxService.apply(LbServices.class); + LBService lbService = lbServices.get(lbName); + if (Objects.nonNull(lbService)) { + return lbService; + } + } catch (Exception e) { + return null; + } + return null; + } + + private String getLbPath(String lbServiceName) { + try { + LbServices lbServices = (LbServices) nsxService.apply(LbServices.class); + LBService lbService = lbServices.get(lbServiceName); + return Objects.nonNull(lbService) ? lbService.getPath() : null; + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to get NSX LB server pool, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + private String getLbProfileForProtocol(String protocol) { + try { + LbAppProfiles lbAppProfiles = (LbAppProfiles) nsxService.apply(LbAppProfiles.class); + LBAppProfileListResult lbAppProfileListResults = lbAppProfiles.list(null, null, + null, null, null, null); + Optional appProfile = lbAppProfileListResults.getResults().stream().filter(profile -> profile._getDataValue().getField("path").toString().contains(protocol.toLowerCase(Locale.ROOT))).findFirst(); + return appProfile.map(structure -> structure._getDataValue().getField("path").toString()).orElse(null); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to list NSX LB App profiles, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public String getNsxInfraServices(String ruleName, String port, String protocol, Integer icmpType, Integer icmpCode) { + try { + Services service = (Services) nsxService.apply(Services.class); + + // Find default service if present + ServiceListResult serviceList = service.list(null, true, false, null, null, null, null); + + List services = serviceList.getResults(); + List matchedDefaultSvc = services.parallelStream().filter(svc -> + (svc.getServiceEntries().get(0)._getDataValue().getField("resource_type").toString().equals("L4PortSetServiceEntry")) && + svc.getServiceEntries().get(0)._getDataValue().getField("destination_ports").toString().equals("["+port+"]") + && svc.getServiceEntries().get(0)._getDataValue().getField("l4_protocol").toString().equals(protocol)) + .map(svc -> svc.getServiceEntries().get(0)._getDataValue().getField("parent_path").toString()) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(matchedDefaultSvc)) { + return matchedDefaultSvc.get(0); + } + + // Else, find if there's a service matching the rule name + String servicePath = getServiceById(ruleName); + if (Objects.nonNull(servicePath)) { + return servicePath; + } + + // Else, create a service entry + return getServicePath(ruleName, port, protocol, icmpType, icmpCode); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to list NSX infra service, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + + private com.vmware.nsx_policy.model.Service getInfraService(String ruleName, String port, String protocol, Integer icmpType, Integer icmpCode) { + Services service = (Services) nsxService.apply(Services.class); + String serviceName = getServiceName(ruleName, port, protocol, icmpType, icmpCode); + createNsxInfraService(service, serviceName, ruleName, port, protocol, icmpType, icmpCode); + return service.get(serviceName); + } + + public String getServicePath(String ruleName, String port, String protocol, Integer icmpType, Integer icmpCode) { + com.vmware.nsx_policy.model.Service svc = getInfraService(ruleName, port, protocol, icmpType, icmpCode); + return svc.getServiceEntries().get(0)._getDataValue().getField("parent_path").toString(); + } + + public void createNsxInfraService(Services service, String serviceName, String ruleName, String port, String protocol, + Integer icmpType, Integer icmpCode) { + try { + List serviceEntries = new ArrayList<>(); + protocol = "ICMP".equalsIgnoreCase(protocol) ? "ICMPv4" : protocol; + String serviceEntryName = getServiceEntryName(ruleName, port, protocol); + if (protocol.equals("ICMPv4")) { + serviceEntries.add(new ICMPTypeServiceEntry.Builder() + .setId(serviceEntryName) + .setDisplayName(serviceEntryName) +// .setIcmpCode(Long.valueOf(icmpCode)) + .setIcmpType(Long.valueOf(icmpType)) + .setProtocol(protocol) + .build() + ); + } else { + serviceEntries.add(new L4PortSetServiceEntry.Builder() + .setId(serviceEntryName) + .setDisplayName(serviceEntryName) + .setDestinationPorts(List.of(port)) + .setL4Protocol(protocol) + .build()); + } + com.vmware.nsx_policy.model.Service infraService = new com.vmware.nsx_policy.model.Service.Builder() + .setServiceEntries(serviceEntries) + .setId(serviceName) + .setDisplayName(serviceName) + .build(); + service.patch(serviceName, infraService); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to create NSX infra service, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + private String getServiceById(String ruleName) { + try { + Services service = (Services) nsxService.apply(Services.class); + com.vmware.nsx_policy.model.Service svc1 = service.get(ruleName); + if (Objects.nonNull(svc1)) { + return ((L4PortSetServiceEntry) svc1.getServiceEntries().get(0)).getParentPath(); + } + } catch (Exception e) { + return null; + } + return null; + } + + /** + * Create a Group for the Segment on the Inventory, with the same name as the segment and being the segment the only member of the group + */ + public void createGroupForSegment(String segmentName) { + logger.info(String.format("Creating Group for Segment %s", segmentName)); + + PathExpression pathExpression = new PathExpression(); + List paths = List.of(String.format("%s/%s", SEGMENTS_PATH, segmentName)); + pathExpression.setPaths(paths); + + Groups service = (Groups) nsxService.apply(Groups.class); + Group group = new Group.Builder() + .setId(segmentName) + .setDisplayName(segmentName) + .setExpression(List.of(pathExpression)) + .build(); + service.patch(DEFAULT_DOMAIN, segmentName, group); + } + + /** + * Remove Segment Group from the Inventory + */ + private void removeGroupForSegment(String segmentName) { + logger.info(String.format("Removing Group for Segment %s", segmentName)); + Groups service = (Groups) nsxService.apply(Groups.class); + service.delete(DEFAULT_DOMAIN, segmentName, true, false); + } + + private void removeSegmentDistributedFirewallRules(String segmentName) { + try { + SecurityPolicies services = (SecurityPolicies) nsxService.apply(SecurityPolicies.class); + services.delete(DEFAULT_DOMAIN, segmentName); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to remove NSX distributed firewall policy for segment %s, due to: %s", segmentName, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public void createSegmentDistributedFirewall(String segmentName, List nsxRules) { + try { + String groupPath = getGroupPath(segmentName); + if (Objects.isNull(groupPath)) { + throw new CloudRuntimeException(String.format("Failed to find group for segment %s", segmentName)); + } + SecurityPolicies services = (SecurityPolicies) nsxService.apply(SecurityPolicies.class); + List rules = getRulesForDistributedFirewall(segmentName, nsxRules); + SecurityPolicy policy = new SecurityPolicy.Builder() + .setDisplayName(segmentName) + .setId(segmentName) + .setCategory("Application") + .setRules(rules) + .setScope(List.of(groupPath)) + .build(); + services.patch(DEFAULT_DOMAIN, segmentName, policy); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to create NSX distributed firewall policy for segment %s, due to: %s", segmentName, ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public void deleteDistributedFirewallRules(String segmentName, List nsxRules) { + for(NsxNetworkRule rule : nsxRules) { + String ruleId = NsxControllerUtils.getNsxDistributedFirewallPolicyRuleId(segmentName, rule.getRuleId()); + String svcName = getServiceName(ruleId, rule.getPrivatePort(), rule.getProtocol(), rule.getIcmpType(), rule.getIcmpCode()); + // delete rules + Rules rules = (Rules) nsxService.apply(Rules.class); + rules.delete(DEFAULT_DOMAIN, segmentName, ruleId); + // delete service - if any + Services services = (Services) nsxService.apply(Services.class); + services.delete(svcName); + } + } + + private List getRulesForDistributedFirewall(String segmentName, List nsxRules) { + List rules = new ArrayList<>(); + String groupPath = getGroupPath(segmentName); + if (Objects.isNull(groupPath)) { + throw new CloudRuntimeException(String.format("Failed to find group for segment %s", segmentName)); + } + for (NsxNetworkRule rule : nsxRules) { + String ruleId = NsxControllerUtils.getNsxDistributedFirewallPolicyRuleId(segmentName, rule.getRuleId()); + Rule ruleToAdd = new Rule.Builder() + .setAction(rule.getAclAction().toString()) + .setId(ruleId) + .setDisplayName(ruleId) + .setResourceType("SecurityPolicy") + .setSourceGroups(getGroupsForTraffic(rule, segmentName, true)) + .setDestinationGroups(getGroupsForTraffic(rule, segmentName, false)) + .setServices(getServicesListForDistributedFirewallRule(rule, segmentName)) + .setScope(List.of(groupPath)) + .build(); + rules.add(ruleToAdd); + } + return rules; + } + + private List getServicesListForDistributedFirewallRule(NsxNetworkRule rule, String segmentName) { + List services = List.of("ANY"); + if (!rule.getProtocol().equalsIgnoreCase("all")) { + String ruleName = String.format("%s-R%s", segmentName, rule.getRuleId()); + String serviceName = getNsxInfraServices(ruleName, rule.getPrivatePort(), rule.getProtocol(), + rule.getIcmpType(), rule.getIcmpCode()); + services = List.of(serviceName); + } + return services; + } + + protected List getGroupsForTraffic(NsxNetworkRule rule, + String segmentName, boolean source) { + List segmentGroup = List.of(String.format("%s/%s", GROUPS_PATH_PREFIX, segmentName)); + List sourceCidrList = rule.getSourceCidrList(); + List destCidrList = rule.getDestinationCidrList(); + List ingressSource = (rule.getService() == Network.Service.NetworkACL ? segmentGroup : destCidrList); + List egressSource = (rule.getService() == Network.Service.NetworkACL ? sourceCidrList : destCidrList); + + String trafficType = rule.getTrafficType(); + if (trafficType.equalsIgnoreCase("ingress")) { + return source ? sourceCidrList : ingressSource; + } else if (trafficType.equalsIgnoreCase("egress")) { + return source ? segmentGroup : egressSource; + } + String err = String.format("Unsupported traffic type %s", trafficType); + logger.error(err); + throw new CloudRuntimeException(err); + } + + + private List listNsxGroups() { + try { + Groups groups = (Groups) nsxService.apply(Groups.class); + GroupListResult result = groups.list(DEFAULT_DOMAIN, null, false, null, null, null, null, null); + return result.getResults(); + } catch (Error error) { + ApiError ae = error.getData()._convertTo(ApiError.class); + String msg = String.format("Failed to list NSX groups, due to: %s", ae.getErrorMessage()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + private String getGroupPath(String segmentName) { + List groups = listNsxGroups(); + Optional matchingGroup = groups.stream().filter(group -> group.getDisplayName().equals(segmentName)).findFirst(); + return matchingGroup.map(Group::getPath).orElse(null); + + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java new file mode 100644 index 000000000000..1fb546ed1224 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxElement.java @@ -0,0 +1,902 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.amazonaws.util.CollectionUtils; +import com.cloud.agent.AgentManager; +import com.cloud.agent.Listener; +import com.cloud.agent.api.AgentControlAnswer; +import com.cloud.agent.api.AgentControlCommand; +import com.cloud.agent.api.Answer; +import com.cloud.agent.api.Command; +import com.cloud.agent.api.StartupCommand; +import com.cloud.agent.api.to.LoadBalancerTO; +import com.cloud.api.ApiDBUtils; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.ConnectionException; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.host.Host; +import com.cloud.host.HostVO; +import com.cloud.host.Status; +import com.cloud.network.Network; +import com.cloud.network.NetworkModel; +import com.cloud.network.Networks; +import com.cloud.network.PhysicalNetworkServiceProvider; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.VirtualRouterProvider; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.LoadBalancerVMMapDao; +import com.cloud.network.dao.LoadBalancerVMMapVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.dao.VirtualRouterProviderDao; +import com.cloud.network.element.DhcpServiceProvider; +import com.cloud.network.element.DnsServiceProvider; +import com.cloud.network.element.FirewallServiceProvider; +import com.cloud.network.element.IpDeployer; +import com.cloud.network.element.LoadBalancingServiceProvider; +import com.cloud.network.element.NetworkACLServiceProvider; +import com.cloud.network.element.PortForwardingServiceProvider; +import com.cloud.network.element.StaticNatServiceProvider; +import com.cloud.network.element.VirtualRouterElement; +import com.cloud.network.element.VirtualRouterProviderVO; +import com.cloud.network.element.VpcProvider; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.LoadBalancerContainer; +import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.rules.StaticNat; +import com.cloud.network.vpc.NetworkACLItem; +import com.cloud.network.vpc.PrivateGateway; +import com.cloud.network.vpc.StaticRouteProfile; +import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.offering.NetworkOffering; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ResourceStateAdapter; +import com.cloud.resource.ServerResource; +import com.cloud.resource.UnableDeleteHostException; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.uservm.UserVm; +import com.cloud.utils.Pair; +import com.cloud.utils.component.AdapterBase; +import com.cloud.utils.db.QueryBuilder; +import com.cloud.utils.db.SearchCriteria; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.VMInstanceDao; +import net.sf.ehcache.config.InvalidConfigurationException; +import org.apache.cloudstack.StartupNsxCommand; +import org.apache.cloudstack.api.command.admin.internallb.ConfigureInternalLoadBalancerElementCmd; +import org.apache.cloudstack.api.command.admin.internallb.CreateInternalLoadBalancerElementCmd; +import org.apache.cloudstack.api.command.admin.internallb.ListInternalLoadBalancerElementsCmd; +import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; +import org.apache.cloudstack.resource.NsxLoadBalancerMember; +import org.apache.cloudstack.resource.NsxNetworkRule; +import org.apache.cloudstack.resource.NsxOpObject; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.LongFunction; + +@Component +public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, VpcProvider, + StaticNatServiceProvider, IpDeployer, PortForwardingServiceProvider, NetworkACLServiceProvider, + LoadBalancingServiceProvider, FirewallServiceProvider, InternalLoadBalancerElementService, ResourceStateAdapter, Listener { + + + @Inject + AccountManager accountMgr; + @Inject + NsxServiceImpl nsxService; + @Inject + DataCenterDao dataCenterDao; + @Inject + NetworkDao networkDao; + @Inject + AgentManager agentManager; + @Inject + ResourceManager resourceManager; + @Inject + PhysicalNetworkDao physicalNetworkDao; + @Inject + NetworkModel networkModel; + @Inject + DomainDao domainDao; + @Inject + protected VpcOfferingServiceMapDao vpcOfferingServiceMapDao; + @Inject + IPAddressDao ipAddressDao; + @Inject + VMInstanceDao vmInstanceDao; + @Inject + VpcDao vpcDao; + @Inject + LoadBalancerVMMapDao lbVmMapDao; + @Inject + VirtualRouterProviderDao vrProviderDao; + @Inject + PhysicalNetworkServiceProviderDao pNtwkSvcProviderDao; + + protected Logger logger = LogManager.getLogger(getClass()); + + private final Map> capabilities = initCapabilities(); + + + private static Map> initCapabilities() { + Map> capabilities = new HashMap<>(); + + Map dhcpCapabilities = Map.of(Network.Capability.DhcpAccrossMultipleSubnets, "true"); + capabilities.put(Network.Service.Dhcp, dhcpCapabilities); + + Map dnsCapabilities = new HashMap<>(); + dnsCapabilities.put(Network.Capability.AllowDnsSuffixModification, "true"); + capabilities.put(Network.Service.Dns, dnsCapabilities); + + capabilities.put(Network.Service.StaticNat, null); + + // Set capabilities for LB service + Map lbCapabilities = new HashMap(); + lbCapabilities.put(Network.Capability.SupportedLBAlgorithms, "roundrobin,leastconn"); + lbCapabilities.put(Network.Capability.SupportedLBIsolation, "dedicated"); + lbCapabilities.put(Network.Capability.SupportedProtocols, "tcp, udp"); + lbCapabilities.put(Network.Capability.SupportedStickinessMethods, VirtualRouterElement.getHAProxyStickinessCapability()); + lbCapabilities.put(Network.Capability.LbSchemes, String.join(",", LoadBalancerContainer.Scheme.Internal.name(), LoadBalancerContainer.Scheme.Public.name())); + + capabilities.put(Network.Service.Lb, lbCapabilities); + capabilities.put(Network.Service.PortForwarding, null); + capabilities.put(Network.Service.NetworkACL, null); + + Map firewallCapabilities = new HashMap<>(); + firewallCapabilities.put(Network.Capability.SupportedProtocols, "tcp,udp,icmp"); + firewallCapabilities.put(Network.Capability.SupportedEgressProtocols, "tcp,udp,icmp,all"); + firewallCapabilities.put(Network.Capability.MultipleIps, "true"); + firewallCapabilities.put(Network.Capability.TrafficStatistics, "per public ip"); + firewallCapabilities.put(Network.Capability.SupportedTrafficDirection, "ingress, egress"); + capabilities.put(Network.Service.Firewall, firewallCapabilities); + + Map sourceNatCapabilities = new HashMap<>(); + sourceNatCapabilities.put(Network.Capability.RedundantRouter, "true"); + sourceNatCapabilities.put(Network.Capability.SupportedSourceNatTypes, "peraccount"); + capabilities.put(Network.Service.SourceNat, sourceNatCapabilities); + return capabilities; + } + @Override + public boolean addDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + return true; + } + + @Override + public boolean configDhcpSupportForSubnet(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + return true; + } + + @Override + public boolean removeDhcpSupportForSubnet(Network network) throws ResourceUnavailableException { + return true; + } + + @Override + public boolean setExtraDhcpOptions(Network network, long nicId, Map dhcpOptions) { + return true; + } + + @Override + public boolean removeDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vmProfile) throws ResourceUnavailableException { + return true; + } + + @Override + public boolean addDnsEntry(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + return true; + } + + @Override + public boolean configDnsSupportForSubnet(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, InsufficientCapacityException, ResourceUnavailableException { + return true; + } + + @Override + public boolean removeDnsSupportForSubnet(Network network) throws ResourceUnavailableException { + return true; + } + + @Override + public Map> getCapabilities() { + return capabilities; + } + + @Override + public boolean applyIps(Network network, List ipAddress, Set services) throws ResourceUnavailableException { + return true; + } + + @Override + public Network.Provider getProvider() { + return Network.Provider.Nsx; + } + + @Override + public boolean implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + // TODO: Check if the network is NSX based (was already implemented as part of the guru.setup() + return true; + } + + @Override + public boolean prepare(Network network, NicProfile nic, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + return false; + } + + @Override + public boolean release(Network network, NicProfile nic, VirtualMachineProfile vm, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return false; + } + + @Override + public boolean shutdown(Network network, ReservationContext context, boolean cleanup) throws ConcurrentOperationException, ResourceUnavailableException { + return canHandle(network, Network.Service.Connectivity); + } + + @Override + public boolean destroy(Network network, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + Account account = accountMgr.getAccount(network.getAccountId()); + NetworkVO networkVO = networkDao.findById(network.getId()); + DataCenterVO zone = dataCenterDao.findById(network.getDataCenterId()); + DomainVO domain = domainDao.findById(account.getDomainId()); + if (Objects.isNull(zone)) { + String msg = String.format("Cannot find zone with ID %s", network.getDataCenterId()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + return nsxService.deleteNetwork(zone.getId(), account.getId(), domain.getId(), networkVO); + } + + @Override + public boolean isReady(PhysicalNetworkServiceProvider provider) { + return true; + } + + @Override + public boolean shutdownProviderInstances(PhysicalNetworkServiceProvider provider, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException { + return false; + } + + @Override + public boolean canEnableIndividualServices() { + return true; + } + + @Override + public boolean verifyServicesCombination(Set services) { + return true; + } + + @Override + public boolean configure(String name, Map params) throws ConfigurationException { + agentManager.registerForHostEvents(this, true, true, true); + resourceManager.registerResourceStateAdapter(this.getClass().getSimpleName(), this); + return true; + } + + @Override + public boolean start() { + return false; + } + + @Override + public boolean stop() { + return false; + } + + @Override + public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) { + return null; + } + + @Override + public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map details, List hostTags) { + if (!(startup[0] instanceof StartupNsxCommand)) { + return null; + } + host.setType(Host.Type.L2Networking); + return host; + } + + @Override + public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException { + return null; + } + + private DomainVO getDomainFromAccount(Account account) { + DomainVO domain = domainDao.findById(account.getDomainId()); + if (Objects.isNull(domain)) { + String msg = String.format("Unable to find domain with id: %s", account.getDomainId()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + return domain; + } + + @Override + public boolean implementVpc(Vpc vpc, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { + DataCenterVO zone = zoneFunction.apply(vpc.getZoneId()); + Pair isNsxAndAccount = validateVpcConfigurationAndGetAccount(zone, vpc); + if (Boolean.FALSE.equals(isNsxAndAccount.first())) { + return true; + } + if (Boolean.TRUE.equals(isNsxAndAccount.first()) && Objects.isNull(isNsxAndAccount.second())) { + throw new InvalidParameterValueException(String.format("Failed to find account with id %s", vpc.getAccountId())); + } + return true; + } + + @Override + public boolean shutdownVpc(Vpc vpc, ReservationContext context) throws ConcurrentOperationException { + DataCenterVO zone = zoneFunction.apply(vpc.getZoneId()); + Pair isNsxAndAccount = validateVpcConfigurationAndGetAccount(zone, vpc); + if (Boolean.FALSE.equals(isNsxAndAccount.first())) { + return true; + } + if (Boolean.TRUE.equals(isNsxAndAccount.first()) && Objects.isNull(isNsxAndAccount.second())) { + throw new InvalidParameterValueException(String.format("Failed to find account with id %s", vpc.getAccountId())); + } + Account account = isNsxAndAccount.second(); + DomainVO domain = getDomainFromAccount(account); + return nsxService.deleteVpcNetwork(vpc.getZoneId(), account.getId(), domain.getId(), vpc.getId(), vpc.getName()); + } + + private Pair validateVpcConfigurationAndGetAccount(DataCenterVO zone, Vpc vpc) { + if (Objects.isNull(zone)) { + throw new InvalidParameterValueException(String.format("Failed to find zone with id %s", vpc.getZoneId())); + } + Account account = null; + boolean forNsx = false; + List physicalNetworks = physicalNetworkDao.listByZoneAndTrafficType(zone.getId(), Networks.TrafficType.Guest); + if (CollectionUtils.isNullOrEmpty(physicalNetworks) || physicalNetworks.size() > 1 ) { + throw new InvalidConfigurationException(String.format("Desired number of physical networks is not present in the zone %s for traffic type %s. ", zone.getName(), Networks.TrafficType.Guest.name())); + } + if (physicalNetworks.get(0).getIsolationMethods().contains("NSX")) { + account = accountMgr.getAccount(vpc.getAccountId()); + forNsx = true; + } + return new Pair<>(forNsx, account); + } + + @Override + public boolean createPrivateGateway(PrivateGateway gateway) throws ConcurrentOperationException, ResourceUnavailableException { + return false; + } + + @Override + public boolean deletePrivateGateway(PrivateGateway privateGateway) throws ConcurrentOperationException, ResourceUnavailableException { + return false; + } + + @Override + public boolean applyStaticRoutes(Vpc vpc, List routes) throws ResourceUnavailableException { + return false; + } + + @Override + public boolean applyACLItemsToPrivateGw(PrivateGateway gateway, List rules) throws ResourceUnavailableException { + return false; + } + + @Override + public boolean processAnswers(long agentId, long seq, Answer[] answers) { + return false; + } + + @Override + public boolean processCommands(long agentId, long seq, Command[] commands) { + return false; + } + + @Override + public AgentControlAnswer processControlCommand(long agentId, AgentControlCommand cmd) { + return null; + } + + @Override + public void processHostAdded(long hostId) { + // Do nothing + } + + @Override + public void processConnect(Host host, StartupCommand cmd, boolean forRebalance) throws ConnectionException { + // Do nothing + } + + @Override + public boolean processDisconnect(long agentId, Status state) { + return false; + } + + @Override + public void processHostAboutToBeRemoved(long hostId) { + // Do nothing + } + + @Override + public void processHostRemoved(long hostId, long clusterId) { + // Do nothing + } + + @Override + public boolean isRecurring() { + return false; + } + + @Override + public int getTimeout() { + return 0; + } + + @Override + public boolean processTimeout(long agentId, long seq) { + return false; + } + + protected boolean canHandle(Network network, Network.Service service) { + logger.debug("Checking if Nsx Element can handle service " + service.getName() + " on network " + + network.getDisplayText()); + + if (!networkModel.isProviderForNetwork(getProvider(), network.getId())) { + logger.debug("Nsx Element is not a provider for network " + network.getDisplayText()); + return false; + } + + return true; + } + + private final LongFunction zoneFunction = zoneId -> dataCenterDao.findById(zoneId); + + @Override + public IpDeployer getIpDeployer(Network network) { + return this; + } + + @Override + public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { + for(StaticNat staticNat : rules) { + long sourceIpAddressId = staticNat.getSourceIpAddressId(); + IPAddressVO ipAddressVO = ipAddressDao.findByIdIncludingRemoved(sourceIpAddressId); + VMInstanceVO vm = vmInstanceDao.findByIdIncludingRemoved(ipAddressVO.getAssociatedWithVmId()); + // floating ip is released when nic was deleted + if (vm == null || networkModel.getNicInNetworkIncludingRemoved(vm.getId(), config.getId()) == null) { + continue; + } + Pair vpcOrNetwork = getVpcOrNetwork(config.getVpcId(), config.getId()); + VpcVO vpc = vpcOrNetwork.first(); + NetworkVO network = vpcOrNetwork.second(); + Long networkResourceId = Objects.nonNull(vpc) ? vpc.getId() : network.getId(); + String networkResourceName = Objects.nonNull(vpc) ? vpc.getName() : network.getName(); + boolean isVpcResource = Objects.nonNull(vpc); + if (!staticNat.isForRevoke()) { + return nsxService.createStaticNatRule(config.getDataCenterId(), config.getDomainId(), config.getAccountId(), + networkResourceId, networkResourceName, isVpcResource, vm.getId(), + ipAddressVO.getAddress().addr(), staticNat.getDestIpAddress()); + } else { + return nsxService.deleteStaticNatRule(config.getDataCenterId(), config.getDomainId(), config.getAccountId(), + networkResourceId, networkResourceName, isVpcResource); + } + } + return false; + } + + @Override + public boolean applyPFRules(Network network, List rules) throws ResourceUnavailableException { + if (!canHandle(network, Network.Service.PortForwarding)) { + return false; + } + boolean result = true; + for (PortForwardingRule rule : rules) { + IPAddressVO publicIp = ApiDBUtils.findIpAddressById(rule.getSourceIpAddressId()); + UserVm vm = ApiDBUtils.findUserVmById(rule.getVirtualMachineId()); + if (vm == null && rule.getState() != FirewallRule.State.Revoke) { + continue; + } + NsxOpObject nsxObject = getNsxOpObject(network); + String publicPort = getPublicPortRange(rule); + + String privatePort = getPrivatePFPortRange(rule); + + NsxNetworkRule networkRule = new NsxNetworkRule.Builder() + .setDomainId(nsxObject.getDomainId()) + .setAccountId(nsxObject.getAccountId()) + .setZoneId(nsxObject.getZoneId()) + .setNetworkResourceId(nsxObject.getNetworkResourceId()) + .setNetworkResourceName(nsxObject.getNetworkResourceName()) + .setVpcResource(nsxObject.isVpcResource()) + .setVmId(Objects.nonNull(vm) ? vm.getId() : 0) + .setVmIp(Objects.nonNull(vm) ? vm.getPrivateIpAddress() : null) + .setPublicIp(publicIp.getAddress().addr()) + .setPrivatePort(privatePort) + .setPublicPort(publicPort) + .setRuleId(rule.getId()) + .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT)) + .build(); + if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(rule.getState())) { + result &= nsxService.createPortForwardRule(networkRule); + } else if (rule.getState() == FirewallRule.State.Revoke) { + result &= nsxService.deletePortForwardRule(networkRule); + } + } + return result; + } + + public Pair getVpcOrNetwork(Long vpcId, long networkId) { + VpcVO vpc = null; + NetworkVO network = null; + if (Objects.nonNull(vpcId)) { + vpc = vpcDao.findById(vpcId); + if (Objects.isNull(vpc)) { + throw new CloudRuntimeException(String.format("Failed to find VPC with id: %s", vpcId)); + } + } else { + network = networkDao.findById(networkId); + if (Objects.isNull(network)) { + throw new CloudRuntimeException(String.format("Failed to find network with id: %s", networkId)); + } + } + return new Pair<>(vpc, network); + } + + private static String getPublicPortRange(PortForwardingRule rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + + private static String getPrivatePFPortRange(PortForwardingRule rule) { + return rule.getDestinationPortStart() == rule.getDestinationPortEnd() ? + String.valueOf(rule.getDestinationPortStart()) : + String.valueOf(rule.getDestinationPortStart()).concat("-").concat(String.valueOf(rule.getDestinationPortEnd())); + } + + private static String getPrivatePortRange(FirewallRule rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + + private static String getPrivatePortRangeForACLRule(NetworkACLItem rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } + + private long getResourceId(String resource, VpcVO vpc, NetworkVO network) { + switch (resource) { + case "domain": + return Objects.nonNull(vpc) ? vpc.getDomainId() : network.getDomainId(); + case "account": + return Objects.nonNull(vpc) ? vpc.getAccountId() : network.getAccountId(); + case "zone": + return Objects.nonNull(vpc) ? vpc.getZoneId() : network.getDataCenterId(); + default: + return 0; + } + } + + private NsxOpObject getNsxOpObject(Network network) { + Pair vpcOrNetwork = getVpcOrNetwork(network.getVpcId(), network.getId()); + VpcVO vpc = vpcOrNetwork.first(); + NetworkVO networkVO = vpcOrNetwork.second(); + long domainId = getResourceId("domain", vpc, networkVO); + long accountId = getResourceId("account", vpc, networkVO); + long zoneId = getResourceId("zone", vpc, networkVO); + + return new NsxOpObject.Builder() + .vpcVO(vpc) + .networkVO(networkVO) + .domainId(domainId) + .accountId(accountId) + .zoneId(zoneId) + .build(); + } + + @Override + public boolean applyLBRules(Network network, List rules) throws ResourceUnavailableException { + boolean result = true; + for (LoadBalancingRule loadBalancingRule : rules) { + IPAddressVO publicIp = ipAddressDao.findByIpAndDcId(network.getDataCenterId(), + loadBalancingRule.getSourceIp().addr()); + NsxOpObject nsxObject = getNsxOpObject(network); + + List lbMembers = getLoadBalancerMembers(loadBalancingRule); + NsxNetworkRule networkRule = new NsxNetworkRule.Builder() + .setDomainId(nsxObject.getDomainId()) + .setAccountId(nsxObject.getAccountId()) + .setZoneId(nsxObject.getZoneId()) + .setNetworkResourceId(nsxObject.getNetworkResourceId()) + .setNetworkResourceName(nsxObject.getNetworkResourceName()) + .setVpcResource(nsxObject.isVpcResource()) + .setMemberList(lbMembers) + .setPublicIp(LoadBalancerContainer.Scheme.Public == loadBalancingRule.getScheme() ? + publicIp.getAddress().addr() : loadBalancingRule.getSourceIp().addr()) + .setPublicPort(String.valueOf(loadBalancingRule.getSourcePortStart())) + .setPrivatePort(String.valueOf(loadBalancingRule.getDefaultPortStart())) + .setRuleId(loadBalancingRule.getId()) + .setProtocol(loadBalancingRule.getLbProtocol().toUpperCase(Locale.ROOT)) + .setAlgorithm(loadBalancingRule.getAlgorithm()) + .build(); + if (Arrays.asList(FirewallRule.State.Add, FirewallRule.State.Active).contains(loadBalancingRule.getState())) { + result &= nsxService.createLbRule(networkRule); + } else if (loadBalancingRule.getState() == FirewallRule.State.Revoke) { + result &= nsxService.deleteLbRule(networkRule); + } + } + return result; + } + + @Override + public boolean validateLBRule(Network network, LoadBalancingRule rule) { + return true; + } + + @Override + public List updateHealthChecks(Network network, List lbrules) { + return new ArrayList<>(); + } + + @Override + public boolean handlesOnlyRulesInTransitionState() { + return false; + } + + private List getLoadBalancerMembers(LoadBalancingRule lbRule) { + List lbVms = lbVmMapDao.listByLoadBalancerId(lbRule.getId(), false); + List lbMembers = new ArrayList<>(); + + for (LoadBalancerVMMapVO lbVm : lbVms) { + NsxLoadBalancerMember member = new NsxLoadBalancerMember(lbVm.getInstanceId(), lbVm.getInstanceIp(), lbRule.getDefaultPortStart()); + lbMembers.add(member); + } + return lbMembers; + } + + @Override + public boolean applyNetworkACLs(Network network, List rules) throws ResourceUnavailableException { + if (!canHandle(network, Network.Service.NetworkACL)) { + return false; + } + + List nsxDelNetworkRules = new ArrayList<>(); + boolean success = true; + for (NetworkACLItem rule : rules) { + String privatePort = getPrivatePortRangeForACLRule(rule); + NsxNetworkRule networkRule = getNsxNetworkRuleForAcl(rule, privatePort); + if (Arrays.asList(NetworkACLItem.State.Active, NetworkACLItem.State.Add).contains(rule.getState())) { + success = success && nsxService.addFirewallRules(network, List.of(networkRule)); + } else if (NetworkACLItem.State.Revoke == rule.getState()) { + nsxDelNetworkRules.add(networkRule); + } + } + + if (!nsxDelNetworkRules.isEmpty()) { + success = nsxService.deleteFirewallRules(network, nsxDelNetworkRules); + if (!success) { + logger.warn("Not all firewall rules were successfully deleted"); + } + } + return success; + } + + @Override + public boolean reorderAclRules(Vpc vpc, List networks, List networkACLItems) { + List aclRulesList = new ArrayList<>(); + for (NetworkACLItem rule : networkACLItems) { + String privatePort = getPrivatePortRangeForACLRule(rule); + aclRulesList.add(getNsxNetworkRuleForAcl(rule, privatePort)); + } + for (Network network: networks) { + nsxService.deleteFirewallRules(network, aclRulesList); + } + boolean success = true; + for (Network network : networks) { + for (NsxNetworkRule aclRule : aclRulesList) { + success = success && nsxService.addFirewallRules(network, List.of(aclRule)); + } + } + return success; + } + + private NsxNetworkRule getNsxNetworkRuleForAcl(NetworkACLItem rule, String privatePort) { + return new NsxNetworkRule.Builder() + .setRuleId(rule.getId()) + .setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ? transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY")) + .setAclAction(transformActionValue(rule.getAction())) + .setTrafficType(rule.getTrafficType().toString()) + .setProtocol(rule.getProtocol().toUpperCase()) + .setPublicPort(String.valueOf(rule.getSourcePortStart())) + .setPrivatePort(privatePort) + .setIcmpCode(rule.getIcmpCode()) + .setIcmpType(rule.getIcmpType()) + .setService(Network.Service.NetworkACL) + .build(); + } + @Override + public boolean applyFWRules(Network network, List rules) throws ResourceUnavailableException { + + if (!canHandle(network, Network.Service.Firewall)) { + return false; + } + List nsxAddNetworkRules = new ArrayList<>(); + List nsxDelNetworkRules = new ArrayList<>(); + for (FirewallRule rule : rules) { + NsxNetworkRule networkRule = new NsxNetworkRule.Builder() + .setRuleId(rule.getId()) + .setAclAction(NsxNetworkRule.NsxRuleAction.ALLOW) + .setSourceCidrList(Objects.nonNull(rule.getSourceCidrList()) ? + transformCidrListValues(rule.getSourceCidrList()) : List.of("ANY")) + .setDestinationCidrList(Objects.nonNull(rule.getDestinationCidrList()) ? + transformCidrListValues(rule.getDestinationCidrList()) : List.of("ANY")) + .setIcmpCode(rule.getIcmpCode()) + .setIcmpType(rule.getIcmpType()) + .setPrivatePort(getPrivatePortRange(rule)) + .setTrafficType(rule.getTrafficType().toString()) + .setService(Network.Service.Firewall) + .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT)) + .build(); + if (rule.getState() == FirewallRule.State.Add) { + nsxAddNetworkRules.add(networkRule); + } else if (rule.getState() == FirewallRule.State.Revoke) { + nsxDelNetworkRules.add(networkRule); + } + } + boolean success = true; + if (!nsxDelNetworkRules.isEmpty()) { + success = nsxService.deleteFirewallRules(network, nsxDelNetworkRules); + if (!success) { + logger.warn("Not all firewall rules were successfully deleted"); + } + } + return success && nsxService.addFirewallRules(network, nsxAddNetworkRules); + } + + protected NsxNetworkRule.NsxRuleAction transformActionValue(NetworkACLItem.Action action) { + if (action == NetworkACLItem.Action.Allow) { + return NsxNetworkRule.NsxRuleAction.ALLOW; + } else if (action == NetworkACLItem.Action.Deny) { + return NsxNetworkRule.NsxRuleAction.DROP; + } + String err = String.format("Unsupported action %s", action.toString()); + logger.error(err); + throw new CloudRuntimeException(err); + } + + /** + * Replace 0.0.0.0/0 to ANY on each occurrence + */ + protected List transformCidrListValues(List sourceCidrList) { + List list = new ArrayList<>(); + if (org.apache.commons.collections.CollectionUtils.isNotEmpty(sourceCidrList)) { + for (String cidr : sourceCidrList) { + if (cidr.equals("0.0.0.0/0")) { + list.add("ANY"); + } else { + list.add(cidr); + } + } + } + return list; + } + + @Override + public VirtualRouterProvider configureInternalLoadBalancerElement(long id, boolean enable) { + VirtualRouterProviderVO element = vrProviderDao.findById(id); + if (element == null || element.getType() != VirtualRouterProvider.Type.Nsx) { + throw new InvalidParameterValueException("Can't find " + getName() + " " + + "element with network service provider id " + id + " to be used as a provider for " + + getName()); + } + + element.setEnabled(enable); + element = vrProviderDao.persist(element); + + return element; + } + + @Override + public VirtualRouterProvider addInternalLoadBalancerElement(long ntwkSvcProviderId) { + VirtualRouterProviderVO element = vrProviderDao.findByNspIdAndType(ntwkSvcProviderId, VirtualRouterProvider.Type.Nsx); + if (element != null) { + logger.debug("There is already an " + getName() + " with service provider id " + ntwkSvcProviderId); + return null; + } + + PhysicalNetworkServiceProvider provider = pNtwkSvcProviderDao.findById(ntwkSvcProviderId); + if (provider == null || !provider.getProviderName().equalsIgnoreCase(getName())) { + throw new InvalidParameterValueException("Invalid network service provider is specified"); + } + + element = new VirtualRouterProviderVO(ntwkSvcProviderId, VirtualRouterProvider.Type.Nsx); + element = vrProviderDao.persist(element); + return element; + } + + @Override + public VirtualRouterProvider getInternalLoadBalancerElement(long id) { + VirtualRouterProvider provider = vrProviderDao.findById(id); + if (provider == null || provider.getType() != VirtualRouterProvider.Type.Nsx) { + throw new InvalidParameterValueException("Unable to find " + getName() + " by id"); + } + return provider; + } + + @Override + public List searchForInternalLoadBalancerElements(Long id, Long ntwkSvsProviderId, Boolean enabled) { + QueryBuilder sc = QueryBuilder.create(VirtualRouterProviderVO.class); + if (id != null) { + sc.and(sc.entity().getId(), SearchCriteria.Op.EQ, id); + } + if (ntwkSvsProviderId != null) { + sc.and(sc.entity().getNspId(), SearchCriteria.Op.EQ, ntwkSvsProviderId); + } + if (enabled != null) { + sc.and(sc.entity().isEnabled(), SearchCriteria.Op.EQ, enabled); + } + + //return only Internal LB elements + sc.and(sc.entity().getType(), SearchCriteria.Op.EQ, VirtualRouterProvider.Type.Nsx); + + return sc.list(); + } + + @Override + public VirtualRouterProvider.Type getProviderType() { + return VirtualRouterProvider.Type.Nsx; + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList>(); + cmdList.add(CreateInternalLoadBalancerElementCmd.class); + cmdList.add(ConfigureInternalLoadBalancerElementCmd.class); + cmdList.add(ListInternalLoadBalancerElementsCmd.class); + return cmdList; + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java new file mode 100644 index 000000000000..0d556da9f2c0 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxGuestNetworkGuru.java @@ -0,0 +1,342 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import static java.util.Objects.isNull; +import static java.util.Objects.nonNull; + +import com.cloud.dc.DataCenter; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.NetworkMigrationResponder; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkProfile; +import com.cloud.network.Network; +import com.cloud.network.Networks; +import com.cloud.network.PhysicalNetwork; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.guru.GuestNetworkGuru; +import com.cloud.network.vpc.VpcVO; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingServiceMapDao; +import com.cloud.user.Account; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.db.DB; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.CreateNsxDhcpRelayConfigCommand; +import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand; +import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand; +import org.apache.cloudstack.utils.NsxControllerUtils; + +import org.apache.cloudstack.utils.NsxHelper; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.inject.Inject; +import java.util.List; +import java.util.Objects; + +public class NsxGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigrationResponder { + protected Logger logger = LogManager.getLogger(getClass()); + + @Inject + NetworkOfferingServiceMapDao networkOfferingServiceMapDao; + @Inject + NsxControllerUtils nsxControllerUtils; + @Inject + AccountDao accountDao; + @Inject + DomainDao domainDao; + @Inject + NetworkModel networkModel; + + public NsxGuestNetworkGuru() { + super(); + _isolationMethods = new PhysicalNetwork.IsolationMethod[] {new PhysicalNetwork.IsolationMethod("NSX")}; + } + + @Override + public boolean canHandle(NetworkOffering offering, DataCenter.NetworkType networkType, + PhysicalNetwork physicalNetwork) { + return networkType == DataCenter.NetworkType.Advanced && isMyTrafficType(offering.getTrafficType()) + && isMyIsolationMethod(physicalNetwork) && (NetworkOffering.NsxMode.ROUTED.name().equals(offering.getNsxMode()) + || (networkOfferingServiceMapDao.isProviderForNetworkOffering( + offering.getId(), Network.Provider.Nsx) && NetworkOffering.NsxMode.NATTED.name().equals(offering.getNsxMode()))); + } + + @Override + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { + PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); + DataCenter dc = _dcDao.findById(plan.getDataCenterId()); + + if (!canHandle(offering, dc.getNetworkType(), physnet)) { + logger.debug("Refusing to design this network"); + return null; + } + + NetworkVO network = (NetworkVO) super.design(offering, plan, userSpecified, name, vpcId, owner); + if (network == null) { + return null; + } + network.setBroadcastDomainType(Networks.BroadcastDomainType.NSX); + + if (userSpecified != null) { + if ((userSpecified.getIp6Cidr() == null && userSpecified.getIp6Gateway() != null) || ( + userSpecified.getIp6Cidr() != null && userSpecified.getIp6Gateway() == null)) { + throw new InvalidParameterValueException("cidrv6 and gatewayv6 must be specified together."); + } + + if (userSpecified.getIp6Cidr() != null) { + network.setIp6Cidr(userSpecified.getIp6Cidr()); + network.setIp6Gateway(userSpecified.getIp6Gateway()); + } + } + + network.setBroadcastDomainType(Networks.BroadcastDomainType.NSX); + network.setState(Network.State.Allocated); + + NetworkVO implemented = new NetworkVO(network.getTrafficType(), network.getMode(), + network.getBroadcastDomainType(), network.getNetworkOfferingId(), Network.State.Implemented, + network.getDataCenterId(), network.getPhysicalNetworkId(), offering.isRedundantRouter()); + implemented.setAccountId(owner.getAccountId()); + + if (network.getGateway() != null) { + implemented.setGateway(network.getGateway()); + } + + if (network.getCidr() != null) { + implemented.setCidr(network.getCidr()); + } + + if (vpcId != null) { + implemented.setVpcId(vpcId); + } + + if (name != null) { + implemented.setName(name); + } + implemented.setBroadcastUri(Networks.BroadcastDomainType.NSX.toUri("nsx")); + + return network; + } + + @Override + public void setup(Network network, long networkId) { + try { + NetworkVO designedNetwork = _networkDao.findById(networkId); + long zoneId = network.getDataCenterId(); + DataCenter zone = _dcDao.findById(zoneId); + if (isNull(zone)) { + throw new CloudRuntimeException(String.format("Failed to find zone with id: %s", zoneId)); + } + createNsxSegment(designedNetwork, zone); + } catch (Exception ex) { + throw new CloudRuntimeException("unable to create NSX network " + network.getUuid() + "due to: " + ex.getMessage()); + } + } + + @Override + @DB + public void deallocate(Network config, NicProfile nic, VirtualMachineProfile vm) { + // Do nothing + } + + @Override + public Network implement(Network network, NetworkOffering offering, DeployDestination dest, + ReservationContext context) { + NetworkVO implemented = new NetworkVO(network.getTrafficType(), network.getMode(), + network.getBroadcastDomainType(), network.getNetworkOfferingId(), Network.State.Implemented, + network.getDataCenterId(), network.getPhysicalNetworkId(), offering.isRedundantRouter()); + implemented.setAccountId(network.getAccountId()); + + if (network.getGateway() != null) { + implemented.setGateway(network.getGateway()); + } + + if (network.getCidr() != null) { + implemented.setCidr(network.getCidr()); + } + + if (network.getVpcId() != null) { + implemented.setVpcId(network.getVpcId()); + } + + if (network.getName() != null) { + implemented.setName(network.getName()); + } + implemented.setBroadcastUri(Networks.BroadcastDomainType.NSX.toUri("nsx")); + return implemented; + } + + @Override + public NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { + NicProfile nicProfile = super.allocate(network, nic, vm); + if (vm.getType() != VirtualMachine.Type.DomainRouter) { + return nicProfile; + } + + final DataCenter zone = _dcDao.findById(network.getDataCenterId()); + long zoneId = network.getDataCenterId(); + if (Objects.isNull(zone)) { + String msg = String.format("Unable to find zone with id: %s", zoneId); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + Account account = accountDao.findById(network.getAccountId()); + if (Objects.isNull(account)) { + String msg = String.format("Unable to find account with id: %s", network.getAccountId()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + VpcVO vpc = _vpcDao.findById(network.getVpcId()); + if (Objects.isNull(vpc)) { + String msg = String.format("Unable to find VPC with id: %s, allocating for network %s", network.getVpcId(), network.getName()); + logger.debug(msg); + } + + DomainVO domain = domainDao.findById(account.getDomainId()); + if (Objects.isNull(domain)) { + String msg = String.format("Unable to find domain with id: %s", account.getDomainId()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + + NetworkOfferingVO networkOfferingVO = networkOfferingDao.findById(network.getNetworkOfferingId()); + + if (isNull(network.getVpcId()) && networkOfferingVO.getNsxMode().equals(NetworkOffering.NsxMode.NATTED.name())) { + long domainId = domain.getId(); + long accountId = account.getId(); + long dataCenterId = zone.getId(); + long resourceId = network.getId(); + PublicIpAddress ipAddress = networkModel.getSourceNatIpAddressForGuestNetwork(account, network); + String translatedIp = ipAddress.getAddress().addr(); + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(domainId, accountId, dataCenterId, resourceId, false); + logger.debug(String.format("Creating NSX NAT Rule for Tier1 GW %s for translated IP %s for Isolated network %s", tier1GatewayName, translatedIp, network.getName())); + String natRuleId = NsxControllerUtils.getNsxNatRuleId(domainId, accountId, dataCenterId, resourceId, false); + CreateOrUpdateNsxTier1NatRuleCommand cmd = NsxHelper.createOrUpdateNsxNatRuleCommand(domainId, accountId, dataCenterId, tier1GatewayName, "SNAT", translatedIp, natRuleId); + NsxAnswer nsxAnswer = nsxControllerUtils.sendNsxCommand(cmd, dataCenterId); + if (!nsxAnswer.getResult()) { + String msg = String.format("Could not create NSX NAT Rule on Tier1 Gateway %s for IP %s for Isolated network %s", tier1GatewayName, translatedIp, network.getName()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + + // Create the DHCP relay config for the segment + String iPv4Address = nicProfile.getIPv4Address(); + List addresses = List.of(iPv4Address); + CreateNsxDhcpRelayConfigCommand command = NsxHelper.createNsxDhcpRelayConfigCommand(domain, account, zone, vpc, network, addresses); + NsxAnswer answer = nsxControllerUtils.sendNsxCommand(command, zone.getId()); + if (!answer.getResult()) { + String msg = String.format("Error creating DHCP relay config for network %s and nic %s: %s", network.getName(), nic.getName(), answer.getDetails()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + return nicProfile; + } + + @Override + public void reserve(final NicProfile nic, final Network network, final VirtualMachineProfile vm, + final DeployDestination dest, final ReservationContext context) + throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { + // Do nothing + } + + @Override + public boolean release(final NicProfile nic, final VirtualMachineProfile vm, final String reservationId) { + return true; + } + + @Override + public void shutdown(final NetworkProfile profile, final NetworkOffering offering) { + // Do nothing + } + + @Override + public boolean trash(Network network, NetworkOffering offering) { + return true; + } + + @Override + public boolean prepareMigration(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) { + return false; + } + + @Override + public void rollbackMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) { + // Do nothing + } + + @Override + public void commitMigration(NicProfile nic, Network network, VirtualMachineProfile vm, ReservationContext src, ReservationContext dst) { + // Do nothing + } + + public void createNsxSegment(NetworkVO networkVO, DataCenter zone) { + Account account = accountDao.findById(networkVO.getAccountId()); + if (isNull(account)) { + throw new CloudRuntimeException(String.format("Unable to find account with id: %s", networkVO.getAccountId())); + } + DomainVO domain = domainDao.findById(account.getDomainId()); + if (Objects.isNull(domain)) { + String msg = String.format("Unable to find domain with id: %s", account.getDomainId()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + String vpcName = null; + if (nonNull(networkVO.getVpcId())) { + VpcVO vpc = _vpcDao.findById(networkVO.getVpcId()); + if (isNull(vpc)) { + throw new CloudRuntimeException(String.format("Failed to find VPC network with id: %s", networkVO.getVpcId())); + } + vpcName = vpc.getName(); + } else { + logger.debug(String.format("Creating a Tier 1 Gateway for the network %s before creating the NSX segment", networkVO.getName())); + long networkOfferingId = networkVO.getNetworkOfferingId(); + NetworkOfferingVO networkOfferingVO = networkOfferingDao.findById(networkOfferingId); + boolean isSourceNatSupported = !NetworkOffering.NsxMode.ROUTED.name().equals(networkOfferingVO.getNsxMode()) && + networkOfferingServiceMapDao.areServicesSupportedByNetworkOffering(networkVO.getNetworkOfferingId(), Network.Service.SourceNat); + CreateNsxTier1GatewayCommand nsxTier1GatewayCommand = new CreateNsxTier1GatewayCommand(domain.getId(), account.getId(), zone.getId(), networkVO.getId(), networkVO.getName(), false, isSourceNatSupported); + + NsxAnswer nsxAnswer = nsxControllerUtils.sendNsxCommand(nsxTier1GatewayCommand, zone.getId()); + if (!nsxAnswer.getResult()) { + String msg = String.format("Could not create a Tier 1 Gateway for network %s: %s", networkVO.getName(), nsxAnswer.getDetails()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + CreateNsxSegmentCommand command = NsxHelper.createNsxSegmentCommand(domain, account, zone, vpcName, networkVO); + NsxAnswer answer = nsxControllerUtils.sendNsxCommand(command, zone.getId()); + if (!answer.getResult()) { + throw new CloudRuntimeException("can not create NSX network"); + } + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxProviderService.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxProviderService.java new file mode 100644 index 000000000000..47dfe04db3ef --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxProviderService.java @@ -0,0 +1,35 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.network.nsx.NsxProvider; +import com.cloud.utils.component.PluggableService; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.command.AddNsxControllerCmd; +import org.apache.cloudstack.api.response.NsxControllerResponse; + +import java.util.List; + +public interface NsxProviderService extends PluggableService { + NsxProvider addProvider(AddNsxControllerCmd cmd); + + NsxControllerResponse createNsxControllerResponse(NsxProvider nsxProvider); + + List listNsxProviders(Long zoneId); + + boolean deleteNsxController(Long nsxControllerId); +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxProviderServiceImpl.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxProviderServiceImpl.java new file mode 100644 index 000000000000..c59ebfd87551 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxProviderServiceImpl.java @@ -0,0 +1,213 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.amazonaws.util.CollectionUtils; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.DetailVO; +import com.cloud.host.Host; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.network.Network; +import com.cloud.network.Networks; +import com.cloud.network.nsx.NsxProvider; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.element.NsxProviderVO; +import com.cloud.resource.ResourceManager; +import com.cloud.utils.db.Transaction; +import com.cloud.utils.db.TransactionCallback; +import com.cloud.utils.exception.CloudRuntimeException; +import com.google.common.annotations.VisibleForTesting; +import org.apache.cloudstack.api.command.DeleteNsxControllerCmd; +import org.apache.cloudstack.api.command.ListNsxControllersCmd; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.command.AddNsxControllerCmd; +import org.apache.cloudstack.api.response.NsxControllerResponse; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.resource.NsxResource; +import org.apache.commons.lang3.StringUtils; + +import javax.inject.Inject; +import javax.naming.ConfigurationException; +import java.util.ArrayList; +import java.util.List; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.UUID; + +public class NsxProviderServiceImpl implements NsxProviderService { + + @Inject + NsxProviderDao nsxProviderDao; + @Inject + DataCenterDao dataCenterDao; + @Inject + PhysicalNetworkDao physicalNetworkDao; + @Inject + NetworkDao networkDao; + @Inject + ResourceManager resourceManager; + @Inject + HostDetailsDao hostDetailsDao; + + @Override + public NsxProvider addProvider(AddNsxControllerCmd cmd) { + final Long zoneId = cmd.getZoneId(); + final String name = cmd.getName(); + final String hostname = cmd.getHostname(); + final String port = cmd.getPort() == null || cmd.getPort().equals(StringUtils.EMPTY) ? "443" : cmd.getPort(); + final String username = cmd.getUsername(); + final String password = cmd.getPassword(); + final String tier0Gateway = cmd.getTier0Gateway(); + final String edgeCluster = cmd.getEdgeCluster(); + final String transportZone = cmd.getTransportZone(); + + Map params = new HashMap<>(); + params.put("guid", UUID.randomUUID().toString()); + params.put("zoneId", zoneId.toString()); + params.put("name", name); + params.put("hostname", hostname); + params.put("port", port); + params.put("username", username); + params.put("password", password); + params.put("tier0Gateway", tier0Gateway); + params.put("edgeCluster", edgeCluster); + params.put("transportZone", transportZone); + + Map hostdetails = new HashMap<>(params); + NsxProvider nsxProvider; + + NsxResource nsxResource = new NsxResource(); + try { + nsxResource.configure(hostname, hostdetails); + final Host host = resourceManager.addHost(zoneId, nsxResource, nsxResource.getType(), params); + if (host != null) { + nsxProvider = Transaction.execute((TransactionCallback) status -> { + NsxProviderVO nsxProviderVO = new NsxProviderVO.Builder() + .setZoneId(zoneId) + .setHostId(host.getId()) + .setProviderName(name) + .setHostname(hostname) + .setPort(port) + .setUsername(username) + .setPassword(password) + .setTier0Gateway(tier0Gateway) + .setEdgeCluster(edgeCluster) + .setTransportZone(transportZone) + .build(); + + nsxProviderDao.persist(nsxProviderVO); + + DetailVO detail = new DetailVO(host.getId(), "nsxcontrollerid", + String.valueOf(nsxProviderVO.getId())); + hostDetailsDao.persist(detail); + + return nsxProviderVO; + }); + } else { + throw new CloudRuntimeException("Failed to add NSX controller due to internal error."); + } + } catch (ConfigurationException e) { + throw new CloudRuntimeException(e.getMessage()); + } + return nsxProvider; + } + + @Override + public NsxControllerResponse createNsxControllerResponse(NsxProvider nsxProvider) { + DataCenterVO zone = dataCenterDao.findById(nsxProvider.getZoneId()); + if (Objects.isNull(zone)) { + throw new CloudRuntimeException(String.format("Failed to find zone with id %s", nsxProvider.getZoneId())); + } + NsxControllerResponse response = new NsxControllerResponse(); + response.setName(nsxProvider.getProviderName()); + response.setUuid(nsxProvider.getUuid()); + response.setHostname(nsxProvider.getHostname()); + response.setPort(nsxProvider.getPort()); + response.setZoneId(zone.getUuid()); + response.setZoneName(zone.getName()); + response.setTier0Gateway(nsxProvider.getTier0Gateway()); + response.setEdgeCluster(nsxProvider.getEdgeCluster()); + response.setTransportZone(nsxProvider.getTransportZone()); + response.setObjectName("nsxController"); + return response; + } + + @Override + public List listNsxProviders(Long zoneId) { + List nsxControllersResponseList = new ArrayList<>(); + if (zoneId != null) { + NsxProviderVO nsxProviderVO = nsxProviderDao.findByZoneId(zoneId); + if (Objects.nonNull(nsxProviderVO)) { + nsxControllersResponseList.add(createNsxControllerResponse(nsxProviderVO)); + } + } else { + List nsxProviderVOList = nsxProviderDao.listAll(); + for (NsxProviderVO nsxProviderVO : nsxProviderVOList) { + nsxControllersResponseList.add(createNsxControllerResponse(nsxProviderVO)); + } + } + + return nsxControllersResponseList; + } + + @Override + public boolean deleteNsxController(Long nsxControllerId) { + NsxProviderVO nsxProvider = nsxProviderDao.findById(nsxControllerId); + if (Objects.isNull(nsxProvider)) { + throw new InvalidParameterValueException(String.format("Failed to find NSX controller with id: %s", nsxControllerId)); + } + Long zoneId = nsxProvider.getZoneId(); + // Find the physical network we work for + List physicalNetworks = physicalNetworkDao.listByZone(zoneId); + for (PhysicalNetworkVO physicalNetwork : physicalNetworks) { + List networkList = networkDao.listByPhysicalNetwork(physicalNetwork.getId()); + if (!CollectionUtils.isNullOrEmpty(networkList)) { + validateNetworkState(networkList); + } + } + nsxProviderDao.remove(nsxControllerId); + return true; + } + + @Override + public List> getCommands() { + List> cmdList = new ArrayList<>(); + if (Boolean.TRUE.equals(NetworkOrchestrationService.NSX_ENABLED.value())) { + cmdList.add(AddNsxControllerCmd.class); + cmdList.add(ListNsxControllersCmd.class); + cmdList.add(DeleteNsxControllerCmd.class); + } + return cmdList; + } + + @VisibleForTesting + void validateNetworkState(List networkList) { + for (NetworkVO network : networkList) { + if (network.getBroadcastDomainType() == Networks.BroadcastDomainType.NSX && + ((network.getState() != Network.State.Shutdown) && (network.getState() != Network.State.Destroy))) { + throw new CloudRuntimeException("This NSX Controller cannot be deleted as there are one or more logical networks provisioned by CloudStack on it."); + } + } + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java new file mode 100644 index 000000000000..7463a19fd4e5 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxPublicNetworkGuru.java @@ -0,0 +1,170 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.VlanDetailsDao; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.exception.ConcurrentOperationException; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapacityException; +import com.cloud.network.Network; +import com.cloud.network.Networks; +import com.cloud.network.nsx.NsxService; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.guru.PublicNetworkGuru; +import com.cloud.network.vpc.VpcOffering; +import com.cloud.network.vpc.VpcOfferingVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.network.vpc.dao.VpcOfferingDao; +import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.user.Account; +import com.cloud.utils.exception.CloudRuntimeException; +import com.cloud.vm.NicProfile; +import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.utils.NsxControllerUtils; +import org.apache.cloudstack.utils.NsxHelper; +import org.apache.commons.collections.CollectionUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.inject.Inject; +import java.util.List; +import java.util.stream.Collectors; + +public class NsxPublicNetworkGuru extends PublicNetworkGuru { + + @Inject + private VlanDetailsDao vlanDetailsDao; + @Inject + private VpcDao vpcDao; + @Inject + private VpcOfferingServiceMapDao vpcOfferingServiceMapDao; + @Inject + private NsxControllerUtils nsxControllerUtils; + @Inject + private NsxService nsxService; + @Inject + private VpcOfferingDao vpcOfferingDao; + @Inject + private NetworkOfferingDao offeringDao; + + protected Logger logger = LogManager.getLogger(getClass()); + + public NsxPublicNetworkGuru() { + super(); + } + + @Override + protected boolean canHandle(NetworkOffering offering) { + return isMyTrafficType(offering.getTrafficType()) && offering.isSystemOnly() && offering.isForNsx(); + } + + @Override + public Network design(NetworkOffering offering, DeploymentPlan plan, Network network, String name, Long vpcId, Account owner) { + if (!canHandle(offering)) { + return null; + } + + if (offering.getTrafficType() == Networks.TrafficType.Public) { + return new NetworkVO(offering.getTrafficType(), Networks.Mode.Static, network.getBroadcastDomainType(), offering.getId(), Network.State.Setup, plan.getDataCenterId(), + plan.getPhysicalNetworkId(), offering.isRedundantRouter()); + } + return null; + } + + @Override + public NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException { + logger.debug("NSX Public network guru: allocate"); + + IPAddressVO ipAddress = _ipAddressDao.findByIp(nic.getIPv4Address()); + if (ipAddress == null) { + String err = String.format("Cannot find the IP address %s", nic.getIPv4Address()); + logger.error(err); + throw new CloudRuntimeException(err); + } + Long vpcId = ipAddress.getVpcId(); + boolean isForVpc = vpcId != null; + VpcVO vpc = vpcDao.findById(vpcId); + if (vpc == null) { + String err = String.format("Cannot find a VPC with ID %s", vpcId); + logger.error(err); + throw new CloudRuntimeException(err); + } + + // For NSX, use VR Public IP != Source NAT + List ips = _ipAddressDao.listByAssociatedVpc(vpc.getId(), true); + if (CollectionUtils.isEmpty(ips)) { + String err = String.format("Cannot find a source NAT IP for the VPC %s", vpc.getName()); + logger.error(err); + throw new CloudRuntimeException(err); + } + ips = ips.stream().filter(x -> !x.getAddress().addr().equals(nic.getIPv4Address())).collect(Collectors.toList()); + // Use Source NAT IP address from the NSX Public Range. Do not Use the VR Public IP address + ipAddress = ips.get(0); + if (ipAddress.isSourceNat() && !ipAddress.isForSystemVms()) { + VlanDetailsVO detail = vlanDetailsDao.findDetail(ipAddress.getVlanId(), ApiConstants.NSX_DETAIL_KEY); + if (detail != null && detail.getValue().equalsIgnoreCase("true")) { + long accountId = vpc.getAccountId(); + long domainId = vpc.getDomainId(); + long dataCenterId = vpc.getZoneId(); + long resourceId = vpc.getId(); + Network.Service[] services = { Network.Service.SourceNat }; + long networkOfferingId = vpc.getVpcOfferingId(); + VpcOfferingVO vpcVO = vpcOfferingDao.findById(networkOfferingId); + boolean sourceNatEnabled = !NetworkOffering.NsxMode.ROUTED.name().equals(vpcVO.getNsxMode()) && + vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(vpc.getVpcOfferingId(), services); + + logger.info(String.format("Creating Tier 1 Gateway for VPC %s", vpc.getName())); + boolean result = nsxService.createVpcNetwork(dataCenterId, accountId, domainId, resourceId, vpc.getName(), sourceNatEnabled); + if (!result) { + String msg = String.format("Error creating Tier 1 Gateway for VPC %s", vpc.getName()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + + boolean hasNatSupport = false; + VpcOffering vpcOffering = vpcOfferingDao.findById(vpc.getVpcOfferingId()); + hasNatSupport = NetworkOffering.NsxMode.NATTED.name().equals(vpcOffering.getNsxMode()); + + if (!hasNatSupport) { + return nic; + } + + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(domainId, accountId, dataCenterId, resourceId, isForVpc); + String translatedIp = ipAddress.getAddress().addr(); + logger.debug(String.format("Creating NSX Nat Rule for Tier1 GW %s for translated IP %s", tier1GatewayName, translatedIp)); + String natRuleId = NsxControllerUtils.getNsxNatRuleId(domainId, accountId, dataCenterId, resourceId, isForVpc); + CreateOrUpdateNsxTier1NatRuleCommand cmd = NsxHelper.createOrUpdateNsxNatRuleCommand(domainId, accountId, dataCenterId, tier1GatewayName, "SNAT", translatedIp, natRuleId); + NsxAnswer nsxAnswer = nsxControllerUtils.sendNsxCommand(cmd, dataCenterId); + if (!nsxAnswer.getResult()) { + String msg = String.format("Could not create NSX Nat Rule on Tier1 Gateway %s for IP %s", tier1GatewayName, translatedIp); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + } + } + return nic; + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java new file mode 100644 index 000000000000..f8880826a3f8 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceImpl.java @@ -0,0 +1,193 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.network.IpAddress; +import com.cloud.network.Network; +import com.cloud.network.nsx.NsxService; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.CreateNsxDistributedFirewallRulesCommand; +import org.apache.cloudstack.agent.api.CreateNsxLoadBalancerRuleCommand; +import org.apache.cloudstack.agent.api.CreateNsxPortForwardRuleCommand; +import org.apache.cloudstack.agent.api.CreateNsxStaticNatCommand; +import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand; +import org.apache.cloudstack.agent.api.DeleteNsxDistributedFirewallRulesCommand; +import org.apache.cloudstack.agent.api.DeleteNsxLoadBalancerRuleCommand; +import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand; +import org.apache.cloudstack.agent.api.DeleteNsxNatRuleCommand; +import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand; +import org.apache.cloudstack.resource.NsxNetworkRule; +import org.apache.cloudstack.utils.NsxControllerUtils; +import org.apache.cloudstack.utils.NsxHelper; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.inject.Inject; +import java.util.List; +import java.util.Objects; + +public class NsxServiceImpl implements NsxService { + @Inject + NsxControllerUtils nsxControllerUtils; + @Inject + VpcDao vpcDao; + @Inject + NetworkDao networkDao; + + protected Logger logger = LogManager.getLogger(getClass()); + + public boolean createVpcNetwork(Long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled) { + CreateNsxTier1GatewayCommand createNsxTier1GatewayCommand = + new CreateNsxTier1GatewayCommand(domainId, accountId, zoneId, vpcId, vpcName, true, sourceNatEnabled); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(createNsxTier1GatewayCommand, zoneId); + return result.getResult(); + } + + @Override + public boolean updateVpcSourceNatIp(Vpc vpc, IpAddress address) { + if (vpc == null || address == null) { + return false; + } + long accountId = vpc.getAccountId(); + long domainId = vpc.getDomainId(); + long zoneId = vpc.getZoneId(); + long vpcId = vpc.getId(); + + logger.debug(String.format("Updating the source NAT IP for NSX VPC %s to IP: %s", vpc.getName(), address.getAddress().addr())); + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(domainId, accountId, zoneId, vpcId, true); + String sourceNatRuleId = NsxControllerUtils.getNsxNatRuleId(domainId, accountId, zoneId, vpcId, true); + CreateOrUpdateNsxTier1NatRuleCommand cmd = NsxHelper.createOrUpdateNsxNatRuleCommand(domainId, accountId, zoneId, tier1GatewayName, "SNAT", address.getAddress().addr(), sourceNatRuleId); + NsxAnswer answer = nsxControllerUtils.sendNsxCommand(cmd, zoneId); + if (!answer.getResult()) { + logger.error(String.format("Could not update the source NAT IP address for VPC %s: %s", vpc.getName(), answer.getDetails())); + return false; + } + return true; + } + + public boolean createNetwork(Long zoneId, long accountId, long domainId, Long networkId, String networkName) { + CreateNsxTier1GatewayCommand createNsxTier1GatewayCommand = + new CreateNsxTier1GatewayCommand(domainId, accountId, zoneId, networkId, networkName, false, false); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(createNsxTier1GatewayCommand, zoneId); + return result.getResult(); + } + + public boolean deleteVpcNetwork(Long zoneId, long accountId, long domainId, Long vpcId, String vpcName) { + DeleteNsxTier1GatewayCommand deleteNsxTier1GatewayCommand = + new DeleteNsxTier1GatewayCommand(domainId, accountId, zoneId, vpcId, vpcName, true); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(deleteNsxTier1GatewayCommand, zoneId); + return result.getResult(); + } + + public boolean deleteNetwork(long zoneId, long accountId, long domainId, NetworkVO network) { + String vpcName = null; + if (Objects.nonNull(network.getVpcId())) { + VpcVO vpc = vpcDao.findById(network.getVpcId()); + vpcName = Objects.nonNull(vpc) ? vpc.getName() : null; + } + DeleteNsxSegmentCommand deleteNsxSegmentCommand = new DeleteNsxSegmentCommand(domainId, accountId, zoneId, + network.getVpcId(), vpcName, network.getId(), network.getName()); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(deleteNsxSegmentCommand, network.getDataCenterId()); + if (!result.getResult()) { + String msg = String.format("Could not remove the NSX segment for network %s: %s", network.getName(), result.getDetails()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + + if (Objects.isNull(network.getVpcId())) { + DeleteNsxTier1GatewayCommand deleteNsxTier1GatewayCommand = new DeleteNsxTier1GatewayCommand(domainId, accountId, zoneId, network.getId(), network.getName(), false); + result = nsxControllerUtils.sendNsxCommand(deleteNsxTier1GatewayCommand, zoneId); + } + return result.getResult(); + } + + public boolean createStaticNatRule(long zoneId, long domainId, long accountId, Long networkResourceId, String networkResourceName, + boolean isVpcResource, long vmId, String publicIp, String vmIp) { + CreateNsxStaticNatCommand createNsxStaticNatCommand = new CreateNsxStaticNatCommand(domainId, accountId, zoneId, + networkResourceId, networkResourceName, isVpcResource, vmId, publicIp, vmIp); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(createNsxStaticNatCommand, zoneId); + return result.getResult(); + } + + public boolean deleteStaticNatRule(long zoneId, long domainId, long accountId, Long networkResourceId, String networkResourceName, + boolean isVpcResource) { + DeleteNsxNatRuleCommand deleteNsxStaticNatCommand = new DeleteNsxNatRuleCommand(domainId, accountId, zoneId, + networkResourceId, networkResourceName, isVpcResource, null, null, null, null); + deleteNsxStaticNatCommand.setService(Network.Service.StaticNat); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(deleteNsxStaticNatCommand, zoneId); + return result.getResult(); + } + + public boolean createPortForwardRule(NsxNetworkRule netRule) { + // TODO: if port doesn't exist in default list of services, create a service entry + CreateNsxPortForwardRuleCommand createPortForwardCmd = new CreateNsxPortForwardRuleCommand(netRule.getDomainId(), + netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(), + netRule.getNetworkResourceName(), netRule.isVpcResource(), netRule.getVmId(), netRule.getRuleId(), + netRule.getPublicIp(), netRule.getVmIp(), netRule.getPublicPort(), netRule.getPrivatePort(), netRule.getProtocol()); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(createPortForwardCmd, netRule.getZoneId()); + return result.getResult(); + } + + public boolean deletePortForwardRule(NsxNetworkRule netRule) { + DeleteNsxNatRuleCommand deleteCmd = new DeleteNsxNatRuleCommand(netRule.getDomainId(), + netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(), + netRule.getNetworkResourceName(), netRule.isVpcResource(), netRule.getVmId(), netRule.getRuleId(), netRule.getPrivatePort(), netRule.getProtocol()); + deleteCmd.setService(Network.Service.PortForwarding); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(deleteCmd, netRule.getZoneId()); + return result.getResult(); + } + + public boolean createLbRule(NsxNetworkRule netRule) { + CreateNsxLoadBalancerRuleCommand command = new CreateNsxLoadBalancerRuleCommand(netRule.getDomainId(), + netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(), + netRule.getNetworkResourceName(), netRule.isVpcResource(), netRule.getMemberList(), netRule.getRuleId(), + netRule.getPublicPort(), netRule.getPrivatePort(), netRule.getAlgorithm(), netRule.getProtocol()); + command.setPublicIp(netRule.getPublicIp()); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(command, netRule.getZoneId()); + return result.getResult(); + } + + public boolean deleteLbRule(NsxNetworkRule netRule) { + DeleteNsxLoadBalancerRuleCommand command = new DeleteNsxLoadBalancerRuleCommand(netRule.getDomainId(), + netRule.getAccountId(), netRule.getZoneId(), netRule.getNetworkResourceId(), + netRule.getNetworkResourceName(), netRule.isVpcResource(), netRule.getMemberList(), netRule.getRuleId(), + netRule.getVmId()); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(command, netRule.getZoneId()); + return result.getResult(); + } + + public boolean addFirewallRules(Network network, List netRules) { + CreateNsxDistributedFirewallRulesCommand command = new CreateNsxDistributedFirewallRulesCommand(network.getDomainId(), + network.getAccountId(), network.getDataCenterId(), network.getVpcId(), network.getId(), netRules); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(command, network.getDataCenterId()); + return result.getResult(); + } + + public boolean deleteFirewallRules(Network network, List netRules) { + DeleteNsxDistributedFirewallRulesCommand command = new DeleteNsxDistributedFirewallRulesCommand(network.getDomainId(), + network.getAccountId(), network.getDataCenterId(), network.getVpcId(), network.getId(), netRules); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(command, network.getDataCenterId()); + return result.getResult(); + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java new file mode 100644 index 000000000000..e064a6b62916 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxControllerUtils.java @@ -0,0 +1,148 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.utils; + +import com.cloud.agent.AgentManager; +import com.cloud.agent.api.Answer; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.element.NsxProviderVO; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.NsxCommand; +import org.apache.cloudstack.service.NsxApiClient; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.stereotype.Component; + +import javax.inject.Inject; + +import static java.util.Objects.isNull; + +@Component +public class NsxControllerUtils { + protected Logger logger = LogManager.getLogger(getClass()); + + @Inject + private AgentManager agentMgr; + @Inject + private NsxProviderDao nsxProviderDao; + + public static String getNsxNatRuleId(long domainId, long accountId, long dataCenterId, long resourceId, boolean isForVpc) { + String resourcePrefix = isForVpc ? "V" : "N"; + return String.format("D%s-A%s-Z%s-%s%s-NAT", domainId, accountId, dataCenterId, resourcePrefix, resourceId); + } + + public static String getNsxDistributedFirewallPolicyRuleId(String segmentName, long ruleId) { + return String.format("%s-R%s", segmentName, ruleId); + } + + public NsxAnswer sendNsxCommand(NsxCommand cmd, long zoneId) throws IllegalArgumentException { + NsxProviderVO nsxProviderVO = nsxProviderDao.findByZoneId(zoneId); + if (nsxProviderVO == null) { + logger.error("No NSX controller was found!"); + throw new InvalidParameterValueException("Failed to find an NSX controller"); + } + Answer answer = agentMgr.easySend(nsxProviderVO.getHostId(), cmd); + + if (answer == null || !answer.getResult()) { + logger.error("NSX API Command failed"); + throw new InvalidParameterValueException("Failed API call to NSX controller"); + } + + return (NsxAnswer) answer; + } + + /** + * Generates the Tier 1 Gateway name and identifier for the resource on the NSX manager + */ + public static String getTier1GatewayName(long domainId, long accountId, long zoneId, + Long networkResourceId, boolean isResourceVpc) { + String resourcePrefix = isResourceVpc ? "V" : "N"; + return String.format("D%s-A%s-Z%s-%s%s", domainId, accountId, zoneId, resourcePrefix, networkResourceId); + } + + public static String getNsxSegmentId(long domainId, long accountId, long zoneId, Long vpcId, long networkId) { + String segmentName = String.format("D%s-A%s-Z%s", domainId, accountId, zoneId); + if (isNull(vpcId)) { + return String.format("%s-S%s", segmentName, networkId); + } + return String.format("%s-V%s-S%s",segmentName, vpcId, networkId); + } + + public static String getNsxDhcpRelayConfigId(long zoneId, long domainId, long accountId, Long vpcId, long networkId) { + String suffix = "Relay"; + if (isNull(vpcId)) { + return String.format("D%s-A%s-Z%s-S%s-%s", domainId, accountId, zoneId, networkId, suffix); + } + return String.format("D%s-A%s-Z%s-V%s-S%s-%s", domainId, accountId, zoneId, vpcId, networkId, suffix); + } + + public static String getStaticNatRuleName(long domainId, long accountId, long zoneId, Long networkResourceId, boolean isVpcResource) { + String suffix = "-STATICNAT"; + return getTier1GatewayName(domainId, accountId, zoneId, networkResourceId, isVpcResource) + suffix; + } + + public static String getPortForwardRuleName(long domainId, long accountId, long zoneId, Long networkResourceId, long ruleId, boolean isVpcResource) { + String suffix = "-PF"; + return getTier1GatewayName(domainId, accountId, zoneId, networkResourceId, isVpcResource) + suffix + ruleId; + } + + public static String getServiceName(String ruleName, String port, String protocol, Integer icmpType, Integer icmpCode) { + return protocol.equalsIgnoreCase("icmp") ? + String.format("%s-SVC-%s-%s-%s", ruleName, icmpType, icmpCode, protocol) : + String.format("%s-SVC-%s-%s", ruleName, port, protocol); + } + + public static String getServiceEntryName(String ruleName, String port, String protocol) { + return ruleName + "-SE-" + port + "-" + protocol; + } + + public static String getLoadBalancerName(String tier1GatewayName) { + return tier1GatewayName + "-LB"; + } + + public static String getLoadBalancerRuleName(String tier1GatewayName, long lbId) { + return tier1GatewayName + "-LB" + lbId; + } + + public static String getServerPoolName(String tier1GatewayName, long lbId) { + return getLoadBalancerRuleName(tier1GatewayName, lbId) + "-SP"; + } + + public static String getActiveMonitorProfileName(String lbServerPoolName, String port, String protocol) { + return lbServerPoolName + "-" + protocol + "-" + port + "-AM"; + } + + public static String getVirtualServerName(String tier1GatewayName, long lbId) { + return getLoadBalancerRuleName(tier1GatewayName, lbId) + "-VS"; + } + + public static String getServerPoolMemberName(String tier1GatewayName, long vmId) { + return tier1GatewayName + "-VM" + vmId; + } + + public static String getLoadBalancerAlgorithm(String algorithm) { + switch (algorithm) { + case "leastconn": + return NsxApiClient.LBAlgorithm.LEAST_CONNECTION.name(); + case "source": + return NsxApiClient.LBAlgorithm.IP_HASH.name(); + default: + return NsxApiClient.LBAlgorithm.ROUND_ROBIN.name(); + } + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxHelper.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxHelper.java new file mode 100644 index 000000000000..b0668a0704f9 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/utils/NsxHelper.java @@ -0,0 +1,53 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.utils; + +import com.cloud.dc.DataCenter; +import com.cloud.domain.DomainVO; +import com.cloud.network.Network; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.user.Account; +import org.apache.cloudstack.agent.api.CreateNsxDhcpRelayConfigCommand; +import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand; +import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand; + +import java.util.List; + +public class NsxHelper { + + private NsxHelper() { + } + + public static CreateNsxDhcpRelayConfigCommand createNsxDhcpRelayConfigCommand(DomainVO domain, Account account, DataCenter zone, VpcVO vpc, Network network, List addresses) { + Long vpcId = vpc != null ? vpc.getId() : null; + String vpcName = vpc != null ? vpc.getName() : null; + return new CreateNsxDhcpRelayConfigCommand(domain.getId(), account.getId(), zone.getId(), + vpcId, vpcName, network.getId(), network.getName(), addresses); + } + + public static CreateNsxSegmentCommand createNsxSegmentCommand(DomainVO domain, Account account, DataCenter zone, String vpcName, NetworkVO networkVO) { + return new CreateNsxSegmentCommand(domain.getId(), account.getId(), zone.getId(), + networkVO.getVpcId(), vpcName, networkVO.getId(), networkVO.getName(), networkVO.getGateway(), networkVO.getCidr()); + } + + public static CreateOrUpdateNsxTier1NatRuleCommand createOrUpdateNsxNatRuleCommand(long domainId, long accountId, long zoneId, + String tier1Gateway, String action, String ipAddress, + String natRuleId) { + return new CreateOrUpdateNsxTier1NatRuleCommand(domainId, accountId, zoneId, tier1Gateway, action, ipAddress, natRuleId); + } +} diff --git a/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/core/spring-nsx-core-managers-context.xml b/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/core/spring-nsx-core-managers-context.xml new file mode 100644 index 000000000000..7010b8c07f44 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/core/spring-nsx-core-managers-context.xml @@ -0,0 +1,32 @@ + + + + + + + diff --git a/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/nsx/module.properties b/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/nsx/module.properties new file mode 100644 index 000000000000..1630826d102d --- /dev/null +++ b/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/nsx/module.properties @@ -0,0 +1,21 @@ +# +# 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. +# + +name=nsx +parent=network diff --git a/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/nsx/spring-nsx-context.xml b/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/nsx/spring-nsx-context.xml new file mode 100644 index 000000000000..d5e3e212de1c --- /dev/null +++ b/plugins/network-elements/nsx/src/main/resources/META-INF/cloudstack/nsx/spring-nsx-context.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/resource/NsxResourceTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/resource/NsxResourceTest.java new file mode 100644 index 000000000000..ee4f4fb64c20 --- /dev/null +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/resource/NsxResourceTest.java @@ -0,0 +1,293 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.resource; + +import com.cloud.network.dao.NetworkVO; +import com.cloud.utils.exception.CloudRuntimeException; +import com.vmware.nsx.model.TransportZone; +import com.vmware.nsx.model.TransportZoneListResult; +import com.vmware.nsx_policy.model.EnforcementPoint; +import com.vmware.nsx_policy.model.Site; +import junit.framework.Assert; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.CreateNsxDistributedFirewallRulesCommand; +import org.apache.cloudstack.agent.api.CreateNsxLoadBalancerRuleCommand; +import org.apache.cloudstack.agent.api.CreateNsxPortForwardRuleCommand; +import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand; +import org.apache.cloudstack.agent.api.CreateNsxStaticNatCommand; +import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand; +import org.apache.cloudstack.agent.api.DeleteNsxDistributedFirewallRulesCommand; +import org.apache.cloudstack.agent.api.DeleteNsxNatRuleCommand; +import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand; +import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.NsxCommand; +import org.apache.cloudstack.service.NsxApiClient; +import org.apache.cloudstack.utils.NsxControllerUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; + +import javax.naming.ConfigurationException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class NsxResourceTest { + + @Mock + NsxApiClient nsxApi; + + NsxResource nsxResource; + AutoCloseable closeable; + @Mock + TransportZoneListResult transportZoneListResult; + + private static final String transportZone = "Overlay"; + private static final String tier0Gateway = "Tier0-GW01"; + private static final String edgeCluster = "EdgeCluster"; + + private static final long domainId = 1L; + private static final long accountId = 2L; + private static final long zoneId = 1L; + + @Before + public void setup() { + closeable = MockitoAnnotations.openMocks(this); + nsxResource = new NsxResource(); + nsxResource.nsxApiClient = nsxApi; + nsxResource.transportZone = transportZone; + nsxResource.tier0Gateway = tier0Gateway; + nsxResource.edgeCluster = edgeCluster; + } + + @After + public void tearDown() throws Exception { + closeable.close(); + } + + @Test + public void testConfigure() throws ConfigurationException { + Map params = new HashMap<>(); + params.put("name", "nsxController"); + params.put("guid", "5944b356-644f-11ee-b8c2-f37bc1b564ff"); + params.put("zoneId", "1"); + params.put("hostname", "host1"); + params.put("username", "admin"); + params.put("password", "password"); + params.put("tier0Gateway", tier0Gateway); + params.put("edgeCluster", edgeCluster); + params.put("transportZone", transportZone); + params.put("port", "443"); + + Assert.assertTrue(nsxResource.configure("nsx", params)); + } + + @Test + public void testConfigure_MissingParameter() throws ConfigurationException { + Map params = new HashMap<>(); + + assertThrows(ConfigurationException.class, () -> nsxResource.configure("nsx", params)); + } + + @Test + public void testCreateNsxTier1Gateway() { + NsxCommand command = new CreateNsxTier1GatewayCommand(domainId, accountId, zoneId, + 3L, "VPC01", true, false); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertTrue(answer.getResult()); + } + + @Test + public void testCreateNsxTier1GatewayError() { + NsxCommand command = new CreateNsxTier1GatewayCommand(domainId, accountId, zoneId, + 3L, "VPC01", true, false); + Mockito.doThrow(new CloudRuntimeException("ERROR")) + .when(nsxApi).createTier1Gateway(anyString(), anyString(), anyString(), anyBoolean()); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertFalse(answer.getResult()); + } + + @Test + public void testDeleteTier1Gateway() { + NsxCommand command = new DeleteNsxTier1GatewayCommand(domainId, accountId, zoneId, + 2L, "VPC01", true); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertTrue(answer.getResult()); + } + + @Test + public void testDeleteTier1GatewayError() { + NsxCommand command = new DeleteNsxTier1GatewayCommand(domainId, accountId, zoneId, + 2L, "VPC01", true); + Mockito.doThrow(new CloudRuntimeException("ERROR")).when(nsxApi).deleteTier1Gateway(anyString()); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertFalse(answer.getResult()); + } + + @Test + public void testCreateNsxSegment() { + NetworkVO tierNetwork = new NetworkVO(); + tierNetwork.setName("tier1"); + tierNetwork.setCidr("10.0.0.0/8"); + tierNetwork.setGateway("10.0.0.1"); + Site site = mock(Site.class); + List siteList = List.of(site); + EnforcementPoint enforcementPoint = mock(EnforcementPoint.class); + List enforcementPointList = List.of(enforcementPoint); + List transportZoneList = List.of(new TransportZone.Builder().setDisplayName(transportZone).build()); + + NsxCommand command = new CreateNsxSegmentCommand(domainId, accountId, zoneId, + 2L, "VPC01", 3L, "Web", "10.10.10.1", "10.10.10.0/24"); + + when(nsxApi.getDefaultSiteId()).thenReturn("site1"); + + when(nsxApi.getDefaultEnforcementPointPath(anyString())).thenReturn("enforcementPointPath"); + + when(nsxApi.getTransportZones()).thenReturn(transportZoneListResult); + when(transportZoneListResult.getResults()).thenReturn(transportZoneList); + + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertTrue(answer.getResult()); + } + + @Test + public void testCreateNsxSegmentEmptySites() { + when(nsxApi.getDefaultSiteId()).thenReturn(null); + CreateNsxSegmentCommand command = Mockito.mock(CreateNsxSegmentCommand.class); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertFalse(answer.getResult()); + } + + @Test + public void testCreateNsxSegmentEmptyEnforcementPoints() { + Site site = mock(Site.class); + when(nsxApi.getDefaultSiteId()).thenReturn("site1"); + when(nsxApi.getDefaultEnforcementPointPath(anyString())).thenReturn(null); + CreateNsxSegmentCommand command = Mockito.mock(CreateNsxSegmentCommand.class); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertFalse(answer.getResult()); + } + + @Test + public void testCreateNsxSegmentEmptyTransportZones() { + Site site = mock(Site.class); + when(nsxApi.getDefaultSiteId()).thenReturn("site1"); + CreateNsxSegmentCommand command = Mockito.mock(CreateNsxSegmentCommand.class); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertFalse(answer.getResult()); + } + + @Test + public void testDeleteNsxSegment() { + NetworkVO tierNetwork = new NetworkVO(); + tierNetwork.setName("tier1"); + DeleteNsxSegmentCommand command = new DeleteNsxSegmentCommand(domainId, accountId, zoneId, + 3L, "VPC01", 2L, "Web"); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertTrue(answer.getResult()); + } + + @Test + public void testDeleteNsxSegmentError() { + NetworkVO tierNetwork = new NetworkVO(); + tierNetwork.setName("tier1"); + DeleteNsxSegmentCommand command = new DeleteNsxSegmentCommand(domainId, accountId, zoneId, + 3L, "VPC01", 2L, "Web"); + doThrow(new CloudRuntimeException("ERROR")).when(nsxApi).deleteSegment(anyLong(), anyLong(), anyLong(), anyLong(), anyLong(), anyString()); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertFalse(answer.getResult()); + } + + @Test + public void testCreateStaticNat() { + CreateNsxStaticNatCommand cmd = new CreateNsxStaticNatCommand(domainId, accountId, zoneId, 3L, "VPC01", true, 2L, "10.1.12.10", "172.30.20.12"); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void testCreatePortForwardRule() { + CreateNsxPortForwardRuleCommand cmd = new CreateNsxPortForwardRuleCommand(domainId, accountId, zoneId, 3L, "VPC01", true, 2L, 5L, "10.1.12.10", "172.30.20.12", "2222", "22", "tcp"); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void testDeleteNsxNatRule() { + DeleteNsxNatRuleCommand cmd = new DeleteNsxNatRuleCommand(domainId, accountId, zoneId, 3L, "VPC01", true, 2L, 5L, "22", "tcp"); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void testCreateNsxLoadBalancerRule() { + List loadBalancerMembers = List.of(new NsxLoadBalancerMember( + 1L, "172.30.20.12", 6443 + )); + CreateNsxLoadBalancerRuleCommand cmd = new CreateNsxLoadBalancerRuleCommand(domainId, accountId, zoneId, + 3L, "VPC01", true, loadBalancerMembers, 1L, "6443", "6443", "RoundRobin", "TCP"); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + + @Test + public void testCreateNsxDistributedFirewallRule() { + List networkRules = List.of(new NsxNetworkRule()); + CreateNsxDistributedFirewallRulesCommand cmd = new CreateNsxDistributedFirewallRulesCommand(domainId, accountId, zoneId, + 3L, 1L, networkRules); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void testDeleteNsxDistributedFirewallRule() { + List networkRules = List.of(new NsxNetworkRule()); + DeleteNsxDistributedFirewallRulesCommand cmd = new DeleteNsxDistributedFirewallRulesCommand(domainId, accountId, zoneId, + 3L, 1L, networkRules); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(cmd); + assertTrue(answer.getResult()); + } + + @Test + public void testCreateTier1NatRule() { + long vpcId = 5L; + String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(domainId, accountId, zoneId, vpcId, true); + CreateOrUpdateNsxTier1NatRuleCommand command = new CreateOrUpdateNsxTier1NatRuleCommand(domainId, accountId, zoneId, + tier1GatewayName, "SNAT", "10.1.10.10", "natRuleId"); + NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command); + assertTrue(answer.getResult()); + } +} diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java new file mode 100644 index 000000000000..a0fde08ade8b --- /dev/null +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxApiClientTest.java @@ -0,0 +1,96 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.network.Network; +import com.vmware.nsx_policy.infra.domains.Groups; +import com.vmware.nsx_policy.model.Group; +import com.vmware.nsx_policy.model.PathExpression; +import com.vmware.vapi.bindings.Service; +import org.apache.cloudstack.resource.NsxNetworkRule; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockedConstruction; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.util.List; +import java.util.function.Function; + +public class NsxApiClientTest { + + @Mock + private Function, Service> nsxService; + @Mock + private Groups groupService; + + private NsxApiClient client = new NsxApiClient(); + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + client.nsxService = nsxService; + Mockito.when(nsxService.apply(Groups.class)).thenReturn(groupService); + } + + @Test + public void testCreateGroupForSegment() { + final Group[] groups = new Group[1]; + final PathExpression[] pathExpressions = new PathExpression[1]; + try (MockedConstruction ignored = Mockito.mockConstruction(Group.class, (mock, context) -> { + groups[0] = mock; + }); MockedConstruction ignoredExp = Mockito.mockConstruction(PathExpression.class, (mock, context) -> { + pathExpressions[0] = mock; + }) + ) { + String segmentName = "segment1"; + client.createGroupForSegment(segmentName); + Mockito.verify(groupService).patch(NsxApiClient.DEFAULT_DOMAIN, segmentName, groups[0]); + String segmentPath = String.format("%s/%s", NsxApiClient.SEGMENTS_PATH, segmentName); + Mockito.verify(groups[0]).setExpression(List.of(pathExpressions[0])); + Mockito.verify(pathExpressions[0]).setPaths(List.of(segmentPath)); + } + } + + @Test + public void testGetGroupsForTrafficIngress() { + NsxNetworkRule rule = Mockito.mock(NsxNetworkRule.class); + Mockito.when(rule.getSourceCidrList()).thenReturn(List.of("ANY")); + Mockito.when(rule.getTrafficType()).thenReturn("Ingress"); + Mockito.when(rule.getService()).thenReturn(Network.Service.NetworkACL); + String segmentName = "segment"; + List sourceGroups = client.getGroupsForTraffic(rule, segmentName, true); + List destinationGroups = client.getGroupsForTraffic(rule, segmentName, false); + Assert.assertEquals(List.of("ANY"), sourceGroups); + Assert.assertEquals(List.of(String.format("%s/%s", NsxApiClient.GROUPS_PATH_PREFIX, segmentName)), destinationGroups); + } + + @Test + public void testGetGroupsForTrafficEgress() { + NsxNetworkRule rule = Mockito.mock(NsxNetworkRule.class); + Mockito.when(rule.getSourceCidrList()).thenReturn(List.of("ANY")); + Mockito.when(rule.getTrafficType()).thenReturn("Egress"); + Mockito.when(rule.getService()).thenReturn(Network.Service.NetworkACL); + String segmentName = "segment"; + List sourceGroups = client.getGroupsForTraffic(rule, segmentName, true); + List destinationGroups = client.getGroupsForTraffic(rule, segmentName, false); + Assert.assertEquals(List.of(String.format("%s/%s", NsxApiClient.GROUPS_PATH_PREFIX, segmentName)), sourceGroups); + Assert.assertEquals(List.of("ANY"), destinationGroups); + } +} diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java new file mode 100644 index 000000000000..ff7fa5427ee7 --- /dev/null +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxElementTest.java @@ -0,0 +1,493 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.api.ApiDBUtils; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.hypervisor.Hypervisor; +import com.cloud.network.Network; +import com.cloud.network.NetworkModel; +import com.cloud.network.Networks; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.LoadBalancerVMMapDao; +import com.cloud.network.dao.LoadBalancerVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.lb.LoadBalancingRule; +import com.cloud.network.rules.FirewallRule; +import com.cloud.network.rules.FirewallRuleVO; +import com.cloud.network.rules.PortForwardingRule; +import com.cloud.network.rules.PortForwardingRuleVO; +import com.cloud.network.rules.StaticNatImpl; +import com.cloud.network.vpc.NetworkACLItem; +import com.cloud.network.vpc.NetworkACLItemVO; +import com.cloud.network.vpc.Vpc; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; +import com.cloud.resource.ResourceManager; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.utils.Pair; +import com.cloud.utils.net.Ip; +import com.cloud.vm.NicVO; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.acl.ControlledEntity; +import org.apache.cloudstack.resource.NsxNetworkRule; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class NsxElementTest { + + @Mock + DataCenterDao dataCenterDao; + @Mock + NsxServiceImpl nsxService; + @Mock + AccountManager accountManager; + @Mock + NetworkDao networkDao; + @Mock + ResourceManager resourceManager; + @Mock + PhysicalNetworkDao physicalNetworkDao; + @Mock + NetworkModel networkModel; + @Mock + Vpc vpc; + @Mock + DataCenterVO zone; + @Mock + DataCenterVO dataCenterVO; + @Mock + Account account; + @Mock + DomainVO domain; + @Mock + IPAddressDao ipAddressDao; + @Mock + VMInstanceDao vmInstanceDao; + @Mock + VpcDao vpcDao; + @Mock + UserVmDao userVmDao; + @Mock + private VpcOfferingServiceMapDao vpcOfferingServiceMapDao; + @Mock + LoadBalancerVMMapDao lbVmMapDao; + + NsxElement nsxElement; + ReservationContext reservationContext; + DeployDestination deployDestination; + @Mock + DomainDao domainDao; + + @Before + public void setup() throws NoSuchFieldException, IllegalAccessException { + nsxElement = new NsxElement(); + + nsxElement.dataCenterDao = dataCenterDao; + nsxElement.nsxService = nsxService; + nsxElement.accountMgr = accountManager; + nsxElement.networkDao = networkDao; + nsxElement.resourceManager = resourceManager; + nsxElement.physicalNetworkDao = physicalNetworkDao; + nsxElement.domainDao = domainDao; + nsxElement.networkModel = networkModel; + nsxElement.vpcOfferingServiceMapDao = vpcOfferingServiceMapDao; + nsxElement.ipAddressDao = ipAddressDao; + nsxElement.vmInstanceDao = vmInstanceDao; + nsxElement.vpcDao = vpcDao; + nsxElement.lbVmMapDao = lbVmMapDao; + + Field field = ApiDBUtils.class.getDeclaredField("s_ipAddressDao"); + field.setAccessible(true); + field.set(null, ipAddressDao); + + field = ApiDBUtils.class.getDeclaredField("s_userVmDao"); + field.setAccessible(true); + field.set(null, userVmDao); + reservationContext = mock(ReservationContext.class); + deployDestination = mock(DeployDestination.class); + + when(vpc.getZoneId()).thenReturn(1L); + when(vpc.getAccountId()).thenReturn(2L); + when(dataCenterVO.getId()).thenReturn(1L); + when(vpc.getName()).thenReturn("VPC01"); + when(accountManager.getAccount(2L)).thenReturn(account); + when(dataCenterDao.findById(anyLong())).thenReturn(dataCenterVO); + when(domainDao.findById(anyLong())).thenReturn(domain); + when(vpc.getZoneId()).thenReturn(1L); + when(vpc.getName()).thenReturn("testVPC"); + + PhysicalNetworkVO physicalNetworkVO = new PhysicalNetworkVO(); + physicalNetworkVO.setIsolationMethods(List.of("NSX")); + List physicalNetworkVOList = List.of(physicalNetworkVO); + + when(physicalNetworkDao.listByZoneAndTrafficType(1L, Networks.TrafficType.Guest)).thenReturn(physicalNetworkVOList); + } + + @Test + public void testImplementVpc() throws ResourceUnavailableException, InsufficientCapacityException { + assertTrue(nsxElement.implementVpc(vpc, deployDestination, reservationContext)); + } + + @Test + public void testShutdownVpc() { + when(nsxService.deleteVpcNetwork(anyLong(), anyLong(), anyLong(), anyLong(), anyString())).thenReturn(true); + + assertTrue(nsxElement.shutdownVpc(vpc, reservationContext)); + } + + @Test + public void testTransformActionValue() { + NsxNetworkRule.NsxRuleAction action = nsxElement.transformActionValue(NetworkACLItem.Action.Deny); + Assert.assertEquals(NsxNetworkRule.NsxRuleAction.DROP, action); + } + + @Test + public void testTransformCidrListValuesEmptyList() { + List values = nsxElement.transformCidrListValues(null); + Assert.assertNotNull(values); + Assert.assertTrue(values.isEmpty()); + } + + @Test + public void testTransformCidrListValuesList() { + List values = nsxElement.transformCidrListValues(List.of("0.0.0.0/0")); + Assert.assertEquals(1, values.size()); + Assert.assertEquals("ANY", values.get(0)); + } + + @Test + public void testCanHandleService() { + when(networkModel.isProviderForNetwork(any(Network.Provider.class), anyLong())).thenReturn(true); + + Network.Service service = new Network.Service("service1", new Network.Capability("capability")); + NetworkVO network = new NetworkVO(); + network.setName("network1"); + assertTrue(nsxElement.canHandle(network, service)); + } + + @Test + public void testApplyStaticNatRules() throws ResourceUnavailableException { + StaticNatImpl rule = new StaticNatImpl(1L , 1L, 3L, 7L, "172.30.10.15", false); + NetworkVO networkVO = new NetworkVO(1L, Networks.TrafficType.Public, Networks.Mode.Static, + Networks.BroadcastDomainType.NSX, 12L, 2L, 5L, 1L, "network1", + "network1", null, Network.GuestType.Isolated, 2L, 2L, + ControlledEntity.ACLType.Domain, false, 1L, false ); + + Ip ip = new Ip("10.1.13.15"); + IPAddressVO ipAddress = new IPAddressVO(ip, 2L, 0xaabbccddeeffL, 3L, false); + ipAddress.setAssociatedWithVmId(10L); + + VMInstanceVO vm = new VMInstanceVO(10L, 9L, "vm1", "i-5-10-VM" , VirtualMachine.Type.User, + 18L, Hypervisor.HypervisorType.VMware, 26L, + 2L, 5L, 6L, false, false); + + NicVO nic = Mockito.mock(NicVO.class); + VpcVO vpc = Mockito.mock(VpcVO.class); + + when(ipAddressDao.findByIdIncludingRemoved(anyLong())).thenReturn(ipAddress); + when(vmInstanceDao.findByIdIncludingRemoved(anyLong())).thenReturn(vm); + when(networkModel.getNicInNetworkIncludingRemoved(anyLong(), anyLong())).thenReturn(nic); + when(vpcDao.findById(anyLong())).thenReturn(vpc); + when(vpc.getId()).thenReturn(1L); + when(vpc.getName()).thenReturn("vpc1"); + when(nsxService.createStaticNatRule(anyLong(), anyLong(), anyLong(), anyLong(), anyString(), anyBoolean(), anyLong(), anyString(), anyString())).thenReturn(true); + + assertTrue(nsxElement.applyStaticNats(networkVO, List.of(rule))); + } + + @Test + public void testApplyPFRules_add() throws ResourceUnavailableException { + NetworkVO networkVO = new NetworkVO(1L, Networks.TrafficType.Public, Networks.Mode.Static, + Networks.BroadcastDomainType.NSX, 12L, 2L, 5L, 1L, "network1", + "network1", null, Network.GuestType.Isolated, 2L, 2L, + ControlledEntity.ACLType.Domain, false, 1L, false ); + PortForwardingRuleVO rule = new PortForwardingRuleVO("1", 11L, 80, 90, new Ip("172.30.10.11"), 8080, 8090, "tcp", 12L, + 5L, 2L, 15L); + rule.setState(FirewallRule.State.Add); + Network.Service service = new Network.Service("service1", new Network.Capability("capability")); + + when(nsxElement.canHandle(networkVO, service)).thenReturn(true); + assertTrue(nsxElement.applyPFRules(networkVO, List.of(rule))); + } + + @Test + public void testApplyPFRules_delete() throws ResourceUnavailableException { + NetworkVO networkVO = new NetworkVO(1L, Networks.TrafficType.Public, Networks.Mode.Static, + Networks.BroadcastDomainType.NSX, 12L, 2L, 5L, 1L, "network1", + "network1", null, Network.GuestType.Isolated, 2L, 2L, + ControlledEntity.ACLType.Domain, false, 1L, false ); + PortForwardingRuleVO rule = new PortForwardingRuleVO("1", 11L, 80, 90, new Ip("172.30.10.11"), 8080, 8090, "tcp", 12L, + 5L, 2L, 15L); + rule.setState(FirewallRule.State.Revoke); + Network.Service service = new Network.Service("service1", new Network.Capability("capability")); + VpcVO vpcVO = Mockito.mock(VpcVO.class); + when(vpcDao.findById(1L)).thenReturn(vpcVO); + when(vpcVO.getDomainId()).thenReturn(2L); + IPAddressVO ipAddress = new IPAddressVO(new Ip("10.1.13.10"), 1L, 1L, 1L,false); + when(ApiDBUtils.findIpAddressById(anyLong())).thenReturn(ipAddress); + when(nsxElement.canHandle(networkVO, service)).thenReturn(true); + when(nsxService.deletePortForwardRule(any(NsxNetworkRule.class))).thenReturn(true); + assertTrue(nsxElement.applyPFRules(networkVO, List.of(rule))); + } + + @Test + public void testGetVpcOrNetworkReturnsVpcIfVpcIdPresent() { + VpcVO vpc = new VpcVO(); + when(vpcDao.findById(anyLong())).thenReturn(vpc); + + Pair vpcNetworkPair = nsxElement.getVpcOrNetwork(1L, 1L); + assertNotNull(vpcNetworkPair.first()); + assertNull(vpcNetworkPair.second()); + } + + @Test + public void testGetVpcOrNetworkReturnsNetworkIfVpcIdNotPresent() { + NetworkVO network = new NetworkVO(); + when(networkDao.findById(anyLong())).thenReturn(network); + + Pair vpcNetworkPair = nsxElement.getVpcOrNetwork(null, 1L); + assertNull(vpcNetworkPair.first()); + assertNotNull(vpcNetworkPair.second()); + } + + private Method getPublicPortRangeMethod() throws NoSuchMethodException { + Method method = NsxElement.class.getDeclaredMethod("getPublicPortRange", PortForwardingRule.class); + method.setAccessible(true); + return method; + } + + private Method getPrivatePFPortRangeMethod() throws NoSuchMethodException { + Method method = NsxElement.class.getDeclaredMethod("getPrivatePFPortRange", PortForwardingRule.class); + method.setAccessible(true); + return method; + } + + private Method getPrivatePortRangeMethod() throws NoSuchMethodException { + Method method = NsxElement.class.getDeclaredMethod("getPrivatePortRange", FirewallRule.class); + method.setAccessible(true); + return method; + } + + private Method getPrivatePortRangeForACLRuleMethod() throws NoSuchMethodException { + Method method = NsxElement.class.getDeclaredMethod("getPrivatePortRangeForACLRule", NetworkACLItem.class); + method.setAccessible(true); + return method; + } + + @Test + public void testGetPublicPortRangeWhenStartAndEndPortNumbersAreDifferent() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + PortForwardingRule rule = new PortForwardingRuleVO("1", 11L, 80, 90, new Ip("172.30.10.11"), 8080, 8090, "tcp", 12L, + 5L, 2L, 15L); + assertEquals("80-90", getPublicPortRangeMethod().invoke(null, rule)); + } + + @Test + public void testGetPublicPortRangeWhenStartAndEndPortNumbersAreSame() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + PortForwardingRule rule = new PortForwardingRuleVO("1", 11L, 80, 80, new Ip("172.30.10.11"), 8080, 8080, "tcp", 12L, + 5L, 2L, 15L); + assertEquals("80", getPublicPortRangeMethod().invoke(null, rule)); + } + + @Test + public void testGetPrivatePFPortRangeWhenStartAndEndPortNumbersAreDifferent() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + PortForwardingRule rule = new PortForwardingRuleVO("1", 11L, 80, 90, new Ip("172.30.10.11"), 8080, 8090, "tcp", 12L, + 5L, 2L, 15L); + assertEquals("8080-8090", getPrivatePFPortRangeMethod().invoke(null, rule)); + } + + @Test + public void testGetPrivatePFPortRangeWhenStartAndEndPortNumbersAreSame() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + PortForwardingRule rule = new PortForwardingRuleVO("1", 11L, 80, 80, new Ip("172.30.10.11"), 8080, 8080, "tcp", 12L, + 5L, 2L, 15L); + assertEquals("8080", getPrivatePFPortRangeMethod().invoke(null, rule)); + } + + @Test + public void testGetPrivatePortRangeWhenStartAndEndPortNumbersAreSame() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + FirewallRuleVO rule = new FirewallRuleVO("1", 11L, 80, 80, "tcp", 23L, 5L, 2L, + FirewallRule.Purpose.Firewall, List.of("172.30.10.0/24"), null, null, null, null, FirewallRule.TrafficType.Egress, FirewallRule.FirewallRuleType.User); + assertEquals("80", getPrivatePortRangeMethod().invoke(null, rule)); + } + + @Test + public void testGetPrivatePortRangeWhenStartAndEndPortNumbersAreDifferent() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + FirewallRuleVO rule = new FirewallRuleVO("1", 11L, 80, 90, "tcp", 23L, 5L, 2L, + FirewallRule.Purpose.Firewall, List.of("172.30.10.0/24"), null, null, null, null, FirewallRule.TrafficType.Egress, FirewallRule.FirewallRuleType.User); + assertEquals("80-90", getPrivatePortRangeMethod().invoke(null, rule)); + } + + @Test + public void testGetPrivatePortRangeForACLWhenStartAndEndPortNumbersAreSame() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + NetworkACLItem rule = new NetworkACLItemVO(80, 80, "udp", 10L, List.of("172.30.10.0/24"), null, null, NetworkACLItem.TrafficType.Ingress, NetworkACLItem.Action.Allow, + 2, null); + assertEquals("80", getPrivatePortRangeForACLRuleMethod().invoke(null, rule)); + } + + @Test + public void testGetPrivatePortRangeForACLWhenStartAndEndPortNumbersAreDifferent() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + NetworkACLItem rule = new NetworkACLItemVO(80, 90, "udp", 10L, List.of("172.30.10.0/24"), null, null, NetworkACLItem.TrafficType.Ingress, NetworkACLItem.Action.Allow, + 2, null); + assertEquals("80-90", getPrivatePortRangeForACLRuleMethod().invoke(null, rule)); + } + + @Test + public void testApplyLBRules_add() throws ResourceUnavailableException { + NetworkVO networkVO = new NetworkVO(1L, Networks.TrafficType.Public, Networks.Mode.Static, + Networks.BroadcastDomainType.NSX, 12L, 2L, 5L, 1L, "network1", + "network1", null, Network.GuestType.Isolated, 2L, 2L, + ControlledEntity.ACLType.Domain, false, 1L, false ); + LoadBalancerVO lb = new LoadBalancerVO(null, null, null, 0L, 8080, 8081, null, 0L, 0L, 1L, null, null); + lb.setState(FirewallRule.State.Add); + LoadBalancingRule.LbDestination destination = new LoadBalancingRule.LbDestination(6443, 6443, "172.30.110.11", false); + LoadBalancingRule rule = new LoadBalancingRule(lb, List.of(destination), null, null, new Ip("10.1.13.10"), null, "TCP"); + + VpcVO vpc = Mockito.mock(VpcVO.class); + + IPAddressVO ipAddress = new IPAddressVO(new Ip("10.1.13.10"), 1L, 1L, 1L,false); + when(vpcDao.findById(anyLong())).thenReturn(vpc); + when(vpc.getDomainId()).thenReturn(2L); + when(vpc.getAccountId()).thenReturn(5L); + when(ipAddressDao.findByIpAndDcId(anyLong(), anyString())).thenReturn(ipAddress); + when(nsxService.createLbRule(any(NsxNetworkRule.class))).thenReturn(true); + + assertTrue(nsxElement.applyLBRules(networkVO, List.of(rule))); + } + + @Test + public void testApplyLBRules_delete() throws ResourceUnavailableException { + NetworkVO networkVO = new NetworkVO(1L, Networks.TrafficType.Public, Networks.Mode.Static, + Networks.BroadcastDomainType.NSX, 12L, 2L, 5L, 1L, "network1", + "network1", null, Network.GuestType.Isolated, 2L, 2L, + ControlledEntity.ACLType.Domain, false, 1L, false ); + LoadBalancerVO lb = new LoadBalancerVO(null, null, null, 0L, 8080, 8081, null, 0L, 0L, 1L, null, null); + lb.setState(FirewallRule.State.Revoke); + LoadBalancingRule.LbDestination destination = new LoadBalancingRule.LbDestination(6443, 6443, "172.30.110.11", false); + LoadBalancingRule rule = new LoadBalancingRule(lb, List.of(destination), null, null, new Ip("10.1.13.10"), null, "TCP"); + + VpcVO vpc = Mockito.mock(VpcVO.class); + + IPAddressVO ipAddress = new IPAddressVO(new Ip("10.1.13.10"), 1L, 1L, 1L,false); + when(vpcDao.findById(anyLong())).thenReturn(vpc); + when(vpc.getDomainId()).thenReturn(2L); + when(vpc.getAccountId()).thenReturn(5L); + when(ipAddressDao.findByIpAndDcId(anyLong(), anyString())).thenReturn(ipAddress); + when(nsxService.deleteLbRule(any(NsxNetworkRule.class))).thenReturn(true); + + assertTrue(nsxElement.applyLBRules(networkVO, List.of(rule))); + } + + @Test + public void testApplyNetworkAclRules() throws ResourceUnavailableException { + NetworkVO networkVO = new NetworkVO(1L, Networks.TrafficType.Public, Networks.Mode.Static, + Networks.BroadcastDomainType.NSX, 12L, 2L, 5L, 1L, "network1", + "network1", null, Network.GuestType.Isolated, 2L, 2L, + ControlledEntity.ACLType.Domain, false, 1L, false ); + NetworkACLItem rule = new NetworkACLItemVO(80, 80, "udp", 10L, List.of("172.30.10.0/24"), null, null, NetworkACLItem.TrafficType.Ingress, NetworkACLItem.Action.Allow, + 2, null); + Network.Service service = new Network.Service("service1", new Network.Capability("capability")); + + when(nsxElement.canHandle(networkVO, service)).thenReturn(true); + assertTrue(nsxElement.applyNetworkACLs(networkVO, List.of(rule))); + } + + @Test + public void testDeleteNetworkAclRules() throws ResourceUnavailableException { + NetworkVO networkVO = new NetworkVO(1L, Networks.TrafficType.Public, Networks.Mode.Static, + Networks.BroadcastDomainType.NSX, 12L, 2L, 5L, 1L, "network1", + "network1", null, Network.GuestType.Isolated, 2L, 2L, + ControlledEntity.ACLType.Domain, false, 1L, false ); + NetworkACLItemVO rule = new NetworkACLItemVO(80, 80, "udp", 10L, List.of("172.30.10.0/24"), null, null, NetworkACLItem.TrafficType.Ingress, NetworkACLItem.Action.Allow, + 2, null); + rule.setState(NetworkACLItem.State.Revoke); + Network.Service service = new Network.Service("service1", new Network.Capability("capability")); + + when(nsxElement.canHandle(networkVO, service)).thenReturn(true); + when(nsxService.deleteFirewallRules(any(Network.class), any(List.class))).thenReturn(true); + assertTrue(nsxElement.applyNetworkACLs(networkVO, List.of(rule))); + } + + @Test + public void testApplyFirewallRules() throws ResourceUnavailableException { + NetworkVO networkVO = new NetworkVO(1L, Networks.TrafficType.Public, Networks.Mode.Static, + Networks.BroadcastDomainType.NSX, 12L, 2L, 5L, 1L, "network1", + "network1", null, Network.GuestType.Isolated, 2L, 2L, + ControlledEntity.ACLType.Domain, false, 1L, false ); + FirewallRuleVO rule = new FirewallRuleVO("1", 11L, 80, 80, "tcp", 23L, 5L, 2L, + FirewallRule.Purpose.Firewall, List.of("172.30.10.0/24"), null, null, null, null, FirewallRule.TrafficType.Egress, FirewallRule.FirewallRuleType.User); + Network.Service service = new Network.Service("service1", new Network.Capability("capability")); + + when(nsxElement.canHandle(networkVO, service)).thenReturn(true); + when(nsxService.addFirewallRules(any(Network.class), any(List.class))).thenReturn(true); + assertTrue(nsxElement.applyFWRules(networkVO, List.of(rule))); + } + + @Test + public void testRevokeFirewallRules() throws ResourceUnavailableException { + NetworkVO networkVO = new NetworkVO(1L, Networks.TrafficType.Public, Networks.Mode.Static, + Networks.BroadcastDomainType.NSX, 12L, 2L, 5L, 1L, "network1", + "network1", null, Network.GuestType.Isolated, 2L, 2L, + ControlledEntity.ACLType.Domain, false, 1L, false ); + FirewallRuleVO rule = new FirewallRuleVO("1", 11L, 80, 80, "tcp", 23L, 5L, 2L, + FirewallRule.Purpose.Firewall, List.of("172.30.10.0/24"), null, null, null, null, FirewallRule.TrafficType.Egress, FirewallRule.FirewallRuleType.User); + rule.setState(FirewallRule.State.Revoke); + Network.Service service = new Network.Service("service1", new Network.Capability("capability")); + + when(nsxElement.canHandle(networkVO, service)).thenReturn(true); + when(nsxService.deleteFirewallRules(any(Network.class), any(List.class))).thenReturn(true); + when(nsxService.addFirewallRules(any(Network.class), any(List.class))).thenReturn(true); + assertTrue(nsxElement.applyFWRules(networkVO, List.of(rule))); + } +} diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxGuestNetworkGuruTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxGuestNetworkGuruTest.java new file mode 100644 index 000000000000..66b9684203bd --- /dev/null +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxGuestNetworkGuruTest.java @@ -0,0 +1,329 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.deploy.DeployDestination; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.domain.DomainVO; +import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapacityException; +import com.cloud.network.IpAddressManager; +import com.cloud.network.Network; +import com.cloud.network.NetworkModel; +import com.cloud.network.Networks; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.guru.GuestNetworkGuru; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +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.AccountVO; +import com.cloud.user.dao.AccountDao; +import com.cloud.utils.Pair; +import com.cloud.vm.NicProfile; +import com.cloud.vm.ReservationContext; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.CreateNsxDhcpRelayConfigCommand; +import org.apache.cloudstack.agent.api.CreateNsxSegmentCommand; +import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.NsxCommand; +import org.apache.cloudstack.utils.NsxControllerUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.lenient; + +@RunWith(MockitoJUnitRunner.class) +public class NsxGuestNetworkGuruTest { + + @Mock + PhysicalNetworkDao physicalNetworkDao; + @Mock + DataCenterDao dcDao; + @Mock + VpcDao vpcDao; + @Mock + NetworkOfferingServiceMapDao networkOfferingServiceMapDao; + @Mock + NsxControllerUtils nsxControllerUtils; + @Mock + AccountDao accountDao; + @Mock + PhysicalNetworkVO physicalNetwork; + @Mock + DataCenterVO dataCenterVO; + @Mock + NetworkOffering offering; + @Mock + DeploymentPlan plan; + @Mock + Network network; + @Mock + Account account; + @Mock + VpcVO vpcVO; + @Mock + NetworkModel networkModel; + @Mock + DomainDao domainDao; + @Mock + NetworkDao networkDao; + @Mock + IpAddressManager ipAddressManager; + @Mock + NetworkOfferingDao networkOfferingDao; + + NsxGuestNetworkGuru guru; + AutoCloseable closeable; + + @Before + public void setUp() throws IllegalAccessException, NoSuchFieldException { + closeable = MockitoAnnotations.openMocks(this); + guru = new NsxGuestNetworkGuru(); + + ReflectionTestUtils.setField(guru, "_dcDao", dcDao); + ReflectionTestUtils.setField(guru, "_networkDao", networkDao); + ReflectionTestUtils.setField(guru, "_networkModel", networkModel); + ReflectionTestUtils.setField(guru, "_vpcDao", vpcDao); + ReflectionTestUtils.setField((GuestNetworkGuru) guru, "_ipAddrMgr", ipAddressManager); + ReflectionTestUtils.setField((GuestNetworkGuru) guru, "_networkModel", networkModel); + ReflectionTestUtils.setField((GuestNetworkGuru) guru, "networkOfferingDao", networkOfferingDao); + ReflectionTestUtils.setField((GuestNetworkGuru) guru, "_physicalNetworkDao", physicalNetworkDao); + + guru.networkOfferingServiceMapDao = networkOfferingServiceMapDao; + guru.nsxControllerUtils = nsxControllerUtils; + guru.accountDao = accountDao; + guru.domainDao = domainDao; + + Mockito.when(dataCenterVO.getNetworkType()).thenReturn(DataCenter.NetworkType.Advanced); + + when(physicalNetwork.getIsolationMethods()).thenReturn(List.of("NSX")); + + when(offering.getTrafficType()).thenReturn(Networks.TrafficType.Guest); + when(offering.getGuestType()).thenReturn(Network.GuestType.Isolated); + when(offering.getNsxMode()).thenReturn(NetworkOffering.NsxMode.NATTED.name()); + when(offering.getId()).thenReturn(1L); + + when(plan.getDataCenterId()).thenReturn(1L); + when(plan.getPhysicalNetworkId()).thenReturn(1L); + + when(vpcDao.findById(anyLong())).thenReturn(vpcVO); + + when(vpcVO.getName()).thenReturn("VPC01"); + + when(account.getAccountId()).thenReturn(1L); + when(accountDao.findById(anyLong())).thenReturn(mock(AccountVO.class)); + when(domainDao.findById(anyLong())).thenReturn(mock(DomainVO.class)); + + Mockito.when(networkOfferingServiceMapDao.isProviderForNetworkOffering(offering.getId(), Network.Provider.Nsx)).thenReturn( + true); + } + + @After + public void tearDown() throws Exception { + closeable.close(); + } + + @Test + public void testIsMyIsolationMethod() { + assertTrue(guru.isMyIsolationMethod(physicalNetwork)); + } + + @Test + public void testCanHandle() { + assertTrue(guru.canHandle(offering, dataCenterVO.getNetworkType(), physicalNetwork)); + } + + @Test + public void testNsxNetworkDesign() { + when(physicalNetworkDao.findById(ArgumentMatchers.anyLong())).thenReturn(physicalNetwork); + when(dcDao.findById(ArgumentMatchers.anyLong())).thenReturn(dataCenterVO); + + Network designedNetwork = guru.design(offering, plan, network, "", 1L, account); + assertNotNull(designedNetwork); + assertSame(Networks.BroadcastDomainType.NSX, designedNetwork.getBroadcastDomainType()); + assertSame(Network.State.Allocated, designedNetwork.getState()); + } + + @Test + public void testNsxNetworkSetup() { + when(dcDao.findById(ArgumentMatchers.anyLong())).thenReturn(dataCenterVO); + when(networkDao.findById(ArgumentMatchers.anyLong())).thenReturn(mock(NetworkVO.class)); + when(nsxControllerUtils.sendNsxCommand(any(CreateNsxSegmentCommand.class), anyLong())).thenReturn( + new NsxAnswer(new NsxCommand(), true, "")); + + guru.setup(network, 1L); + verify(nsxControllerUtils, times(1)).sendNsxCommand(any(CreateNsxSegmentCommand.class), anyLong()); + } + + @Test + public void testNsxNetworkImplementation() { + final DeployDestination deployDestination = mock(DeployDestination.class); + final ReservationContext reservationContext = mock(ReservationContext.class); + + when(network.getTrafficType()).thenReturn(Networks.TrafficType.Guest); + when(network.getMode()).thenReturn(Networks.Mode.Dhcp); + when(network.getGateway()).thenReturn("192.168.1.1"); + when(network.getCidr()).thenReturn("192.168.1.0/24"); + when(network.getBroadcastDomainType()).thenReturn(Networks.BroadcastDomainType.NSX); + when(network.getNetworkOfferingId()).thenReturn(1L); + lenient().when(network.getState()).thenReturn(Network.State.Implementing); + when(network.getDataCenterId()).thenReturn(2L); + when(network.getPhysicalNetworkId()).thenReturn(3L); + when(network.getVpcId()).thenReturn(4L); + when(offering.isRedundantRouter()).thenReturn(false); + lenient().when(offering.getGuestType()).thenReturn(Network.GuestType.Isolated); + + + final Network implemented = guru.implement(network, offering, deployDestination, reservationContext); + assertEquals(Networks.BroadcastDomainType.NSX.toUri("nsx"), implemented.getBroadcastUri()); + assertEquals("192.168.1.1", implemented.getGateway()); + assertEquals("192.168.1.0/24", implemented.getCidr()); + assertEquals(Networks.Mode.Dhcp, implemented.getMode()); + assertEquals(Networks.BroadcastDomainType.NSX, implemented.getBroadcastDomainType()); + assertEquals(1L, implemented.getNetworkOfferingId()); + assertEquals(Network.State.Implemented, implemented.getState()); + assertEquals(2L, implemented.getDataCenterId()); + assertEquals(3L, implemented.getPhysicalNetworkId().longValue()); + assertEquals(4L, implemented.getVpcId().longValue()); + assertFalse(implemented.isRedundant()); + } + + @Test + public void testAllocateForUserVM() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { + Network network = Mockito.mock(Network.class); + NicProfile nicProfile = Mockito.mock(NicProfile.class); + VirtualMachineProfile vmProfile = Mockito.mock(VirtualMachineProfile.class); + VirtualMachine virtualMachine = Mockito.mock(VirtualMachine.class); + Pair dns = new Pair<>("10.1.5.1", "8.8.8.8"); + String macAddress = "00:00:00:11:1D:1E:CD"; + + when(network.getTrafficType()).thenReturn(Networks.TrafficType.Guest); + when(vmProfile.getVirtualMachine()).thenReturn(virtualMachine); + when(virtualMachine.getType()).thenReturn(VirtualMachine.Type.User); + when(networkModel.getNetworkIp4Dns(any(Network.class), nullable(DataCenter.class))).thenReturn(dns); + when(nicProfile.getMacAddress()).thenReturn(macAddress); + when(networkOfferingDao.isIpv6Supported(anyLong())).thenReturn(false); + + NicProfile profile = guru.allocate(network, nicProfile, vmProfile); + assertNotNull(profile); + } + + @Test + public void testAllocateForDomainRouter() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { + Network network = Mockito.mock(Network.class); + NicProfile nicProfile = Mockito.mock(NicProfile.class); + VirtualMachineProfile vmProfile = Mockito.mock(VirtualMachineProfile.class); + VirtualMachine virtualMachine = Mockito.mock(VirtualMachine.class); + Pair dns = new Pair<>("10.1.5.1", "8.8.8.8"); + String macAddress = "00:00:00:11:1D:1E:CD"; + + when(network.getTrafficType()).thenReturn(Networks.TrafficType.Guest); + when(vmProfile.getType()).thenReturn(VirtualMachine.Type.DomainRouter); + when(vmProfile.getVirtualMachine()).thenReturn(virtualMachine); + when(virtualMachine.getType()).thenReturn(VirtualMachine.Type.DomainRouter); + when(network.getId()).thenReturn(2L); + when(nicProfile.getMacAddress()).thenReturn(macAddress); + when(networkOfferingDao.isIpv6Supported(anyLong())).thenReturn(false); + when(network.getDataCenterId()).thenReturn(1L); + when(network.getAccountId()).thenReturn(5L); + when(network.getVpcId()).thenReturn(51L); + when(dcDao.findById(anyLong())).thenReturn(Mockito.mock(DataCenterVO.class)); + when(accountDao.findById(anyLong())).thenReturn(Mockito.mock(AccountVO.class)); + when(vpcDao.findById(anyLong())).thenReturn(Mockito.mock(VpcVO.class)); + when(domainDao.findById(anyLong())).thenReturn(Mockito.mock(DomainVO.class)); + when(nicProfile.getIPv4Address()).thenReturn("10.1.13.10"); + when(nsxControllerUtils.sendNsxCommand(any(CreateNsxDhcpRelayConfigCommand.class), + anyLong())).thenReturn(new NsxAnswer(new NsxCommand(), true, "")); + + NicProfile profile = guru.allocate(network, nicProfile, vmProfile); + + assertNotNull(profile); + verify(nsxControllerUtils, times(1)).sendNsxCommand(any(CreateNsxDhcpRelayConfigCommand.class), + anyLong()); + } + + @Test + public void testCreateNsxSegmentForVpc() { + NetworkVO networkVO = Mockito.mock(NetworkVO.class); + DataCenter dataCenter = Mockito.mock(DataCenter.class); + + when(networkVO.getAccountId()).thenReturn(1L); + when(nsxControllerUtils.sendNsxCommand(any(CreateNsxSegmentCommand.class), + anyLong())).thenReturn(new NsxAnswer(new NsxCommand(), true, "")); + guru.createNsxSegment(networkVO, dataCenter); + verify(nsxControllerUtils, times(1)).sendNsxCommand(any(CreateNsxSegmentCommand.class), + anyLong()); + } + + + @Test + public void testCreateNsxSegmentForIsolatedNetwork() { + NetworkVO networkVO = Mockito.mock(NetworkVO.class); + NetworkOfferingVO offeringVO = Mockito.mock(NetworkOfferingVO.class); + DataCenter dataCenter = Mockito.mock(DataCenter.class); + + when(networkVO.getAccountId()).thenReturn(1L); + when(networkVO.getVpcId()).thenReturn(null); + when(nsxControllerUtils.sendNsxCommand(any(CreateNsxTier1GatewayCommand.class), + anyLong())).thenReturn(new NsxAnswer(new NsxCommand(), true, "")); + when(nsxControllerUtils.sendNsxCommand(any(CreateNsxSegmentCommand.class), + anyLong())).thenReturn(new NsxAnswer(new NsxCommand(), true, "")); + when(networkVO.getNetworkOfferingId()).thenReturn(1L); + when(networkOfferingDao.findById(1L)).thenReturn(offeringVO); + when(offeringVO.getNsxMode()).thenReturn(NetworkOffering.NsxMode.NATTED.name()); + guru.createNsxSegment(networkVO, dataCenter); + verify(nsxControllerUtils, times(1)).sendNsxCommand(any(CreateNsxTier1GatewayCommand.class), + anyLong()); + verify(nsxControllerUtils, times(1)).sendNsxCommand(any(CreateNsxSegmentCommand.class), + anyLong()); + } +} diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxProviderServiceImplTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxProviderServiceImplTest.java new file mode 100644 index 000000000000..cb6f6511d24d --- /dev/null +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxProviderServiceImplTest.java @@ -0,0 +1,174 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.host.Host; +import com.cloud.host.dao.HostDetailsDao; +import com.cloud.network.Network; +import com.cloud.network.Networks; +import com.cloud.network.nsx.NsxProvider; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.dao.PhysicalNetworkVO; +import com.cloud.network.element.NsxProviderVO; +import com.cloud.resource.ResourceManager; +import com.cloud.resource.ServerResource; +import com.cloud.utils.exception.CloudRuntimeException; +import org.apache.cloudstack.api.BaseResponse; +import org.apache.cloudstack.api.command.AddNsxControllerCmd; +import org.apache.cloudstack.api.response.NsxControllerResponse; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.UUID; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyMap; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class NsxProviderServiceImplTest { + @Mock + NsxProviderDao nsxProviderDao; + @Mock + DataCenterDao dataCenterDao; + @Mock + PhysicalNetworkDao physicalNetworkDao; + @Mock + NetworkDao networkDao; + @Mock + ResourceManager resourceManager; + @Mock + HostDetailsDao hostDetailsDao; + + NsxProviderServiceImpl nsxProviderService; + + @Before + public void setup() { + nsxProviderService = new NsxProviderServiceImpl(); + nsxProviderService.resourceManager = resourceManager; + nsxProviderService.nsxProviderDao = nsxProviderDao; + nsxProviderService.hostDetailsDao = hostDetailsDao; + nsxProviderService.dataCenterDao = dataCenterDao; + nsxProviderService.networkDao = networkDao; + nsxProviderService.physicalNetworkDao = physicalNetworkDao; + } + + @Test + public void testAddProvider() { + AddNsxControllerCmd cmd = mock(AddNsxControllerCmd.class); + when(cmd.getZoneId()).thenReturn(1L); + when(cmd.getName()).thenReturn("NsxController"); + when(cmd.getHostname()).thenReturn("192.168.0.100"); + when(cmd.getPort()).thenReturn("443"); + when(cmd.getUsername()).thenReturn("admin"); + when(cmd.getPassword()).thenReturn("password"); + when(cmd.getEdgeCluster()).thenReturn("EdgeCluster"); + when(cmd.getTier0Gateway()).thenReturn("Tier0-GW01"); + when(cmd.getTransportZone()).thenReturn("Overlay"); + when(resourceManager.addHost(anyLong(), any(ServerResource.class), any(Host.Type.class), anyMap())).thenReturn(mock(Host.class)); + try { + NsxProvider provider = nsxProviderService.addProvider(cmd); + Assert.assertNotNull(provider); + } catch (CloudRuntimeException e) { + e.printStackTrace(); + fail("Failed to add NSX controller due to internal error."); + } + } + + @Test + public void testCreateNsxControllerResponse() { + NsxProvider nsxProvider = mock(NsxProvider.class); + DataCenterVO zone = mock(DataCenterVO.class); + String uuid = UUID.randomUUID().toString(); + when(dataCenterDao.findById(anyLong())).thenReturn(zone); + when(zone.getUuid()).thenReturn(UUID.randomUUID().toString()); + when(zone.getName()).thenReturn("ZoneNSX"); + when(nsxProvider.getProviderName()).thenReturn("NSXController"); + when(nsxProvider.getUuid()).thenReturn(uuid); + when(nsxProvider.getHostname()).thenReturn("hostname"); + when(nsxProvider.getPort()).thenReturn("443"); + when(nsxProvider.getTier0Gateway()).thenReturn("Tier0Gw"); + when(nsxProvider.getEdgeCluster()).thenReturn("EdgeCluster"); + when(nsxProvider.getTransportZone()).thenReturn("Overlay"); + + NsxControllerResponse response = nsxProviderService.createNsxControllerResponse(nsxProvider); + + assertEquals("EdgeCluster", response.getEdgeCluster()); + assertEquals("Tier0Gw", response.getTier0Gateway()); + assertEquals("Overlay", response.getTransportZone()); + assertEquals("ZoneNSX", response.getZoneName()); + } + + @Test + public void testListNsxControllers() { + NsxProviderVO nsxProviderVO = Mockito.mock(NsxProviderVO.class); + + when(nsxProviderVO.getZoneId()).thenReturn(1L); + when(dataCenterDao.findById(1L)).thenReturn(mock(DataCenterVO.class)); + when(nsxProviderDao.findByZoneId(anyLong())).thenReturn(nsxProviderVO); + + List baseResponseList = nsxProviderService.listNsxProviders(1L); + assertEquals(1, baseResponseList.size()); + } + + @Test + public void testDeleteNsxController() { + NsxProviderVO nsxProviderVO = Mockito.mock(NsxProviderVO.class); + PhysicalNetworkVO physicalNetworkVO = mock(PhysicalNetworkVO.class); + List physicalNetworkVOList = List.of(physicalNetworkVO); + NetworkVO networkVO = mock(NetworkVO.class); + List networkVOList = List.of(networkVO); + + when(nsxProviderVO.getZoneId()).thenReturn(1L); + when(physicalNetworkVO.getId()).thenReturn(2L); + when(physicalNetworkDao.listByZone(1L)).thenReturn(physicalNetworkVOList); + when(nsxProviderDao.findById(anyLong())).thenReturn(nsxProviderVO); + when(networkDao.listByPhysicalNetwork(anyLong())).thenReturn(networkVOList); + + assertTrue(nsxProviderService.deleteNsxController(1L)); + } + + @Test + public void testNetworkStateValidation() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + NetworkVO networkVO = Mockito.mock(NetworkVO.class); + List networkVOList = List.of(networkVO); + when(networkVO.getBroadcastDomainType()).thenReturn(Networks.BroadcastDomainType.NSX); + when(networkVO.getState()).thenReturn(Network.State.Allocated); + + NsxProviderServiceImpl nsxProviderService = new NsxProviderServiceImpl(); + + assertThrows(CloudRuntimeException.class, () -> nsxProviderService.validateNetworkState(networkVOList)); + } +} diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java new file mode 100644 index 000000000000..da21bf11b641 --- /dev/null +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxPublicNetworkGuruTest.java @@ -0,0 +1,178 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.VlanDetailsDao; +import com.cloud.deploy.DeploymentPlan; +import com.cloud.exception.InsufficientAddressCapacityException; +import com.cloud.exception.InsufficientVirtualNetworkCapacityException; +import com.cloud.network.Network; +import com.cloud.network.Networks; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.guru.PublicNetworkGuru; +import com.cloud.network.vpc.VpcOfferingVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.network.vpc.dao.VpcOfferingDao; +import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao; +import com.cloud.offering.NetworkOffering; +import com.cloud.user.Account; +import com.cloud.utils.net.Ip; +import com.cloud.vm.NicProfile; +import com.cloud.vm.VirtualMachineProfile; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand; +import org.apache.cloudstack.agent.api.NsxCommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.utils.NsxControllerUtils; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; + +@RunWith(MockitoJUnitRunner.class) +public class NsxPublicNetworkGuruTest { + + NetworkOffering offering; + + NsxPublicNetworkGuru guru; + @Mock + NsxServiceImpl nsxService; + @Mock + IPAddressDao ipAddressDao; + @Mock + VpcDao vpcDao; + @Mock + VlanDetailsDao vlanDetailsDao; + @Mock + VpcOfferingServiceMapDao vpcOfferingServiceMapDao; + @Mock + VpcOfferingDao vpcOfferingDao; + @Mock + NsxControllerUtils nsxControllerUtils; + + @Before + public void setup() { + guru = new NsxPublicNetworkGuru(); + + ReflectionTestUtils.setField((PublicNetworkGuru) guru, "_ipAddressDao", ipAddressDao); + ReflectionTestUtils.setField(guru, "vpcDao", vpcDao); + ReflectionTestUtils.setField(guru, "vlanDetailsDao", vlanDetailsDao); + ReflectionTestUtils.setField(guru, "vpcOfferingServiceMapDao", vpcOfferingServiceMapDao); + ReflectionTestUtils.setField(guru, "nsxService", nsxService); + ReflectionTestUtils.setField(guru, "vpcOfferingDao", vpcOfferingDao); + ReflectionTestUtils.setField(guru, "nsxControllerUtils", nsxControllerUtils); + + offering = Mockito.mock(NetworkOffering.class); + when(offering.getTrafficType()).thenReturn(Networks.TrafficType.Public); + when(offering.isForNsx()).thenReturn(true); + when(offering.isSystemOnly()).thenReturn(true); + } + + @Test + public void testCanHandle() { + Assert.assertTrue(guru.canHandle(offering)); + } + + @Test + public void testCannotHandle() { + NetworkOffering offering = Mockito.mock(NetworkOffering.class); + + when(offering.getTrafficType()).thenReturn(Networks.TrafficType.Guest); + + Assert.assertFalse(guru.canHandle(offering)); + } + + @Test + public void testDesign() { + DeploymentPlan plan = Mockito.mock(DeploymentPlan.class); + Network network = Mockito.mock(Network.class); + Account account = Mockito.mock(Account.class); + +// when(network.getTrafficType()).thenReturn(Networks.TrafficType.Public); + + Network designedNetwork = guru.design(offering, plan, network, "net1", 1L, account); + Assert.assertEquals(Networks.TrafficType.Public, designedNetwork.getTrafficType()); + } + + @Test + public void testDesign_whenOfferingIsForGuestTraffic() { + DeploymentPlan plan = Mockito.mock(DeploymentPlan.class); + Network network = Mockito.mock(Network.class); + Account account = Mockito.mock(Account.class); + + when(offering.getTrafficType()).thenReturn(Networks.TrafficType.Guest); + + Network designedNetwork = guru.design(offering, plan, network, "net1", 1L, account); + Assert.assertNull(designedNetwork); + } + + @Test + public void testAllocate() throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { + String publicIpVR = "10.1.12.10"; + String publicIpNSX = "10.1.13.10"; + Network network = Mockito.mock(Network.class); + NicProfile profile = Mockito.mock(NicProfile.class); + VirtualMachineProfile vmProfile = Mockito.mock(VirtualMachineProfile.class); + IPAddressVO srcNatIpOnVR = new IPAddressVO(new Ip(publicIpVR), 2L , 0xaabbccddeeffL, 2L, true); + srcNatIpOnVR.setVpcId(12L); + IPAddressVO srcNatIpOnNSX = new IPAddressVO(new Ip(publicIpNSX), 2L , 0xaabbccddeeffL, 3L, true); + srcNatIpOnNSX.setVpcId(12L); + VpcVO vpcVO = Mockito.mock(VpcVO.class); + List sourceNatList = List.of(srcNatIpOnNSX); + VlanDetailsVO vlanDetailVO = new VlanDetailsVO(3L,ApiConstants.NSX_DETAIL_KEY, "true", false); + VpcOfferingVO vpcOffering = Mockito.mock(VpcOfferingVO.class); + + + when(profile.getIPv4Address()).thenReturn(publicIpVR); + when(ipAddressDao.findByIp(anyString())).thenReturn(srcNatIpOnVR); + when(vpcDao.findById(anyLong())).thenReturn(vpcVO); + when(ipAddressDao.listByAssociatedVpc(12L, true)).thenReturn(sourceNatList); + when(vlanDetailsDao.findDetail(anyLong(), anyString())).thenReturn(vlanDetailVO); + when(vpcVO.getVpcOfferingId()).thenReturn(12L); + when(vpcVO.getId()).thenReturn(12L); + when(vpcVO.getName()).thenReturn("nsxVPCNet"); + when(vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(anyLong(), any())).thenReturn(true); + when(nsxService.createVpcNetwork(anyLong(), anyLong(), anyLong(), anyLong(), anyString(), anyBoolean())).thenReturn(true); + when(vpcOfferingDao.findById(anyLong())).thenReturn(vpcOffering); + when(vpcOffering.getNsxMode()).thenReturn(NetworkOffering.NsxMode.NATTED.name()); + when(nsxControllerUtils.sendNsxCommand(any(CreateOrUpdateNsxTier1NatRuleCommand.class), + anyLong())).thenReturn(new NsxAnswer(new NsxCommand(), true, "")); + + guru.allocate(network, profile, vmProfile); + + verify(nsxControllerUtils, times(1)).sendNsxCommand(any(CreateOrUpdateNsxTier1NatRuleCommand.class), + anyLong()); + + } +} diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxServiceImplTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxServiceImplTest.java new file mode 100644 index 000000000000..41f47bc610e5 --- /dev/null +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/service/NsxServiceImplTest.java @@ -0,0 +1,162 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.service; + +import com.cloud.network.IpAddress; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.utils.net.Ip; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.CreateNsxStaticNatCommand; +import org.apache.cloudstack.agent.api.CreateNsxTier1GatewayCommand; +import org.apache.cloudstack.agent.api.CreateOrUpdateNsxTier1NatRuleCommand; +import org.apache.cloudstack.agent.api.DeleteNsxNatRuleCommand; +import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand; +import org.apache.cloudstack.agent.api.DeleteNsxTier1GatewayCommand; +import org.apache.cloudstack.utils.NsxControllerUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class NsxServiceImplTest { + @Mock + private NsxControllerUtils nsxControllerUtils; + @Mock + private VpcDao vpcDao; + NsxServiceImpl nsxService; + + AutoCloseable closeable; + + private static final long domainId = 1L; + private static final long accountId = 2L; + private static final long zoneId = 1L; + + @Before + public void setup() { + closeable = MockitoAnnotations.openMocks(this); + nsxService = new NsxServiceImpl(); + nsxService.nsxControllerUtils = nsxControllerUtils; + nsxService.vpcDao = vpcDao; + } + + @After + public void teardown() throws Exception { + closeable.close(); + } + + @Test + public void testCreateVpcNetwork() { + NsxAnswer createNsxTier1GatewayAnswer = mock(NsxAnswer.class); + when(nsxControllerUtils.sendNsxCommand(any(CreateNsxTier1GatewayCommand.class), anyLong())).thenReturn(createNsxTier1GatewayAnswer); + when(createNsxTier1GatewayAnswer.getResult()).thenReturn(true); + + assertTrue(nsxService.createVpcNetwork(1L, 3L, 2L, 5L, "VPC01", false)); + } + + @Test + public void testDeleteVpcNetwork() { + NsxAnswer deleteNsxTier1GatewayAnswer = mock(NsxAnswer.class); + when(nsxControllerUtils.sendNsxCommand(any(DeleteNsxTier1GatewayCommand.class), anyLong())).thenReturn(deleteNsxTier1GatewayAnswer); + when(deleteNsxTier1GatewayAnswer.getResult()).thenReturn(true); + + assertTrue(nsxService.deleteVpcNetwork(1L, 2L, 3L, 10L, "VPC01")); + } + + @Test + public void testDeleteNetworkOnVpc() { + NetworkVO network = new NetworkVO(); + network.setVpcId(1L); + when(vpcDao.findById(1L)).thenReturn(mock(VpcVO.class)); + NsxAnswer deleteNsxSegmentAnswer = mock(NsxAnswer.class); + when(nsxControllerUtils.sendNsxCommand(any(DeleteNsxSegmentCommand.class), anyLong())).thenReturn(deleteNsxSegmentAnswer); + when(deleteNsxSegmentAnswer.getResult()).thenReturn(true); + + assertTrue(nsxService.deleteNetwork(zoneId, accountId, domainId, network)); + } + + @Test + public void testDeleteNetwork() { + NetworkVO network = new NetworkVO(); + network.setVpcId(null); + NsxAnswer deleteNsxSegmentAnswer = mock(NsxAnswer.class); + when(deleteNsxSegmentAnswer.getResult()).thenReturn(true); + when(nsxControllerUtils.sendNsxCommand(any(DeleteNsxSegmentCommand.class), anyLong())).thenReturn(deleteNsxSegmentAnswer); + NsxAnswer deleteNsxTier1GatewayAnswer = mock(NsxAnswer.class); + when(deleteNsxTier1GatewayAnswer.getResult()).thenReturn(true); + when(nsxControllerUtils.sendNsxCommand(any(DeleteNsxTier1GatewayCommand.class), anyLong())).thenReturn(deleteNsxTier1GatewayAnswer); + assertTrue(nsxService.deleteNetwork(zoneId, accountId, domainId, network)); + } + + @Test + public void testUpdateVpcSourceNatIp() { + VpcVO vpc = mock(VpcVO.class); + IpAddress ipAddress = mock(IpAddress.class); + Ip ip = Mockito.mock(Ip.class); + when(ip.addr()).thenReturn("10.1.10.10"); + when(ipAddress.getAddress()).thenReturn(ip); + long vpcId = 1L; + when(vpc.getAccountId()).thenReturn(accountId); + when(vpc.getDomainId()).thenReturn(domainId); + when(vpc.getZoneId()).thenReturn(zoneId); + when(vpc.getId()).thenReturn(vpcId); + NsxAnswer answer = mock(NsxAnswer.class); + when(answer.getResult()).thenReturn(true); + when(nsxControllerUtils.sendNsxCommand(any(CreateOrUpdateNsxTier1NatRuleCommand.class), eq(zoneId))).thenReturn(answer); + nsxService.updateVpcSourceNatIp(vpc, ipAddress); + Mockito.verify(nsxControllerUtils).sendNsxCommand(any(CreateOrUpdateNsxTier1NatRuleCommand.class), eq(zoneId)); + } + + @Test + public void testCreateStaticNatRule() { + long networkId = 1L; + String networkName = "Network-Test"; + long vmId = 1L; + String publicIp = "10.10.1.10"; + String vmIp = "192.168.1.20"; + NsxAnswer answer = Mockito.mock(NsxAnswer.class); + when(answer.getResult()).thenReturn(true); + when(nsxControllerUtils.sendNsxCommand(any(CreateNsxStaticNatCommand.class), eq(zoneId))).thenReturn(answer); + nsxService.createStaticNatRule(zoneId, domainId, accountId, + networkId, networkName, true, vmId, publicIp, vmIp); + Mockito.verify(nsxControllerUtils).sendNsxCommand(any(CreateNsxStaticNatCommand.class), eq(zoneId)); + } + + @Test + public void testDeleteStaticNatRule() { + long networkId = 1L; + String networkName = "Network-Test"; + NsxAnswer answer = Mockito.mock(NsxAnswer.class); + when(answer.getResult()).thenReturn(true); + when(nsxControllerUtils.sendNsxCommand(any(DeleteNsxNatRuleCommand.class), eq(zoneId))).thenReturn(answer); + nsxService.deleteStaticNatRule(zoneId, domainId, accountId, networkId, networkName, true); + Mockito.verify(nsxControllerUtils).sendNsxCommand(any(DeleteNsxNatRuleCommand.class), eq(zoneId)); + } +} diff --git a/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/utils/NsxControllerUtilsTest.java b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/utils/NsxControllerUtilsTest.java new file mode 100644 index 000000000000..9139fdef68f7 --- /dev/null +++ b/plugins/network-elements/nsx/src/test/java/org/apache/cloudstack/utils/NsxControllerUtilsTest.java @@ -0,0 +1,198 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +package org.apache.cloudstack.utils; + +import com.cloud.agent.AgentManager; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.element.NsxProviderVO; +import org.apache.cloudstack.NsxAnswer; +import org.apache.cloudstack.agent.api.NsxCommand; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.Spy; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class NsxControllerUtilsTest { + + private static final long domainId = 2L; + private static final long accountId = 10L; + private static final long zoneId = 1L; + private static final long nsxProviderHostId = 1L; + + private static final String commonPrefix = String.format("D%s-A%s-Z%s", domainId, accountId, zoneId); + + @Mock + private NsxProviderDao nsxProviderDao; + @Mock + private AgentManager agentMgr; + + @Spy + @InjectMocks + private NsxControllerUtils nsxControllerUtils = new NsxControllerUtils(); + + @Mock + private NsxProviderVO nsxProviderVO; + + @Before + public void setup() { + Mockito.when(nsxProviderDao.findByZoneId(zoneId)).thenReturn(nsxProviderVO); + Mockito.when(nsxProviderVO.getHostId()).thenReturn(nsxProviderHostId); + } + + @Test(expected = InvalidParameterValueException.class) + public void testSendCommandAnswerFailure() { + NsxCommand cmd = Mockito.mock(NsxCommand.class); + Mockito.when(nsxProviderDao.findByZoneId(zoneId)).thenReturn(null); + nsxControllerUtils.sendNsxCommand(cmd, zoneId); + } + + @Test(expected = InvalidParameterValueException.class) + public void testSendCommandNoNsxProvider() { + NsxCommand cmd = Mockito.mock(NsxCommand.class); + Mockito.when(agentMgr.easySend(nsxProviderHostId, cmd)).thenReturn(null); + nsxControllerUtils.sendNsxCommand(cmd, zoneId); + } + + @Test + public void testSendCommand() { + NsxCommand cmd = Mockito.mock(NsxCommand.class); + NsxAnswer answer = Mockito.mock(NsxAnswer.class); + Mockito.when(answer.getResult()).thenReturn(true); + Mockito.when(agentMgr.easySend(nsxProviderHostId, cmd)).thenReturn(answer); + NsxAnswer nsxAnswer = nsxControllerUtils.sendNsxCommand(cmd, zoneId); + Assert.assertNotNull(nsxAnswer); + } + + @Test + public void testGetNsxNatRuleIdForVpc() { + long vpcId = 5L; + String nsxNatRuleId = NsxControllerUtils.getNsxNatRuleId(domainId, accountId, zoneId, vpcId, true); + String ruleIdPart = String.format("V%s-NAT", vpcId); + String expected = String.format("%s-%s", commonPrefix, ruleIdPart); + Assert.assertEquals(expected, nsxNatRuleId); + } + + @Test + public void testGetNsxNatRuleIdForNetwork() { + long networkId = 5L; + String nsxNatRuleId = NsxControllerUtils.getNsxNatRuleId(domainId, accountId, zoneId, networkId, false); + String ruleIdPart = String.format("N%s-NAT", networkId); + String expected = String.format("%s-%s", commonPrefix, ruleIdPart); + Assert.assertEquals(expected, nsxNatRuleId); + } + + @Test + public void testGetNsxSegmentIdForVpcNetwork() { + long vpcId = 5L; + long networkId = 2L; + String nsxSegmentName = NsxControllerUtils.getNsxSegmentId(domainId, accountId, zoneId, vpcId, networkId); + String segmentPart = String.format("V%s-S%s", vpcId, networkId); + String expected = String.format("%s-%s", commonPrefix, segmentPart); + Assert.assertEquals(expected, nsxSegmentName); + } + + @Test + public void testGetNsxSegmentIdForNonVpcNetwork() { + Long vpcId = null; + long networkId = 2L; + String nsxSegmentName = NsxControllerUtils.getNsxSegmentId(domainId, accountId, zoneId, vpcId, networkId); + String segmentPart = String.format("S%s", networkId); + String expected = String.format("%s-%s", commonPrefix, segmentPart); + Assert.assertEquals(expected, nsxSegmentName); + } + + @Test + public void testGetNsxDistributedFirewallPolicyRuleIdForVpcNetwork() { + long vpcId = 5L; + long networkId = 2L; + long ruleId = 1L; + String nsxSegmentName = NsxControllerUtils.getNsxSegmentId(domainId, accountId, zoneId, vpcId, networkId); + String expected = String.format("%s-R%s", nsxSegmentName, ruleId); + Assert.assertEquals(expected, NsxControllerUtils.getNsxDistributedFirewallPolicyRuleId(nsxSegmentName, ruleId)); + } + + @Test + public void testGetTier1GatewayNameForVpcNetwork() { + long networkOnVpcId = 5L; + String networkPart = String.format("V%s", networkOnVpcId); + String expected = String.format("%s-%s", commonPrefix, networkPart); + Assert.assertEquals(expected, NsxControllerUtils.getTier1GatewayName(domainId, accountId, zoneId, networkOnVpcId, true)); + } + + @Test + public void testGetTier1GatewayNameForNetwork() { + long networkId = 5L; + String networkPart = String.format("N%s", networkId); + String expected = String.format("%s-%s", commonPrefix, networkPart); + Assert.assertEquals(expected, NsxControllerUtils.getTier1GatewayName(domainId, accountId, zoneId, networkId, false)); + } + + @Test + public void testGetNsxDhcpRelayConfigIdForVpcNetwork() { + long vpcId = 5L; + long networkId = 2L; + String relayPart = String.format("V%s-S%s-Relay", vpcId, networkId); + String expected = String.format("%s-%s", commonPrefix, relayPart); + String dhcpRelayConfigId = NsxControllerUtils.getNsxDhcpRelayConfigId(zoneId, domainId, accountId, vpcId, networkId); + Assert.assertEquals(expected, dhcpRelayConfigId); + } + + @Test + public void testGetNsxDhcpRelayConfigIdForNetwork() { + Long vpcId = null; + long networkId = 2L; + String relayPart = String.format("S%s-Relay", networkId); + String expected = String.format("%s-%s", commonPrefix, relayPart); + String dhcpRelayConfigId = NsxControllerUtils.getNsxDhcpRelayConfigId(zoneId, domainId, accountId, vpcId, networkId); + Assert.assertEquals(expected, dhcpRelayConfigId); + } + + @Test + public void testGetStaticNatRuleNameForVpc() { + long vpcId = 5L; + String rulePart = String.format("V%s-STATICNAT", vpcId); + String expected = String.format("%s-%s", commonPrefix, rulePart); + String staticNatRuleName = NsxControllerUtils.getStaticNatRuleName(domainId, accountId, zoneId, vpcId, true); + Assert.assertEquals(expected, staticNatRuleName); + } + + @Test + public void testGetStaticNatRuleNameForNetwork() { + long network = 5L; + String rulePart = String.format("N%s-STATICNAT", network); + String expected = String.format("%s-%s", commonPrefix, rulePart); + String staticNatRuleName = NsxControllerUtils.getStaticNatRuleName(domainId, accountId, zoneId, network, false); + Assert.assertEquals(expected, staticNatRuleName); + } + + @Test + public void testGetPortForwardRuleName() { + long vpcId = 5L; + long ruleId = 2L; + String rulePart = String.format("V%s-PF%s", vpcId, ruleId); + String expected = String.format("%s-%s", commonPrefix, rulePart); + String portForwardRuleName = NsxControllerUtils.getPortForwardRuleName(domainId, accountId, zoneId, vpcId, ruleId, true); + Assert.assertEquals(expected, portForwardRuleName); + } +} diff --git a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightGuestNetworkGuru.java b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightGuestNetworkGuru.java index 659caf046490..7b4851fc285c 100644 --- a/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightGuestNetworkGuru.java +++ b/plugins/network-elements/opendaylight/src/main/java/org/apache/cloudstack/network/opendaylight/OpendaylightGuestNetworkGuru.java @@ -97,7 +97,7 @@ protected boolean canHandle(NetworkOffering offering, NetworkType networkType, P } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { PhysicalNetworkVO physnet = physicalNetworkDao.findById(plan.getPhysicalNetworkId()); DataCenter dc = _dcDao.findById(plan.getDataCenterId()); if (!canHandle(offering, dc.getNetworkType(), physnet)) { @@ -113,7 +113,7 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network use logger.debug("Controller " + devices.get(0).getUuid() + " found on physical network " + physnet.getId()); logger.debug("Physical isolation type is ODL, asking GuestNetworkGuru to design this network"); - NetworkVO networkObject = (NetworkVO)super.design(offering, plan, userSpecified, owner); + NetworkVO networkObject = (NetworkVO)super.design(offering, plan, userSpecified, name, vpcId, owner); if (networkObject == null) { return null; } diff --git a/plugins/network-elements/ovs/src/main/java/com/cloud/network/guru/OvsGuestNetworkGuru.java b/plugins/network-elements/ovs/src/main/java/com/cloud/network/guru/OvsGuestNetworkGuru.java index 240af3ac78c1..97531a915377 100644 --- a/plugins/network-elements/ovs/src/main/java/com/cloud/network/guru/OvsGuestNetworkGuru.java +++ b/plugins/network-elements/ovs/src/main/java/com/cloud/network/guru/OvsGuestNetworkGuru.java @@ -94,7 +94,7 @@ && isMyIsolationMethod(physicalNetwork) @Override public Network design(NetworkOffering offering, DeploymentPlan plan, - Network userSpecified, Account owner) { + Network userSpecified, String name, Long vpcId, Account owner) { PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan .getPhysicalNetworkId()); @@ -104,7 +104,7 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, return null; } NetworkVO config = (NetworkVO)super.design(offering, plan, - userSpecified, owner); + userSpecified, name, vpcId, owner); if (config == null) { return null; } diff --git a/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/service/TungstenGuestNetworkGuru.java b/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/service/TungstenGuestNetworkGuru.java index 818d370cca03..4d22806a139c 100644 --- a/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/service/TungstenGuestNetworkGuru.java +++ b/plugins/network-elements/tungsten/src/main/java/org/apache/cloudstack/network/tungsten/service/TungstenGuestNetworkGuru.java @@ -144,7 +144,7 @@ && isMyIsolationMethod(physicalNetwork) && networkOfferingServiceMapDao.isProvid } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); DataCenter dc = _dcDao.findById(plan.getDataCenterId()); @@ -154,7 +154,7 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network use return null; } - NetworkVO network = (NetworkVO) super.design(offering, plan, userSpecified, owner); + NetworkVO network = (NetworkVO) super.design(offering, plan, userSpecified, name, vpcId, owner); if (network == null) { return null; diff --git a/plugins/network-elements/tungsten/src/test/java/org/apache/cloudstack/network/tungsten/service/TungstenGuestNetworkGuruTest.java b/plugins/network-elements/tungsten/src/test/java/org/apache/cloudstack/network/tungsten/service/TungstenGuestNetworkGuruTest.java index 6a5a0132442a..f66e026ba91b 100644 --- a/plugins/network-elements/tungsten/src/test/java/org/apache/cloudstack/network/tungsten/service/TungstenGuestNetworkGuruTest.java +++ b/plugins/network-elements/tungsten/src/test/java/org/apache/cloudstack/network/tungsten/service/TungstenGuestNetworkGuruTest.java @@ -233,7 +233,7 @@ public void testCanDesign() { final Network network = mock(Network.class); final Account account = mock(Account.class); - final Network designedNetwork = guru.design(offering, plan, network, account); + final Network designedNetwork = guru.design(offering, plan, network, "", 1L, account); assertNotNull(designedNetwork); assertSame(Networks.BroadcastDomainType.TUNGSTEN, designedNetwork.getBroadcastDomainType()); assertSame(Network.State.Allocated, designedNetwork.getState()); diff --git a/plugins/network-elements/vxlan/src/main/java/com/cloud/network/guru/VxlanGuestNetworkGuru.java b/plugins/network-elements/vxlan/src/main/java/com/cloud/network/guru/VxlanGuestNetworkGuru.java index 1618c9710f87..a1ff8d309d9f 100644 --- a/plugins/network-elements/vxlan/src/main/java/com/cloud/network/guru/VxlanGuestNetworkGuru.java +++ b/plugins/network-elements/vxlan/src/main/java/com/cloud/network/guru/VxlanGuestNetworkGuru.java @@ -66,8 +66,8 @@ protected boolean canHandle(NetworkOffering offering, final NetworkType networkT } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { - NetworkVO network = (NetworkVO)super.design(offering, plan, userSpecified, owner); + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { + NetworkVO network = (NetworkVO)super.design(offering, plan, userSpecified, name, vpcId, owner); if (network == null) { return null; } diff --git a/plugins/network-elements/vxlan/src/test/java/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java b/plugins/network-elements/vxlan/src/test/java/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java index 4d485e25a642..71f86791725d 100644 --- a/plugins/network-elements/vxlan/src/test/java/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java +++ b/plugins/network-elements/vxlan/src/test/java/com/cloud/network/guru/VxlanGuestNetworkGuruTest.java @@ -138,7 +138,7 @@ public void testDesign() { Network network = mock(Network.class); Account account = mock(Account.class); - Network designednetwork = guru.design(offering, plan, network, account); + Network designednetwork = guru.design(offering, plan, network, "", 1L, account); assertTrue(designednetwork != null); assertTrue(designednetwork.getBroadcastDomainType() == BroadcastDomainType.Vxlan); } diff --git a/plugins/pom.xml b/plugins/pom.xml index cbfba8f82177..1083c36275fd 100755 --- a/plugins/pom.xml +++ b/plugins/pom.xml @@ -113,6 +113,7 @@ network-elements/brocade-vcs network-elements/vxlan network-elements/tungsten + network-elements/nsx outofbandmanagement-drivers/ipmitool outofbandmanagement-drivers/nested-cloudstack diff --git a/server/src/main/java/com/cloud/api/ApiResponseHelper.java b/server/src/main/java/com/cloud/api/ApiResponseHelper.java index a0dc0d17c710..bcf89b7f4d86 100644 --- a/server/src/main/java/com/cloud/api/ApiResponseHelper.java +++ b/server/src/main/java/com/cloud/api/ApiResponseHelper.java @@ -31,6 +31,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TimeZone; import java.util.function.Consumer; @@ -38,6 +39,8 @@ import javax.inject.Inject; +import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.VlanDetailsDao; import com.cloud.hypervisor.Hypervisor; import com.cloud.storage.BucketVO; import org.apache.cloudstack.acl.ControlledEntity; @@ -482,6 +485,8 @@ public class ApiResponseHelper implements ResponseGenerator { FirewallRulesDao firewallRulesDao; @Inject UserDataDao userDataDao; + @Inject + VlanDetailsDao vlanDetailsDao; @Inject ObjectStoreDao _objectStoreDao; @@ -959,6 +964,8 @@ public VlanIpRangeResponse createVlanIpRangeResponse(Class tags = ApiDBUtils.listByResourceTypeAndId(ResourceObjectType.PublicIpAddress, ipAddr.getId()); @@ -2373,6 +2381,8 @@ public NetworkOfferingResponse createNetworkOfferingResponse(NetworkOffering off } response.setForVpc(_configMgr.isOfferingForVpc(offering)); response.setForTungsten(offering.isForTungsten()); + response.setForNsx(offering.isForNsx()); + response.setNsxMode(offering.getNsxMode()); response.setServices(serviceResponses); //set network offering details Map details = _ntwkModel.getNtwkOffDetails(offering.getId()); diff --git a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java index 24b7df5591ff..2bfbb3b9d677 100644 --- a/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java +++ b/server/src/main/java/com/cloud/api/query/dao/DataCenterJoinDaoImpl.java @@ -17,9 +17,12 @@ package com.cloud.api.query.dao; import java.util.List; +import java.util.Objects; import javax.inject.Inject; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.element.NsxProviderVO; import org.apache.cloudstack.annotation.AnnotationService; import org.apache.cloudstack.annotation.dao.AnnotationDao; import org.apache.cloudstack.api.ResponseObject.ResponseView; @@ -51,6 +54,8 @@ public class DataCenterJoinDaoImpl extends GenericDaoBase> sameSubnet) { + final String ip6Gateway, final String ip6Cidr, final Domain domain, final Account vlanOwner, final Network network, final Pair> sameSubnet, boolean forNsx) { final GlobalLock commitVlanLock = GlobalLock.getInternLock("CommitVlan"); commitVlanLock.lock(5); logger.debug("Acquiring lock for committing vlan"); @@ -4615,7 +4628,7 @@ public Vlan doInTransaction(final TransactionStatus status) { newVlanNetmask = sameSubnet.second().second(); } final Vlan vlan = createVlanAndPublicIpRange(zoneId, networkId, physicalNetworkId, forVirtualNetwork, forSystemVms, podId, startIP, endIP, newVlanGateway, newVlanNetmask, vlanId, - false, domain, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr); + false, domain, vlanOwner, startIPv6, endIPv6, ip6Gateway, ip6Cidr, forNsx); // create an entry in the nic_secondary table. This will be the new // gateway that will be configured on the corresponding routervm. return vlan; @@ -4739,7 +4752,7 @@ public boolean hasSameSubnet(boolean ipv4, String vlanGateway, String vlanNetmas @Override @DB public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final boolean forVirtualNetwork, final boolean forSystemVms, final Long podId, final String startIP, final String endIP, - final String vlanGateway, final String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, final Account vlanOwner, final String startIPv6, final String endIPv6, final String vlanIp6Gateway, final String vlanIp6Cidr) { + final String vlanGateway, final String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, final Account vlanOwner, final String startIPv6, final String endIPv6, final String vlanIp6Gateway, final String vlanIp6Cidr, boolean forNsx) { final Network network = _networkModel.getNetwork(networkId); boolean ipv4 = false, ipv6 = false; @@ -4821,11 +4834,11 @@ public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, } else { vlanId = networkVlanId; } - } else if (network.getTrafficType() == TrafficType.Public && vlanId == null) { + } else if (network.getTrafficType() == TrafficType.Public && vlanId == null && !forNsx) { throw new InvalidParameterValueException("Unable to determine vlan id or untagged vlan for public network"); } - if (vlanId == null) { + if (vlanId == null && !forNsx) { vlanId = Vlan.UNTAGGED; } @@ -4922,7 +4935,7 @@ public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, if (isSharedNetworkWithoutSpecifyVlan) { bypassVlanOverlapCheck = true; } - if (!bypassVlanOverlapCheck && _zoneDao.findVnet(zoneId, physicalNetworkId, BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlanId))).size() > 0) { + if (!bypassVlanOverlapCheck && !forNsx && !_zoneDao.findVnet(zoneId, physicalNetworkId, BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlanId))).isEmpty()) { throw new InvalidParameterValueException("The VLAN tag " + vlanId + " is already being used for dynamic vlan allocation for the guest network in zone " + zone.getName()); } @@ -4938,7 +4951,7 @@ public Vlan createVlanAndPublicIpRange(final long zoneId, final long networkId, // Everything was fine, so persist the VLAN final VlanVO vlan = commitVlanAndIpRange(zoneId, networkId, physicalNetworkId, podId, startIP, endIP, vlanGateway, vlanNetmask, vlanId, domain, vlanOwner, vlanIp6Gateway, vlanIp6Cidr, - ipv4, zone, vlanType, ipv6Range, ipRange, forSystemVms); + ipv4, zone, vlanType, ipv6Range, ipRange, forSystemVms, forNsx); return vlan; } @@ -4960,9 +4973,11 @@ private void checkZoneVlanIpOverlap(DataCenterVO zone, Network network, String n continue; } // from here, subnet overlaps - if (vlanId.toLowerCase().contains(Vlan.UNTAGGED) || UriUtils.checkVlanUriOverlap( + VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(vlan.getId(), ApiConstants.NSX_DETAIL_KEY); + if ((Objects.isNull(vlanId) && Objects.nonNull(vlanDetail) && vlanDetail.getValue().equals("true")) || Objects.nonNull(vlanId) && + (vlanId.toLowerCase().contains(Vlan.UNTAGGED) || UriUtils.checkVlanUriOverlap( BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlanId)), - BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlan.getVlanTag())))) { + BroadcastDomainType.getValue(BroadcastDomainType.fromString(vlan.getVlanTag()))))) { // For untagged VLAN Id and overlapping URIs we need to expand and verify IP ranges final String[] otherVlanIpRange = vlan.getIpRange().split("\\-"); final String otherVlanStartIP = otherVlanIpRange[0]; @@ -5007,13 +5022,14 @@ private void checkZoneVlanIpOverlap(DataCenterVO zone, Network network, String n private VlanVO commitVlanAndIpRange(final long zoneId, final long networkId, final long physicalNetworkId, final Long podId, final String startIP, final String endIP, final String vlanGateway, final String vlanNetmask, final String vlanId, final Domain domain, final Account vlanOwner, final String vlanIp6Gateway, final String vlanIp6Cidr, - final boolean ipv4, final DataCenterVO zone, final VlanType vlanType, final String ipv6Range, final String ipRange, final boolean forSystemVms) { + final boolean ipv4, final DataCenterVO zone, final VlanType vlanType, final String ipv6Range, final String ipRange, final boolean forSystemVms, final boolean forNsx) { return Transaction.execute(new TransactionCallback() { @Override public VlanVO doInTransaction(final TransactionStatus status) { VlanVO vlan = new VlanVO(vlanType, vlanId, vlanGateway, vlanNetmask, zone.getId(), ipRange, networkId, physicalNetworkId, vlanIp6Gateway, vlanIp6Cidr, ipv6Range); logger.debug("Saving vlan range " + vlan); vlan = _vlanDao.persist(vlan); + vlanDetailsDao.addDetail(vlan.getId(), ApiConstants.NSX_DETAIL_KEY, String.valueOf(forNsx), true); // IPv6 use a used ip map, is different from ipv4, no need to save // public ip range @@ -5992,7 +6008,10 @@ public NetworkOffering createNetworkOffering(final CreateNetworkOfferingCmd cmd) final Map detailsStr = cmd.getDetails(); final Boolean egressDefaultPolicy = cmd.getEgressDefaultPolicy(); Boolean forVpc = cmd.getForVpc(); + Boolean forNsx = cmd.isForNsx(); Boolean forTungsten = cmd.getForTungsten(); + String nsxMode = cmd.getNsxMode(); + boolean nsxSupportInternalLbSvc = cmd.getNsxSupportsInternalLbService(); Integer maxconn = null; boolean enableKeepAlive = false; String servicePackageuuid = cmd.getServicePackageId(); @@ -6026,6 +6045,26 @@ public NetworkOffering createNetworkOffering(final CreateNetworkOfferingCmd cmd) } } + if (Boolean.TRUE.equals(forNsx) && Boolean.TRUE.equals(forTungsten)) { + throw new InvalidParameterValueException("Network Offering cannot be for both Tungsten-Fabric and NSX"); + } + + if (Boolean.TRUE.equals(forNsx)) { + if (Objects.isNull(nsxMode)) { + throw new InvalidParameterValueException("Mode for an NSX offering needs to be specified. Valid values: " + Arrays.toString(NetworkOffering.NsxMode.values())); + } + if (!EnumUtils.isValidEnum(NetworkOffering.NsxMode.class, nsxMode)) { + throw new InvalidParameterValueException("Invalid mode passed. Valid values: " + Arrays.toString(NetworkOffering.NsxMode.values())); + } + } else { + if (Objects.nonNull(nsxMode)) { + if (logger.isTraceEnabled()) { + logger.trace("nsxMode has is ignored for non-NSX enabled zones"); + } + nsxMode = null; + } + } + // Verify traffic type for (final TrafficType tType : TrafficType.values()) { if (tType.name().equalsIgnoreCase(trafficTypeString)) { @@ -6290,7 +6329,12 @@ public NetworkOffering createNetworkOffering(final CreateNetworkOfferingCmd cmd) } final NetworkOfferingVO offering = createNetworkOffering(name, displayText, trafficType, tags, specifyVlan, availability, networkRate, serviceProviderMap, false, guestType, false, - serviceOfferingId, conserveMode, serviceCapabilityMap, specifyIpRanges, isPersistent, details, egressDefaultPolicy, maxconn, enableKeepAlive, forVpc, forTungsten, domainIds, zoneIds, enable, internetProtocol); + serviceOfferingId, conserveMode, serviceCapabilityMap, specifyIpRanges, isPersistent, details, egressDefaultPolicy, maxconn, enableKeepAlive, forVpc, forTungsten, forNsx, nsxMode, domainIds, zoneIds, enable, internetProtocol); + if (Boolean.TRUE.equals(forNsx) && nsxSupportInternalLbSvc) { + offering.setInternalLb(true); + offering.setPublicLb(false); + _networkOfferingDao.update(offering.getId(), offering); + } CallContext.current().setEventDetails(" Id: " + offering.getId() + " Name: " + name); CallContext.current().putContextParameter(NetworkOffering.class, offering.getId()); return offering; @@ -6430,12 +6474,12 @@ void validateConnectivityServiceCapablities(final Network.GuestType guestType, f @Override @DB public NetworkOfferingVO createNetworkOffering(final String name, final String displayText, final TrafficType trafficType, String tags, final boolean specifyVlan, - final Availability availability, - final Integer networkRate, final Map> serviceProviderMap, final boolean isDefault, final GuestType type, final boolean systemOnly, - final Long serviceOfferingId, - final boolean conserveMode, final Map> serviceCapabilityMap, final boolean specifyIpRanges, final boolean isPersistent, - final Map details, final boolean egressDefaultPolicy, final Integer maxconn, final boolean enableKeepAlive, Boolean forVpc, - Boolean forTungsten, final List domainIds, final List zoneIds, final boolean enableOffering, final NetUtils.InternetProtocol internetProtocol) { + final Availability availability, + final Integer networkRate, final Map> serviceProviderMap, final boolean isDefault, final GuestType type, final boolean systemOnly, + final Long serviceOfferingId, + final boolean conserveMode, final Map> serviceCapabilityMap, final boolean specifyIpRanges, final boolean isPersistent, + final Map details, final boolean egressDefaultPolicy, final Integer maxconn, final boolean enableKeepAlive, Boolean forVpc, + Boolean forTungsten, boolean forNsx, String mode, final List domainIds, final List zoneIds, final boolean enableOffering, final NetUtils.InternetProtocol internetProtocol) { String servicePackageUuid; String spDescription = null; @@ -6596,6 +6640,10 @@ public NetworkOfferingVO createNetworkOffering(final String name, final String d } offeringFinal.setForTungsten(Objects.requireNonNullElse(forTungsten, false)); + offeringFinal.setForNsx(Objects.requireNonNullElse(forNsx, false)); + if (Boolean.TRUE.equals(forNsx)) { + offeringFinal.setNsxMode(mode); + } if (enableOffering) { offeringFinal.setState(NetworkOffering.State.Enabled); @@ -6660,7 +6708,7 @@ public NetworkOfferingVO doInTransaction(final TransactionStatus status) { logger.trace("Added service for the network offering: " + offService + " with provider " + provider.getName()); } - if (vpcOff) { + if (vpcOff && !forNsx) { final List supportedSvcs = new ArrayList(); supportedSvcs.addAll(serviceProviderMap.keySet()); _vpcMgr.validateNtwkOffForVpc(offering, supportedSvcs); @@ -7749,8 +7797,8 @@ public String getConfigComponentName() { public ConfigKey[] getConfigKeys() { return new ConfigKey[] {SystemVMUseLocalStorage, IOPS_MAX_READ_LENGTH, IOPS_MAX_WRITE_LENGTH, BYTES_MAX_READ_LENGTH, BYTES_MAX_WRITE_LENGTH, ADD_HOST_ON_SERVICE_RESTART_KVM, SET_HOST_DOWN_TO_MAINTENANCE, VM_SERVICE_OFFERING_MAX_CPU_CORES, - VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH, MIGRATE_VM_ACROSS_CLUSTERS, - ENABLE_ACCOUNT_SETTINGS_FOR_DOMAIN, ENABLE_DOMAIN_SETTINGS_FOR_CHILD_DOMAIN, ALLOW_DOMAIN_ADMINS_TO_CREATE_TAGGED_OFFERINGS + VM_SERVICE_OFFERING_MAX_RAM_SIZE, VM_USERDATA_MAX_LENGTH, MIGRATE_VM_ACROSS_CLUSTERS, ENABLE_ACCOUNT_SETTINGS_FOR_DOMAIN, + ENABLE_DOMAIN_SETTINGS_FOR_CHILD_DOMAIN, ALLOW_DOMAIN_ADMINS_TO_CREATE_TAGGED_OFFERINGS, AllowNonRFC1918CompliantIPs }; } diff --git a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java index d0d728d4410c..236a28694211 100644 --- a/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java +++ b/server/src/main/java/com/cloud/hypervisor/HypervisorGuruBase.java @@ -18,10 +18,20 @@ import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.UUID; import javax.inject.Inject; +import com.cloud.dc.DataCenter; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.domain.Domain; +import com.cloud.domain.dao.DomainDao; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.user.Account; +import com.cloud.user.AccountManager; +import com.cloud.utils.exception.CloudRuntimeException; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.backup.Backup; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; @@ -76,6 +86,14 @@ public abstract class HypervisorGuruBase extends AdapterBase implements Hypervis protected NetworkDao networkDao; @Inject + protected VpcDao vpcDao; + @Inject + protected AccountManager accountManager; + @Inject + private DomainDao domainDao; + @Inject + private DataCenterDao dcDao; + @Inject private NetworkOfferingDetailsDao networkOfferingDetailsDao; @Inject protected @@ -145,9 +163,27 @@ public NicTO toNicTO(NicProfile profile) { to.setMtu(profile.getMtu()); to.setIp6Dns1(profile.getIPv6Dns1()); to.setIp6Dns2(profile.getIPv6Dns2()); + to.setNetworkId(profile.getNetworkId()); NetworkVO network = networkDao.findById(profile.getNetworkId()); to.setNetworkUuid(network.getUuid()); + Account account = accountManager.getAccount(network.getAccountId()); + Domain domain = domainDao.findById(network.getDomainId()); + DataCenter zone = dcDao.findById(network.getDataCenterId()); + if (Objects.isNull(zone)) { + throw new CloudRuntimeException(String.format("Failed to find zone with ID: %s", network.getDataCenterId())); + } + if (Objects.isNull(account)) { + throw new CloudRuntimeException(String.format("Failed to find account with ID: %s", network.getAccountId())); + } + if (Objects.isNull(domain)) { + throw new CloudRuntimeException(String.format("Failed to find domain with ID: %s", network.getDomainId())); + } + VpcVO vpc = null; + if (Objects.nonNull(network.getVpcId())) { + vpc = vpcDao.findById(network.getVpcId()); + } + to.setNetworkSegmentName(getNetworkName(zone.getId(), domain.getId(), account.getId(), vpc, network.getId())); // Workaround to make sure the TO has the UUID we need for Nicira integration NicVO nicVO = nicDao.findById(profile.getId()); @@ -176,6 +212,15 @@ public NicTO toNicTO(NicProfile profile) { return to; } + private String getNetworkName(long zoneId, long domainId, long accountId, VpcVO vpc, long networkId) { + String prefix = String.format("D%s-A%s-Z%s", domainId, accountId, zoneId); + if (Objects.isNull(vpc)) { + return prefix + "-S" + networkId; + } + return prefix + "-V" + vpc.getId() + "-S" + networkId; + } + + /** * Add extra configuration from VM details. Extra configuration is stored as details starting with 'extraconfig' */ diff --git a/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java b/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java index b8c464cfe3e9..16473e837a99 100644 --- a/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkMigrationManagerImpl.java @@ -460,7 +460,7 @@ public Network upgradeNetworkToNewNetworkOffering(long networkId, long newPhysic DataCenterDeployment plan = new DataCenterDeployment(network.getDataCenterId(), null, null, null, null, newPhysicalNetworkId); for (final NetworkGuru guru : _networkMgr.getNetworkGurus()) { - final Network designedNetwork = guru.design(newOffering, plan, network, networkAccount); + final Network designedNetwork = guru.design(newOffering, plan, network, network.getName(), vpcId, networkAccount); if (designedNetwork == null) { continue; } diff --git a/server/src/main/java/com/cloud/network/NetworkModelImpl.java b/server/src/main/java/com/cloud/network/NetworkModelImpl.java index af0d25c4c1db..07e471c377f8 100644 --- a/server/src/main/java/com/cloud/network/NetworkModelImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkModelImpl.java @@ -101,8 +101,10 @@ import com.cloud.network.rules.FirewallRule.Purpose; import com.cloud.network.rules.FirewallRuleVO; import com.cloud.network.rules.dao.PortForwardingRulesDao; +import com.cloud.network.vpc.Vpc; import com.cloud.network.vpc.VpcGatewayVO; import com.cloud.network.vpc.dao.PrivateIpDao; +import com.cloud.network.vpc.dao.VpcDao; import com.cloud.network.vpc.dao.VpcGatewayDao; import com.cloud.offering.NetworkOffering; import com.cloud.offering.NetworkOffering.Detail; @@ -178,6 +180,8 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi ProjectDao projectDao; @Inject NetworkPermissionDao _networkPermissionDao; + @Inject + VpcDao vpcDao; private List networkElements; @@ -489,7 +493,7 @@ Map> getProviderServicesMap(long networkId) { @Override public Map> getProviderToIpList(Network network, Map> ipToServices) { NetworkOffering offering = _networkOfferingDao.findById(network.getNetworkOfferingId()); - if (!offering.isConserveMode()) { + if (!offering.isConserveMode() && !offering.isForNsx()) { for (PublicIpAddress ip : ipToServices.keySet()) { Set services = new HashSet(); services.addAll(ipToServices.get(ip)); @@ -1615,7 +1619,7 @@ public boolean checkIpForService(IpAddress userIp, Service service, Long network if (!canIpUsedForService(publicIp, service, networkId)) { return false; } - if (!offering.isConserveMode()) { + if (!offering.isConserveMode() && !offering.isForNsx()) { return canIpUsedForNonConserveService(publicIp, service); } return true; @@ -2713,6 +2717,12 @@ public Pair getNetworkIp4Dns(final Network network, final DataCe if (StringUtils.isNotBlank(network.getDns1())) { return new Pair<>(network.getDns1(), network.getDns2()); } + if (network.getVpcId() != null) { + Vpc vpc = vpcDao.findById(network.getVpcId()); + if (vpc != null && StringUtils.isNotBlank(vpc.getIp4Dns1())) { + return new Pair<>(vpc.getIp4Dns1(), vpc.getIp4Dns2()); + } + } return new Pair<>(zone.getDns1(), zone.getDns2()); } @@ -2721,6 +2731,12 @@ public Pair getNetworkIp6Dns(final Network network, final DataCe if (StringUtils.isNotBlank(network.getIp6Dns1())) { return new Pair<>(network.getIp6Dns1(), network.getIp6Dns2()); } + if (network.getVpcId() != null) { + Vpc vpc = vpcDao.findById(network.getVpcId()); + if (vpc != null && StringUtils.isNotBlank(vpc.getIp6Dns1())) { + return new Pair<>(vpc.getIp6Dns1(), vpc.getIp6Dns2()); + } + } return new Pair<>(zone.getIp6Dns1(), zone.getIp6Dns2()); } diff --git a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java index 2b7e0450935f..2d1552d7b0be 100644 --- a/server/src/main/java/com/cloud/network/NetworkServiceImpl.java +++ b/server/src/main/java/com/cloud/network/NetworkServiceImpl.java @@ -34,6 +34,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.stream.Collectors; @@ -41,7 +42,13 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.VlanDetailsVO; +import com.cloud.dc.dao.VlanDetailsDao; +import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.dao.PublicIpQuarantineDao; +import com.cloud.network.dao.VirtualRouterProviderDao; +import com.cloud.network.element.NsxProviderVO; +import com.cloud.network.element.VirtualRouterProviderVO; import com.cloud.offering.ServiceOffering; import com.cloud.service.dao.ServiceOfferingDao; import org.apache.cloudstack.acl.ControlledEntity.ACLType; @@ -79,6 +86,7 @@ import org.apache.cloudstack.network.dao.NetworkPermissionDao; import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.collections.MapUtils; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; @@ -282,6 +290,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C @Inject VlanDao _vlanDao = null; @Inject + private VlanDetailsDao vlanDetailsDao; + @Inject IPAddressDao _ipAddressDao = null; @Inject AccountDao _accountDao = null; @@ -349,8 +359,6 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C @Inject HostDao _hostDao; @Inject - InternalLoadBalancerElementService _internalLbElementSvc; - @Inject DataCenterVnetDao _dcVnetDao; @Inject AccountGuestVlanMapDao _accountGuestVlanMapDao; @@ -404,6 +412,12 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C ServiceOfferingDao serviceOfferingDao; @Inject PublicIpQuarantineDao publicIpQuarantineDao; + @Inject + NsxProviderDao nsxProviderDao; + @Inject + private VirtualRouterProviderDao virtualRouterProviderDao; + List internalLoadBalancerElementServices = new ArrayList<>(); + Map internalLoadBalancerElementServiceMap = new HashMap<>(); @Autowired @Qualifier("networkHelper") @@ -811,9 +825,19 @@ public boolean configure(final String name, final Map params) th @Override public boolean start() { + initializeInternalLoadBalancerElementsMap(); return true; } + private void initializeInternalLoadBalancerElementsMap() { + if (MapUtils.isEmpty(internalLoadBalancerElementServiceMap) && CollectionUtils.isNotEmpty(internalLoadBalancerElementServices)) { + for (InternalLoadBalancerElementService service : internalLoadBalancerElementServices) { + internalLoadBalancerElementServiceMap.put(service.getProviderType().name(), service); + } + logger.debug(String.format("Discovered internal loadbalancer elements configured on NetworkServiceImpl")); + } + } + @Override public boolean stop() { return true; @@ -1137,6 +1161,58 @@ public IpAddress reserveIpAddress(Account account, Boolean displayIp, Long ipAdd return ipVO; } + @Override + public IpAddress reserveIpAddressWithVlanDetail(Account account, DataCenter zone, Boolean displayIp, String vlanDetailKey) throws ResourceAllocationException { + // verify permissions + Account caller = CallContext.current().getCallingAccount(); + _accountMgr.checkAccess(caller, null, true, account); + + VlanVO vlan = findOneVlanRangeMatchingVlanDetailKey(zone, vlanDetailKey); + if (vlan == null) { + String msg = String.format("Cannot find any vlan matching the detail key %s on zone %s", vlanDetailKey, zone.getName()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + + List freeIps = _ipAddressDao.listByVlanIdAndState(vlan.getId(), State.Free); + if (CollectionUtils.isEmpty(freeIps)) { + String msg = String.format("Cannot find any free IP matching on the VLAN range %s on zone %s", vlan.getIpRange(), zone.getName()); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + + Collections.shuffle(freeIps); + IPAddressVO selectedIp = freeIps.get(0); + + selectedIp.setAllocatedTime(new Date()); + selectedIp.setAllocatedToAccountId(account.getAccountId()); + selectedIp.setAllocatedInDomainId(account.getDomainId()); + selectedIp.setState(State.Reserved); + if (displayIp != null) { + selectedIp.setDisplay(displayIp); + } + selectedIp = _ipAddressDao.persist(selectedIp); + + Long ipDedicatedAccountId = getIpDedicatedAccountId(selectedIp.getVlanId()); + if (ipDedicatedAccountId == null) { + _resourceLimitMgr.incrementResourceCount(account.getId(), Resource.ResourceType.public_ip); + } + + return selectedIp; + } + + private VlanVO findOneVlanRangeMatchingVlanDetailKey(DataCenter zone, String vlanDetailKey) { + List zoneVlans = _vlanDao.listByZone(zone.getId()); + for (VlanVO zoneVlan : zoneVlans) { + VlanDetailsVO detail = vlanDetailsDao.findDetail(zoneVlan.getId(), vlanDetailKey); + if (detail != null && detail.getValue().equalsIgnoreCase("true")) { + logger.debug(String.format("Found the VLAN range %s is set for NSX on zone %s", zoneVlan.getIpRange(), zone.getName())); + return zoneVlan; + } + } + return null; + } + private Long getIpDedicatedAccountId(Long vlanId) { List accountVlanMaps = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanId); if (CollectionUtils.isNotEmpty(accountVlanMaps)) { @@ -1414,6 +1490,7 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac _accountMgr.checkAccess(owner, ntwkOff, zone); validateZoneAvailability(caller, zone); + validateNetworkCreationSupported(zone.getId(), zone.getName(), ntwkOff.getGuestType()); ACLType aclType = getAclType(caller, cmd.getAclType(), ntwkOff); @@ -1670,6 +1747,15 @@ public Network createGuestNetwork(CreateNetworkCmd cmd) throws InsufficientCapac return network; } + private void validateNetworkCreationSupported(long zoneId, String zoneName, GuestType guestType) { + NsxProviderVO nsxProviderVO = nsxProviderDao.findByZoneId(zoneId); + if (Objects.nonNull(nsxProviderVO) && List.of(GuestType.L2, GuestType.Shared).contains(guestType)) { + throw new InvalidParameterValueException( + String.format("Creation of %s networks is not supported in NSX enabled zone %s", guestType.name(), zoneName) + ); + } + } + void checkAndSetRouterSourceNatIp(Account owner, CreateNetworkCmd cmd, Network network) throws InsufficientAddressCapacityException, ResourceAllocationException { String sourceNatIp = cmd.getSourceNatIP(); if (sourceNatIp == null) { @@ -2119,7 +2205,7 @@ public Network doInTransaction(TransactionStatus status) throws InsufficientCapa if (createVlan && network != null) { // Create vlan ip range _configMgr.createVlanAndPublicIpRange(pNtwk.getDataCenterId(), network.getId(), physicalNetworkId, false, false, null, startIP, endIP, gateway, netmask, vlanId, - bypassVlanOverlapCheck, null, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr); + bypassVlanOverlapCheck, null, null, startIPv6, endIPv6, ip6Gateway, ip6Cidr, ntwkOff.isForNsx()); } if (associatedNetwork != null) { _networkDetailsDao.persist(new NetworkDetailVO(network.getId(), Network.AssociatedNetworkId, String.valueOf(associatedNetwork.getId()), true)); @@ -3074,7 +3160,7 @@ public Network updateGuestNetwork(final UpdateNetworkCmd cmd) { if (!NetUtils.isValidIp4Cidr(guestVmCidr)) { throw new InvalidParameterValueException("Invalid format of Guest VM CIDR."); } - if (!NetUtils.validateGuestCidr(guestVmCidr)) { + if (!NetUtils.validateGuestCidr(guestVmCidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { throw new InvalidParameterValueException("Invalid format of Guest VM CIDR. Make sure it is RFC1918 compliant. "); } @@ -4023,6 +4109,7 @@ public PhysicalNetworkVO doInTransaction(TransactionStatus status) { // Add the config drive provider addConfigDriveToPhysicalNetwork(pNetwork.getId()); + addNSXProviderToPhysicalNetwork(pNetwork.getId()); CallContext.current().putContextParameter(PhysicalNetwork.class, pNetwork.getUuid()); @@ -5348,7 +5435,8 @@ protected PhysicalNetworkServiceProvider addDefaultInternalLbProviderToPhysicalN throw new CloudRuntimeException("Unable to find the Network Element implementing the " + Network.Provider.InternalLbVm.getName() + " Provider"); } - _internalLbElementSvc.addInternalLoadBalancerElement(nsp.getId()); + InternalLoadBalancerElementService service = getInternalLoadBalancerElementByNetworkServiceProviderId(nsp.getId()); + service.addInternalLoadBalancerElement(nsp.getId()); return nsp; } @@ -5417,6 +5505,22 @@ private PhysicalNetworkServiceProvider addConfigDriveToPhysicalNetwork(long phys } + private PhysicalNetworkServiceProvider addNSXProviderToPhysicalNetwork(long physicalNetworkId) { + PhysicalNetworkVO pvo = _physicalNetworkDao.findById(physicalNetworkId); + DataCenterVO dvo = _dcDao.findById(pvo.getDataCenterId()); + if (dvo.getNetworkType() == NetworkType.Advanced) { + + Provider provider = Network.Provider.getProvider("Nsx"); + if (provider == null) { + return null; + } + + addProviderToPhysicalNetwork(physicalNetworkId, Provider.Nsx.getName(), null, null); + enableProvider(Provider.Nsx.getName()); + } + return null; + } + protected boolean isNetworkSystem(Network network) { NetworkOffering no = _networkOfferingDao.findByIdIncludingRemoved(network.getNetworkOfferingId()); if (no.isSystemOnly()) { @@ -5653,6 +5757,10 @@ public void setNetworkGurus(List networkGurus) { _networkGurus = networkGurus; } + public void setInternalLoadBalancerElementServices(List services) { + this.internalLoadBalancerElementServices = services; + } + @Override @ActionEvent(eventType = EventTypes.EVENT_NET_IP_UPDATE, eventDescription = "updating public ip address", async = true) public IpAddress updateIP(Long id, String customId, Boolean displayIp) { @@ -5994,6 +6102,34 @@ public void removePublicIpAddressFromQuarantine(RemoveQuarantinedIpCmd cmd) thro _ipAddrMgr.removePublicIpAddressFromQuarantine(publicIpQuarantine.getId(), removalReason); } + @Override + public InternalLoadBalancerElementService getInternalLoadBalancerElementByType(Type type) { + return internalLoadBalancerElementServiceMap.getOrDefault(type.name(), null); + } + + @Override + public InternalLoadBalancerElementService getInternalLoadBalancerElementByNetworkServiceProviderId(long networkProviderId) { + PhysicalNetworkServiceProviderVO provider = _pNSPDao.findById(networkProviderId); + if (provider == null) { + String msg = String.format("Cannot find a network service provider with ID %s", networkProviderId); + logger.error(msg); + throw new CloudRuntimeException(msg); + } + Type type = provider.getProviderName().equalsIgnoreCase("nsx") ? Type.Nsx : Type.InternalLbVm; + return getInternalLoadBalancerElementByType(type); + } + + @Override + public InternalLoadBalancerElementService getInternalLoadBalancerElementById(long providerId) { + VirtualRouterProviderVO provider = virtualRouterProviderDao.findById(providerId); + return getInternalLoadBalancerElementByType(provider.getType()); + } + + @Override + public List getInternalLoadBalancerElements() { + return new ArrayList<>(this.internalLoadBalancerElementServiceMap.values()); + } + /** * Retrieves the active quarantine for the given public IP address. It can find by the ID of the quarantine or the address of the public IP. * @throws CloudRuntimeException if it does not find an active quarantine for the given public IP. diff --git a/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java b/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java index 3d1920bcbc37..b8960629d807 100644 --- a/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java +++ b/server/src/main/java/com/cloud/network/element/VpcVirtualRouterElement.java @@ -529,6 +529,11 @@ public boolean applyNetworkACLs(final Network network, final List networks, List networkACLItems) { + return true; + } + @Override protected Type getVirtualRouterProvider() { return Type.VPCVirtualRouter; diff --git a/server/src/main/java/com/cloud/network/firewall/FirewallManagerImpl.java b/server/src/main/java/com/cloud/network/firewall/FirewallManagerImpl.java index 5b0d9eb190d5..e9a93528d058 100644 --- a/server/src/main/java/com/cloud/network/firewall/FirewallManagerImpl.java +++ b/server/src/main/java/com/cloud/network/firewall/FirewallManagerImpl.java @@ -22,12 +22,18 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Set; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.dc.DataCenter; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.element.NsxProviderVO; +import com.cloud.utils.db.EntityManager; import org.apache.cloudstack.api.command.user.firewall.IListFirewallRulesCmd; import org.apache.cloudstack.api.command.user.ipv6.ListIpv6FirewallRulesCmd; import org.apache.cloudstack.context.CallContext; @@ -135,6 +141,10 @@ public class FirewallManagerImpl extends ManagerBase implements FirewallService, NetworkDao _networkDao; @Inject VpcManager _vpcMgr; + @Inject + EntityManager entityManager; + @Inject + NsxProviderDao nsxProviderDao; List _firewallElements; List _pfElements; @@ -687,6 +697,9 @@ public boolean applyFirewallRules(List rules, boolean continueOn } for (FirewallRuleVO rule : rules) { + // validate rule - for NSX + long networkId = rule.getNetworkId(); + validateNsxConstraints(networkId, rule); // load cidrs if any rule.setSourceCidrList(_firewallCidrsDao.getSourceCidrs(rule.getId())); rule.setDestinationCidrsList(_firewallDcidrsDao.getDestCidrs(rule.getId())); @@ -708,6 +721,31 @@ public boolean applyFirewallRules(List rules, boolean continueOn return true; } + private void validateNsxConstraints(long networkId, FirewallRuleVO rule) { + String protocol = rule.getProtocol(); + final Network network = entityManager.findById(Network.class, networkId); + final DataCenter dc = entityManager.findById(DataCenter.class, network.getDataCenterId()); + final NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(dc.getId()); + if (Objects.isNull(nsxProvider)) { + return; + } + + if (NetUtils.ICMP_PROTO.equals(protocol.toLowerCase(Locale.ROOT)) && (rule.getIcmpType() == -1 || rule.getIcmpCode() == -1) + && State.Add.equals(rule.getState())) { + String errorMsg = "Passing -1 for ICMP type is not supported for NSX enabled zones"; + logger.error(errorMsg); + throw new InvalidParameterValueException(errorMsg); + } + + if (List.of(NetUtils.TCP_PROTO, NetUtils.UDP_PROTO).contains(protocol.toLowerCase(Locale.ROOT)) && + (Objects.isNull(rule.getSourcePortStart()) || Objects.isNull(rule.getSourcePortEnd())) && + State.Add.equals(rule.getState())) { + String errorMsg = "Source start and end ports are required to be passed"; + logger.error(errorMsg); + throw new InvalidParameterValueException(errorMsg); + } + } + @Override public boolean applyDefaultEgressFirewallRule(Long networkId, boolean defaultPolicy, boolean add) throws ResourceUnavailableException { diff --git a/server/src/main/java/com/cloud/network/guru/ControlNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/ControlNetworkGuru.java index d553f81cecfe..9cec05446ac6 100644 --- a/server/src/main/java/com/cloud/network/guru/ControlNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/ControlNetworkGuru.java @@ -89,7 +89,7 @@ protected boolean canHandle(NetworkOffering offering) { } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network specifiedConfig, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network specifiedConfig, String name, Long vpcId, Account owner) { if (!canHandle(offering)) { return null; } @@ -103,6 +103,11 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network spe return config; } + @Override + public void setup(Network network, long networkId) { + // do nothing + } + protected ControlNetworkGuru() { super(); } 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 c8c323475908..a8c98fc1deee 100644 --- a/server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/DirectNetworkGuru.java @@ -160,7 +160,7 @@ && isMyIsolationMethod(physnet) } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { DataCenter dc = _dcDao.findById(plan.getDataCenterId()); PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); @@ -249,6 +249,11 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network use return config; } + @Override + public void setup(Network network, long networkId) { + // do nothing + } + protected DirectNetworkGuru() { super(); _isolationMethods = new IsolationMethod[] { new IsolationMethod("VLAN"), new IsolationMethod("VXLAN") }; 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 d11483eba6a1..bdabc6c03a11 100644 --- a/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/ExternalGuestNetworkGuru.java @@ -108,13 +108,13 @@ && isMyIsolationMethod(physicalNetwork) && !offering.isSystemOnly()) { } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { if (_networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Network.Service.Connectivity)) { return null; } - NetworkVO config = (NetworkVO)super.design(offering, plan, userSpecified, owner); + NetworkVO config = (NetworkVO)super.design(offering, plan, userSpecified, name, vpcId, owner); if (config == null) { return null; } else if (_networkModel.networkIsConfiguredForExternalNetworking(plan.getDataCenterId(), config.getId())) { 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 46a0a0ac67d3..ae8ee1ef3aa8 100644 --- a/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/GuestNetworkGuru.java @@ -114,9 +114,9 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur @Inject ConfigurationServer _configServer; @Inject - IpAddressManager _ipAddrMgr; + protected IpAddressManager _ipAddrMgr; @Inject - NetworkOfferingDao networkOfferingDao; + protected NetworkOfferingDao networkOfferingDao; @Inject Ipv6AddressManager ipv6AddressManager; @Inject @@ -146,6 +146,11 @@ protected GuestNetworkGuru() { _isolationMethods = null; } + @Override + public void setup(Network network, long networkId) { + // do nothing + } + private void updateNicIpv6(Network network, NicProfile nic, VirtualMachineProfile vm, DataCenter dc, boolean isGateway) throws InsufficientAddressCapacityException { boolean isIpv6Supported = networkOfferingDao.isIpv6Supported(network.getNetworkOfferingId()); if (!isIpv6Supported || nic.getIPv6Address() != null || network.getIp6Cidr() == null || network.getIp6Gateway() == null) { @@ -217,7 +222,7 @@ public IsolationMethod[] getIsolationMethods() { protected abstract boolean canHandle(NetworkOffering offering, final NetworkType networkType, PhysicalNetwork physicalNetwork); @Override - public Network design(final NetworkOffering offering, final DeploymentPlan plan, final Network userSpecified, final Account owner) { + public Network design(final NetworkOffering offering, final DeploymentPlan plan, final Network userSpecified, String name, Long vpcId, final Account owner) { final DataCenter dc = _dcDao.findById(plan.getDataCenterId()); final PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); diff --git a/server/src/main/java/com/cloud/network/guru/PodBasedNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/PodBasedNetworkGuru.java index a857581397cb..5dafa60f1c75 100644 --- a/server/src/main/java/com/cloud/network/guru/PodBasedNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/PodBasedNetworkGuru.java @@ -74,7 +74,7 @@ public TrafficType[] getSupportedTrafficType() { } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { TrafficType type = offering.getTrafficType(); if (!isMyTrafficType(type)) { @@ -87,6 +87,11 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network use return config; } + @Override + public void setup(Network network, long networkId) { + // do nothing + } + protected PodBasedNetworkGuru() { super(); } 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 9f6551fd78d3..bd4f02040c0b 100644 --- a/server/src/main/java/com/cloud/network/guru/PrivateNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/PrivateNetworkGuru.java @@ -96,7 +96,7 @@ protected boolean canHandle(NetworkOffering offering, DataCenter dc) { } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { DataCenter dc = _entityMgr.findById(DataCenter.class, plan.getDataCenterId()); if (!canHandle(offering, dc)) { return null; @@ -135,6 +135,11 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network use return network; } + @Override + public void setup(Network network, long networkId) { + // do nothing + } + @Override public void deallocate(Network network, NicProfile nic, VirtualMachineProfile vm) { if (logger.isDebugEnabled()) { 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 0061064c8960..1b02e145cc9f 100644 --- a/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/PublicNetworkGuru.java @@ -68,7 +68,7 @@ public class PublicNetworkGuru extends AdapterBase implements NetworkGuru { @Inject NetworkOrchestrationService _networkMgr; @Inject - IPAddressDao _ipAddressDao; + protected IPAddressDao _ipAddressDao; @Inject IpAddressManager _ipAddrMgr; @Inject @@ -98,7 +98,7 @@ protected boolean canHandle(NetworkOffering offering) { } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network network, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network network, String name, Long vpcId, Account owner) { if (!canHandle(offering)) { return null; } @@ -113,6 +113,11 @@ public Network design(NetworkOffering offering, DeploymentPlan plan, Network net } } + @Override + public void setup(Network network, long networkId) { + // do nothing + } + protected PublicNetworkGuru() { super(); } diff --git a/server/src/main/java/com/cloud/network/guru/StorageNetworkGuru.java b/server/src/main/java/com/cloud/network/guru/StorageNetworkGuru.java index 0f199a751264..221661fea126 100644 --- a/server/src/main/java/com/cloud/network/guru/StorageNetworkGuru.java +++ b/server/src/main/java/com/cloud/network/guru/StorageNetworkGuru.java @@ -80,7 +80,7 @@ protected boolean canHandle(NetworkOffering offering) { } @Override - public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { + public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, String name, Long vpcId, Account owner) { if (!canHandle(offering)) { return null; } 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 220617341623..ce5024a5e1b4 100644 --- a/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java +++ b/server/src/main/java/com/cloud/network/router/CommandSetupHelper.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import javax.inject.Inject; @@ -790,7 +791,10 @@ public void createVpcAssociatePublicIPCommands(final VirtualRouter router, final // vlan1, then all ip addresses of vlan2, etc.. final Map> vlanIpMap = new HashMap>(); for (final PublicIpAddress ipAddress : ips) { - final String vlanTag = ipAddress.getVlanTag(); + String vlanTag = ipAddress.getVlanTag(); + if (Objects.isNull(vlanTag)) { + vlanTag = "nsx-"+ipAddress.getAddress().addr(); + } ArrayList ipList = vlanIpMap.get(vlanTag); if (ipList == null) { ipList = new ArrayList(); @@ -841,10 +845,18 @@ public int compare(final PublicIpAddress o1, final PublicIpAddress o2) { for (final PublicIpAddress ipAddr : ipAddrList) { final boolean add = ipAddr.getState() == IpAddress.State.Releasing ? false : true; + String vlanTag = ipAddr.getVlanTag(); + String key = null; + if (Objects.isNull(vlanTag)) { + key = "nsx-" + ipAddr.getAddress().addr(); + } else { + key = BroadcastDomainType.getValue(BroadcastDomainType.fromString(ipAddr.getVlanTag())); + } - final String macAddress = vlanMacAddress.get(BroadcastDomainType.getValue(BroadcastDomainType.fromString(ipAddr.getVlanTag()))); + final String macAddress = vlanMacAddress.get(key); - final IpAddressTO ip = new IpAddressTO(ipAddr.getAccountId(), ipAddr.getAddress().addr(), add, firstIP, ipAddr.isSourceNat(), BroadcastDomainType.fromString(ipAddr.getVlanTag()).toString(), ipAddr.getGateway(), + final IpAddressTO ip = new IpAddressTO(ipAddr.getAccountId(), ipAddr.getAddress().addr(), add, firstIP, ipAddr.isSourceNat(), + Objects.isNull(vlanTag) ? null : BroadcastDomainType.fromString(ipAddr.getVlanTag()).toString(), ipAddr.getGateway(), ipAddr.getNetmask(), macAddress, networkRate, ipAddr.isOneToOneNat()); setIpAddressNetworkParams(ip, network, router); if (network.getPublicMtu() != null) { @@ -1048,6 +1060,9 @@ private Map getVlanLastIpMap(Long vpcId, Long guestNetworkId) { } for (IPAddressVO ip : userIps) { String vlanTag = _vlanDao.findById(ip.getVlanId()).getVlanTag(); + if (Objects.isNull(vlanTag)) { + vlanTag = "nsx-" + ip.getAddress().addr(); + } Boolean lastIp = vlanLastIpMap.get(vlanTag); if (lastIp != null && !lastIp) { continue; @@ -1145,7 +1160,7 @@ public void createVpcAssociatePrivateIPCommands(final VirtualRouter router, fina public SetupGuestNetworkCommand createSetupGuestNetworkCommand(final DomainRouterVO router, final boolean add, final NicProfile guestNic) { final Network network = _networkModel.getNetwork(guestNic.getNetworkId()); - + final NetworkOfferingVO networkOfferingVO = _networkOfferingDao.findById(network.getNetworkOfferingId()); String defaultDns1 = null; String defaultDns2 = null; String defaultIp6Dns1 = null; @@ -1182,6 +1197,7 @@ public SetupGuestNetworkCommand createSetupGuestNetworkCommand(final DomainRoute final SetupGuestNetworkCommand setupCmd = new SetupGuestNetworkCommand(dhcpRange, networkDomain, router.getIsRedundantRouter(), defaultDns1, defaultDns2, add, _itMgr.toNicTO(nicProfile, router.getHypervisorType())); + setupCmd.setVrGuestGateway(networkOfferingVO.isForNsx()); NicVO publicNic = _nicDao.findDefaultNicForVM(router.getId()); if (publicNic != null) { updateSetupGuestNetworkCommandIpv6(setupCmd, network, publicNic, defaultIp6Dns1, defaultIp6Dns2); @@ -1345,7 +1361,8 @@ private void setIpAddressNetworkParams(IpAddressTO ipAddress, final Network netw nicTO.setMac(ipAddress.getVifMacAddress()); nicTO.setType(ipAddress.getTrafficType()); nicTO.setGateway(ipAddress.getVlanGateway()); - nicTO.setBroadcastUri(BroadcastDomainType.fromString(ipAddress.getBroadcastUri())); + URI broadcastUri = ipAddress.getBroadcastUri() != null ? BroadcastDomainType.fromString(ipAddress.getBroadcastUri()) : null; + nicTO.setBroadcastUri(broadcastUri); nicTO.setType(network.getTrafficType()); nicTO.setName(_networkModel.getNetworkTag(router.getHypervisorType(), network)); nicTO.setBroadcastType(network.getBroadcastDomainType()); diff --git a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java index 9c093e37ead4..1f4642bbd854 100644 --- a/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java +++ b/server/src/main/java/com/cloud/network/router/NetworkHelperImpl.java @@ -28,6 +28,8 @@ import javax.annotation.PostConstruct; import javax.inject.Inject; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.utils.validation.ChecksumUtil; import org.apache.cloudstack.api.ApiConstants; import org.apache.cloudstack.context.CallContext; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; @@ -102,7 +104,6 @@ import com.cloud.utils.Pair; import com.cloud.utils.exception.CloudRuntimeException; import com.cloud.utils.net.NetUtils; -import com.cloud.utils.validation.ChecksumUtil; import com.cloud.vm.DomainRouterVO; import com.cloud.vm.Nic; import com.cloud.vm.NicProfile; @@ -173,6 +174,8 @@ public class NetworkHelperImpl implements NetworkHelper { Ipv6Service ipv6Service; @Inject CapacityManager capacityMgr; + @Inject + VpcDao vpcDao; protected final Map> hypervisorsMap = new HashMap<>(); @@ -709,8 +712,8 @@ protected LinkedHashMap> configurePublicNic( defaultNic.setIsolationUri(BroadcastDomainType.Vxlan.toUri(sourceNatIp.getVlanTag())); } else { defaultNic.setBroadcastType(BroadcastDomainType.Vlan); - defaultNic.setBroadcastUri(BroadcastDomainType.Vlan.toUri(sourceNatIp.getVlanTag())); - defaultNic.setIsolationUri(IsolationType.Vlan.toUri(sourceNatIp.getVlanTag())); + defaultNic.setBroadcastUri(sourceNatIp.getVlanTag() != null ? BroadcastDomainType.Vlan.toUri(sourceNatIp.getVlanTag()) : null); + defaultNic.setIsolationUri(sourceNatIp.getVlanTag() != null ? IsolationType.Vlan.toUri(sourceNatIp.getVlanTag()) : null); } //If guest nic has already been added we will have 2 devices in the list. diff --git a/server/src/main/java/com/cloud/network/router/NicProfileHelperImpl.java b/server/src/main/java/com/cloud/network/router/NicProfileHelperImpl.java index 15c8a2b41556..399019db3e2c 100644 --- a/server/src/main/java/com/cloud/network/router/NicProfileHelperImpl.java +++ b/server/src/main/java/com/cloud/network/router/NicProfileHelperImpl.java @@ -21,6 +21,8 @@ import javax.inject.Inject; +import com.cloud.vm.NicVO; +import com.cloud.vm.VirtualMachine; import org.apache.cloudstack.network.router.deployment.RouterDeploymentDefinition; import com.cloud.network.IpAddressManager; @@ -118,7 +120,13 @@ public NicProfile createPrivateNicProfileForGateway(final VpcGateway privateGate public NicProfile createGuestNicProfileForVpcRouter(final RouterDeploymentDefinition vpcRouterDeploymentDefinition, final Network guestNetwork) { final NicProfile guestNic = new NicProfile(); - if (vpcRouterDeploymentDefinition.isRedundant()) { + if (BroadcastDomainType.NSX == guestNetwork.getBroadcastDomainType()) { + NicVO vrNic = _nicDao.findByNetworkIdAndTypeIncludingRemoved(guestNetwork.getId(), VirtualMachine.Type.DomainRouter); + if (vrNic != null) { + guestNic.setIPv4Address(vrNic.getIPv4Address()); + guestNic.setIPv4Gateway(vrNic.getIPv4Gateway()); + } + } else if (vpcRouterDeploymentDefinition.isRedundant()) { guestNic.setIPv4Address(this.acquireGuestIpAddressForVrouterRedundant(guestNetwork)); } else { guestNic.setIPv4Address(guestNetwork.getGateway()); diff --git a/server/src/main/java/com/cloud/network/router/VpcNetworkHelperImpl.java b/server/src/main/java/com/cloud/network/router/VpcNetworkHelperImpl.java index 8c7e45246152..fa2f2aba8fff 100644 --- a/server/src/main/java/com/cloud/network/router/VpcNetworkHelperImpl.java +++ b/server/src/main/java/com/cloud/network/router/VpcNetworkHelperImpl.java @@ -22,6 +22,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.TreeSet; @@ -84,8 +85,11 @@ public void reallocateRouterNetworks(final RouterDeploymentDefinition vpcRouterD final TreeSet publicVlans = new TreeSet(); if (vpcRouterDeploymentDefinition.isPublicNetwork()) { - publicVlans.add(vpcRouterDeploymentDefinition.getSourceNatIP() - .getVlanTag()); + String vlanTag = ""; + if (Objects.nonNull(vpcRouterDeploymentDefinition.getSourceNatIP().getVlanTag())) { + vlanTag = vpcRouterDeploymentDefinition.getSourceNatIP().getVlanTag(); + } + publicVlans.add(vlanTag); } //1) allocate nic for control and source nat public ip 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 69f7555696b1..b22343081fff 100644 --- a/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java +++ b/server/src/main/java/com/cloud/network/router/VpcVirtualNetworkApplianceManagerImpl.java @@ -23,10 +23,13 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Objects; import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.vpc.dao.VpcDao; import org.apache.commons.collections.CollectionUtils; import org.springframework.stereotype.Component; @@ -127,6 +130,10 @@ public class VpcVirtualNetworkApplianceManagerImpl extends VirtualNetworkApplian private EntityManager _entityMgr; @Inject protected HypervisorGuruManager _hvGuruMgr; + @Inject + protected NetworkDao networkDao; + @Inject + protected VpcDao vpcDao; @Override public boolean configure(final String name, final Map params) throws ConfigurationException { @@ -358,7 +365,12 @@ public boolean finalizeCommandsOnStart(final Commands cmds, final VirtualMachine } else if (network.getTrafficType() == TrafficType.Public) { final Pair publicNic = new Pair(routerNic, network); publicNics.add(publicNic); - final String vlanTag = BroadcastDomainType.getValue(routerNic.getBroadcastUri()); + String vlanTag = null; + if (Objects.nonNull(routerNic.getBroadcastUri())) { + vlanTag = BroadcastDomainType.getValue(routerNic.getBroadcastUri()); + } else { + vlanTag = "nsx-"+routerNic.getIPv4Address(); + } vlanMacAddress.put(vlanTag, routerNic.getMacAddress()); } } @@ -388,7 +400,8 @@ public boolean finalizeCommandsOnStart(final Commands cmds, final VirtualMachine _routerDao.update(routerVO.getId(), routerVO); } } - final PlugNicCommand plugNicCmd = new PlugNicCommand(_nwHelper.getNicTO(domainRouterVO, publicNic.getNetworkId(), publicNic.getBroadcastUri().toString()), + String broadcastURI = publicNic.getBroadcastUri() != null ? publicNic.getBroadcastUri().toString() : null; + final PlugNicCommand plugNicCmd = new PlugNicCommand(_nwHelper.getNicTO(domainRouterVO, publicNic.getNetworkId(), broadcastURI), domainRouterVO.getInstanceName(), domainRouterVO.getType(), details); cmds.addCommand(plugNicCmd); final VpcVO vpc = _vpcDao.findById(domainRouterVO.getVpcId()); diff --git a/server/src/main/java/com/cloud/network/vpc/NetworkACLManagerImpl.java b/server/src/main/java/com/cloud/network/vpc/NetworkACLManagerImpl.java index 047961467ac2..2f37bdda75a7 100644 --- a/server/src/main/java/com/cloud/network/vpc/NetworkACLManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/NetworkACLManagerImpl.java @@ -204,6 +204,7 @@ public boolean replaceNetworkACL(final NetworkACL acl, final NetworkVO network) final List aclItems = _networkACLItemDao.listByACL(acl.getId()); if (aclItems == null || aclItems.isEmpty()) { logger.debug("New network ACL is empty. Revoke existing rules before applying ACL"); + } else { if (!revokeACLItemsForNetwork(network.getId())) { throw new CloudRuntimeException("Failed to replace network ACL. Error while removing existing ACL items for network: " + network.getId()); } @@ -367,6 +368,20 @@ public boolean applyACLToPrivateGw(final PrivateGateway gateway) throws Resource return applyACLToPrivateGw(gateway, rules); } + @Override + public boolean reorderAclRules(VpcVO vpc, List networks, List networkACLItems) { + List nsxElements = new ArrayList<>(); + nsxElements.add((NetworkACLServiceProvider) _ntwkModel.getElementImplementingProvider(Network.Provider.Nsx.getName())); + try { + for (final NetworkACLServiceProvider provider : nsxElements) { + return provider.reorderAclRules(vpc, networks, networkACLItems); + } + } catch (final Exception ex) { + logger.debug("Failed to reorder ACLs on NSX due to: " + ex.getLocalizedMessage()); + } + return false; + } + private boolean applyACLToPrivateGw(final PrivateGateway gateway, final List rules) throws ResourceUnavailableException { List vpcElements = new ArrayList(); vpcElements.add((VpcProvider)_ntwkModel.getElementImplementingProvider(Network.Provider.VPCVirtualRouter.getName())); diff --git a/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java b/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java index 94b5ea91ce20..dd0dce5e10ff 100644 --- a/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/NetworkACLServiceImpl.java @@ -20,11 +20,16 @@ import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Locale; import java.util.Map; +import java.util.Objects; import javax.inject.Inject; +import com.cloud.dc.DataCenter; import com.cloud.exception.PermissionDeniedException; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.element.NsxProviderVO; import org.apache.cloudstack.api.ApiErrorCode; import org.apache.cloudstack.api.ServerApiException; import org.apache.cloudstack.api.command.user.network.CreateNetworkACLCmd; @@ -97,6 +102,8 @@ public class NetworkACLServiceImpl extends ManagerBase implements NetworkACLServ private VpcDao _vpcDao; @Inject private VpcService _vpcSvc; + @Inject + private NsxProviderDao nsxProviderDao; private String supportedProtocolsForAclRules = "tcp,udp,icmp,all"; @@ -336,6 +343,7 @@ public NetworkACLItem createNetworkACLItem(CreateNetworkACLCmd createNetworkACLC if (isGlobalAcl(acl.getVpcId()) && !Account.Type.ADMIN.equals(caller.getType())) { throw new PermissionDeniedException("Only Root Admins can create rules for a global ACL."); } + validateNsxConstraints(acl.getVpcId(), protocol, icmpType, icmpCode, sourcePortStart, sourcePortEnd); validateAclRuleNumber(createNetworkACLCmd, acl); NetworkACLItem.Action ruleAction = validateAndCreateNetworkAclRuleAction(action); @@ -426,6 +434,32 @@ protected void validateNetworkAcl(NetworkACL acl) { } } + private void validateNsxConstraints(long vpcId, String protocol, Integer icmpType, + Integer icmpCode, Integer sourcePortStart, Integer sourcePortEnd) { + VpcVO vpc = _vpcDao.findById(vpcId); + if (Objects.isNull(vpc)) { + return; + } + final DataCenter dc = _entityMgr.findById(DataCenter.class, vpc.getZoneId()); + final NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(dc.getId()); + if (Objects.isNull(nsxProvider)) { + return; + } + + if (NetUtils.ICMP_PROTO.equals(protocol.toLowerCase(Locale.ROOT)) && (icmpType == -1 || icmpCode == -1)) { + String errorMsg = "Passing -1 for ICMP type is not supported for NSX enabled zones"; + logger.error(errorMsg); + throw new InvalidParameterValueException(errorMsg); + } + + if (List.of(NetUtils.TCP_PROTO, NetUtils.UDP_PROTO).contains(protocol.toLowerCase(Locale.ROOT)) && + (Objects.isNull(sourcePortStart) || Objects.isNull(sourcePortEnd))) { + String errorMsg = "Source start and end ports are required to be passed"; + logger.error(errorMsg); + throw new InvalidParameterValueException(errorMsg); + } + } + /** * This methods will simply return the ACL rule list ID if it has been provided by the parameter 'createNetworkACLCmd'. * If no ACL rule List ID has been provided the method behave as follows: @@ -815,7 +849,8 @@ public NetworkACLItem updateNetworkACLItem(UpdateNetworkACLItemCmd updateNetwork NetworkACL acl = _networkAclMgr.getNetworkACL(networkACLItemVo.getAclId()); validateNetworkAcl(acl); - + validateNsxConstraints(acl.getVpcId(), networkACLItemVo.getProtocol(), networkACLItemVo.getIcmpType(), + networkACLItemVo.getIcmpCode(), networkACLItemVo.getSourcePortStart(), networkACLItemVo.getSourcePortEnd()); Account account = CallContext.current().getCallingAccount(); validateGlobalAclPermissionAndAclAssociatedToVpc(acl, account, "Only Root Admins can update global ACLs."); @@ -975,14 +1010,26 @@ public NetworkACLItem moveNetworkAclRuleToNewPosition(MoveNetworkAclItemCmd move NetworkACLVO lockedAcl = _networkACLDao.acquireInLockTable(ruleBeingMoved.getAclId()); List allAclRules = getAllAclRulesSortedByNumber(lockedAcl.getId()); validateAclConsistency(moveNetworkAclItemCmd, lockedAcl, allAclRules); - + NetworkACLItem networkACLItem = null; if (previousRule == null) { - return moveRuleToTheTop(ruleBeingMoved, allAclRules); + networkACLItem = moveRuleToTheTop(ruleBeingMoved, allAclRules); + } else if (nextRule == null) { + networkACLItem = moveRuleToTheBottom(ruleBeingMoved, allAclRules); + } else { + networkACLItem = moveRuleBetweenAclRules(ruleBeingMoved, allAclRules, previousRule, nextRule); + } + VpcVO vpc = _vpcDao.findById(lockedAcl.getVpcId()); + if (Objects.isNull(vpc)) { + return networkACLItem; } - if (nextRule == null) { - return moveRuleToTheBottom(ruleBeingMoved, allAclRules); + final DataCenter dc = _entityMgr.findById(DataCenter.class, vpc.getZoneId()); + final NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(dc.getId()); + List networks = _networkDao.listByAclId(lockedAcl.getId()); + if (Objects.nonNull(nsxProvider) && !networks.isEmpty()) { + allAclRules = getAllAclRulesSortedByNumber(lockedAcl.getId()); + _networkAclMgr.reorderAclRules(vpc, networks, allAclRules); } - return moveRuleBetweenAclRules(ruleBeingMoved, allAclRules, previousRule, nextRule); + return networkACLItem; } finally { _networkACLDao.releaseFromLockTable(ruleBeingMoved.getAclId()); } 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 e313b1bfdf04..755cf2bb05cf 100644 --- a/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java +++ b/server/src/main/java/com/cloud/network/vpc/VpcManagerImpl.java @@ -41,6 +41,8 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.network.nsx.NsxService; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.alert.AlertService; import org.apache.cloudstack.annotation.AnnotationService; @@ -63,6 +65,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.EnumUtils; import org.apache.commons.lang3.ObjectUtils; import org.jetbrains.annotations.Nullable; import org.springframework.beans.factory.annotation.Autowired; @@ -262,6 +265,8 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis @Autowired @Qualifier("networkHelper") protected NetworkHelper networkHelper; + @Inject + private NsxService nsxService; @Inject private VpcPrivateGatewayTransactionCallable vpcTxCallable; @@ -270,7 +275,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis private List vpcElements = null; private final List nonSupportedServices = Arrays.asList(Service.SecurityGroup, Service.Firewall); private final List supportedProviders = Arrays.asList(Provider.VPCVirtualRouter, Provider.NiciraNvp, Provider.InternalLbVm, Provider.Netscaler, - Provider.JuniperContrailVpcRouter, Provider.Ovs, Provider.BigSwitchBcf, Provider.ConfigDrive); + Provider.JuniperContrailVpcRouter, Provider.Ovs, Provider.BigSwitchBcf, Provider.ConfigDrive, Provider.Nsx); int _cleanupInterval; int _maxNetworks; @@ -324,7 +329,9 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { svcProviderMap.put(svc, defaultProviders); } } - createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap, true, State.Enabled, null, false, false, false); + createVpcOffering(VpcOffering.defaultVPCOfferingName, VpcOffering.defaultVPCOfferingName, svcProviderMap, + true, State.Enabled, null, false, + false, false, false, null); } // configure default vpc offering with Netscaler as LB Provider @@ -343,7 +350,7 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { svcProviderMap.put(svc, defaultProviders); } } - createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName, svcProviderMap, false, State.Enabled, null, false, false, false); + createVpcOffering(VpcOffering.defaultVPCNSOfferingName, VpcOffering.defaultVPCNSOfferingName, svcProviderMap, false, State.Enabled, null, false, false, false, false, null); } @@ -363,7 +370,44 @@ public void doInTransactionWithoutResult(final TransactionStatus status) { svcProviderMap.put(svc, defaultProviders); } } - createVpcOffering(VpcOffering.redundantVPCOfferingName, VpcOffering.redundantVPCOfferingName, svcProviderMap, true, State.Enabled, null, false, false, true); + createVpcOffering(VpcOffering.redundantVPCOfferingName, VpcOffering.redundantVPCOfferingName, svcProviderMap, true, State.Enabled, + null, false, false, true, false, null); + } + + // configure default vpc offering with NSX as network service provider in NAT mode + if (_vpcOffDao.findByUniqueName(VpcOffering.DEFAULT_VPC_NAT_NSX_OFFERING_NAME) == null) { + logger.debug("Creating default VPC offering with NSX as network service provider" + VpcOffering.DEFAULT_VPC_NAT_NSX_OFFERING_NAME); + final Map> svcProviderMap = new HashMap>(); + final Set defaultProviders = Set.of(Provider.Nsx); + for (final Service svc : getSupportedServices()) { + if (List.of(Service.UserData, Service.Dhcp, Service.Dns).contains(svc)) { + final Set userDataProvider = Set.of(Provider.VPCVirtualRouter); + svcProviderMap.put(svc, userDataProvider); + } else { + svcProviderMap.put(svc, defaultProviders); + } + } + createVpcOffering(VpcOffering.DEFAULT_VPC_NAT_NSX_OFFERING_NAME, VpcOffering.DEFAULT_VPC_NAT_NSX_OFFERING_NAME, svcProviderMap, false, + State.Enabled, null, false, false, false, true, NetworkOffering.NsxMode.NATTED.name()); + + } + + // configure default vpc offering with NSX as network service provider in Route mode + if (_vpcOffDao.findByUniqueName(VpcOffering.DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME) == null) { + logger.debug("Creating default VPC offering with NSX as network service provider" + VpcOffering.DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME); + final Map> svcProviderMap = new HashMap<>(); + final Set defaultProviders = Set.of(Provider.Nsx); + for (final Service svc : getSupportedServices()) { + if (List.of(Service.UserData, Service.Dhcp, Service.Dns).contains(svc)) { + final Set userDataProvider = Set.of(Provider.VPCVirtualRouter); + svcProviderMap.put(svc, userDataProvider); + } else if (List.of(Service.SourceNat, Service.NetworkACL).contains(svc)){ + svcProviderMap.put(svc, defaultProviders); + } + } + createVpcOffering(VpcOffering.DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME, VpcOffering.DEFAULT_VPC_ROUTE_NSX_OFFERING_NAME, svcProviderMap, false, + State.Enabled, null, false, false, false, true, NetworkOffering.NsxMode.ROUTED.name()); + } } }); @@ -422,7 +466,11 @@ public VpcOffering createVpcOffering(CreateVPCOfferingCmd cmd) { final Long serviceOfferingId = cmd.getServiceOfferingId(); final List domainIds = cmd.getDomainIds(); final List zoneIds = cmd.getZoneIds(); + final Boolean forNsx = cmd.isForNsx(); + String nsxMode = cmd.getNsxMode(); final boolean enable = cmd.getEnable(); + nsxMode = validateNsxMode(forNsx, nsxMode); + // check if valid domain if (CollectionUtils.isNotEmpty(cmd.getDomainIds())) { for (final Long domainId: cmd.getDomainIds()) { @@ -445,14 +493,34 @@ public VpcOffering createVpcOffering(CreateVPCOfferingCmd cmd) { } return createVpcOffering(vpcOfferingName, displayText, supportedServices, - serviceProviderList, serviceCapabilityList, internetProtocol, serviceOfferingId, + serviceProviderList, serviceCapabilityList, internetProtocol, serviceOfferingId, forNsx, nsxMode, domainIds, zoneIds, (enable ? State.Enabled : State.Disabled)); } + private String validateNsxMode(Boolean forNsx, String nsxMode) { + if (Boolean.TRUE.equals(forNsx)) { + if (Objects.isNull(nsxMode)) { + throw new InvalidParameterValueException("Mode for an NSX offering needs to be specified.Valid values: " + Arrays.toString(NetworkOffering.NsxMode.values())); + } + if (!EnumUtils.isValidEnum(NetworkOffering.NsxMode.class, nsxMode)) { + throw new InvalidParameterValueException("Invalid mode passed. Valid values: " + Arrays.toString(NetworkOffering.NsxMode.values())); + } + } else { + if (Objects.nonNull(nsxMode)) { + if (logger.isTraceEnabled()) { + logger.trace("nsxMode has is ignored for non-NSX enabled zones"); + } + nsxMode = null; + } + } + return nsxMode; + } + @Override @ActionEvent(eventType = EventTypes.EVENT_VPC_OFFERING_CREATE, eventDescription = "creating vpc offering", create = true) public VpcOffering createVpcOffering(final String name, final String displayText, final List supportedServices, final Map> serviceProviders, - final Map serviceCapabilityList, final NetUtils.InternetProtocol internetProtocol, final Long serviceOfferingId, List domainIds, List zoneIds, State state) { + final Map serviceCapabilityList, final NetUtils.InternetProtocol internetProtocol, final Long serviceOfferingId, + final Boolean forNsx, final String mode, List domainIds, List zoneIds, State state) { if (!Ipv6Service.Ipv6OfferingCreationEnabled.value() && !(internetProtocol == null || NetUtils.InternetProtocol.IPv4.equals(internetProtocol))) { throw new InvalidParameterValueException(String.format("Configuration %s needs to be enabled for creating IPv6 supported VPC offering", Ipv6Service.Ipv6OfferingCreationEnabled.key())); @@ -537,7 +605,7 @@ public VpcOffering createVpcOffering(final String name, final String displayText final boolean offersRegionLevelVPC = isVpcOfferingForRegionLevelVpc(serviceCapabilityList); final boolean redundantRouter = isVpcOfferingRedundantRouter(serviceCapabilityList); final VpcOfferingVO offering = createVpcOffering(name, displayText, svcProviderMap, false, state, serviceOfferingId, supportsDistributedRouter, offersRegionLevelVPC, - redundantRouter); + redundantRouter, forNsx, mode); if (offering != null) { List detailsVO = new ArrayList<>(); @@ -565,7 +633,7 @@ public VpcOffering createVpcOffering(final String name, final String displayText @DB protected VpcOfferingVO createVpcOffering(final String name, final String displayText, final Map> svcProviderMap, final boolean isDefault, final State state, final Long serviceOfferingId, final boolean supportsDistributedRouter, final boolean offersRegionLevelVPC, - final boolean redundantRouter) { + final boolean redundantRouter, Boolean forNsx, String mode) { return Transaction.execute(new TransactionCallback() { @Override @@ -576,6 +644,8 @@ public VpcOfferingVO doInTransaction(final TransactionStatus status) { if (state != null) { offering.setState(state); } + offering.setForNsx(forNsx); + offering.setNsxMode(mode); logger.debug("Adding vpc offering " + offering); offering = _vpcOffDao.persist(offering); // populate services and providers @@ -1092,8 +1162,8 @@ public Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwner final boolean useDistributedRouter = vpcOff.isSupportsDistributedRouter(); final VpcVO vpc = new VpcVO(zoneId, vpcName, displayText, owner.getId(), owner.getDomainId(), vpcOffId, cidr, networkDomain, useDistributedRouter, isRegionLevelVpcOff, vpcOff.isRedundantRouter(), ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2); - vpc.setPublicMtu(publicMtu); - vpc.setDisplay(Boolean.TRUE.equals(displayVpc)); + vpc.setPublicMtu(publicMtu); + vpc.setDisplay(Boolean.TRUE.equals(displayVpc)); return createVpc(displayVpc, vpc); } @@ -1106,18 +1176,38 @@ public Vpc createVpc(CreateVPCCmd cmd) throws ResourceAllocationException { cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu()); String sourceNatIP = cmd.getSourceNatIP(); - if (sourceNatIP != null) { + boolean forNsx = isVpcForNsx(vpc); + if (sourceNatIP != null || forNsx) { + if (forNsx) { + logger.info("Provided source NAT IP will be ignored in an NSX-enabled zone"); + sourceNatIP = null; + } logger.info(String.format("Trying to allocate the specified IP [%s] as the source NAT of VPC [%s].", sourceNatIP, vpc)); allocateSourceNatIp(vpc, sourceNatIP); } return vpc; } + private boolean isVpcForNsx(Vpc vpc) { + if (vpc == null) { + return false; + } + VpcOfferingServiceMapVO mapVO = _vpcOffSvcMapDao.findByServiceProviderAndOfferingId(Service.SourceNat.getName(), Provider.Nsx.getName(), vpc.getVpcOfferingId()); + if (mapVO != null) { + logger.debug(String.format("The VPC %s is NSX-based and supports the %s service", vpc.getName(), Service.SourceNat.getName())); + } + return mapVO != null; + } + private void allocateSourceNatIp(Vpc vpc, String sourceNatIP) { Account account = _accountMgr.getAccount(vpc.getAccountId()); DataCenter zone = _dcDao.findById(vpc.getZoneId()); // reserve this ip and then try { + if (isVpcForNsx(vpc) && org.apache.commons.lang3.StringUtils.isBlank(sourceNatIP)) { + logger.debug(String.format("Reserving a source NAT IP for NSX VPC %s", vpc.getName())); + sourceNatIP = reserveSourceNatIpForNsxVpc(account, zone); + } IpAddress ip = _ipAddrMgr.allocateIp(account, false, CallContext.current().getCallingAccount(), CallContext.current().getCallingUserId(), zone, null, sourceNatIP); this.associateIPToVpc(ip.getId(), vpc.getId()); } catch (ResourceAllocationException | ResourceUnavailableException | InsufficientAddressCapacityException e){ @@ -1125,6 +1215,11 @@ private void allocateSourceNatIp(Vpc vpc, String sourceNatIP) { } } + private String reserveSourceNatIpForNsxVpc(Account account, DataCenter zone) throws ResourceAllocationException { + IpAddress ipAddress = _ntwkSvc.reserveIpAddressWithVlanDetail(account, zone, true, ApiConstants.NSX_DETAIL_KEY); + return ipAddress.getAddress().addr(); + } + @DB protected Vpc createVpc(final Boolean displayVpc, final VpcVO vpc) { final String cidr = vpc.getCidr(); @@ -1134,7 +1229,7 @@ protected Vpc createVpc(final Boolean displayVpc, final VpcVO vpc) { } // cidr has to be RFC 1918 complient - if (!NetUtils.validateGuestCidr(cidr)) { + if (!NetUtils.validateGuestCidr(cidr, !ConfigurationManager.AllowNonRFC1918CompliantIPs.value())) { throw new InvalidParameterValueException("Guest Cidr " + cidr + " is not RFC1918 compliant"); } @@ -1318,6 +1413,11 @@ public Vpc updateVpc(final long vpcId, final String vpcName, final String displa } } return vpcDao.findById(vpcId); + } else if (isVpcForNsx(vpcToUpdate)) { + if (logger.isDebugEnabled()) { + logger.debug("no restart needed."); + } + return vpcDao.findById(vpcId); } else { logger.error(String.format("failed to update vpc %s/%s",vpc.getName(), vpc.getUuid())); return null; @@ -1332,6 +1432,11 @@ private boolean checkAndUpdateRouterSourceNatIp(Vpc vpc, String sourceNatIp) { if (! userIps.isEmpty()) { try { _ipAddrMgr.updateSourceNatIpAddress(requestedIp, userIps); + if (isVpcForNsx(vpc)) { + nsxService.updateVpcSourceNatIp(vpc, requestedIp); + // The NSX source NAT IP change does not require to update the VPC VR + return false; + } } catch (Exception e) { // pokemon exception from transaction String msg = String.format("Update of source NAT ip to %s for network \"%s\"/%s failed due to %s", requestedIp.getAddress().addr(), vpc.getName(), vpc.getUuid(), e.getLocalizedMessage()); @@ -1775,7 +1880,7 @@ public void validateNtwkOffForNtwkInVpc(final Long networkId, final long newNtwk // 5) When aclId is provided, verify that ACLProvider is supported by // network offering - if (aclId != null && !_ntwkModel.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.NetworkACL)) { + if (aclId != null && !_ntwkModel.areServicesSupportedByNetworkOffering(guestNtwkOff.getId(), Service.NetworkACL) && !guestNtwkOff.isForNsx()) { throw new InvalidParameterValueException("Cannot apply NetworkACL. Network Offering does not support NetworkACL service"); } @@ -1793,7 +1898,7 @@ public void validateNtwkOffForVpc(final NetworkOffering guestNtwkOff, final List // 2) Only Isolated networks with Source nat service enabled can be // added to vpc - if (!(guestNtwkOff.getGuestType() == GuestType.Isolated && supportedSvcs.contains(Service.SourceNat))) { + if (!guestNtwkOff.isForNsx() && !(guestNtwkOff.getGuestType() == GuestType.Isolated && supportedSvcs.contains(Service.SourceNat))) { throw new InvalidParameterValueException("Only network offerings of type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " are valid for vpc "); @@ -1804,10 +1909,10 @@ public void validateNtwkOffForVpc(final NetworkOffering guestNtwkOff, final List * TODO This should have never been hardcoded like this in the first * place if (guestNtwkOff.getRedundantRouter()) { throw new * InvalidParameterValueException - * ("No redunant router support when network belnogs to VPC"); } + * ("No redundant router support when network belongs to VPC"); } */ - // 4) Conserve mode should be off in older versions + // 4) Conserve mode should be off in older versions ( < 4.19.0.0) if (guestNtwkOff.isConserveMode()) { logger.info("Creating a network with conserve mode in VPC"); } @@ -3087,7 +3192,8 @@ public boolean applyStaticRoute(final long routeId) throws ResourceUnavailableEx @Override public boolean isSrcNatIpRequired(long vpcOfferingId) { final Map> vpcOffSvcProvidersMap = getVpcOffSvcProvidersMap(vpcOfferingId); - return vpcOffSvcProvidersMap.get(Network.Service.SourceNat).contains(Network.Provider.VPCVirtualRouter); + return Objects.nonNull(vpcOffSvcProvidersMap.get(Network.Service.SourceNat)) && (vpcOffSvcProvidersMap.get(Network.Service.SourceNat).contains(Network.Provider.VPCVirtualRouter) || + vpcOffSvcProvidersMap.get(Service.SourceNat).contains(Provider.Nsx)); } /** diff --git a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java index a2a2ffe19290..237e3a5585ec 100644 --- a/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java +++ b/server/src/main/java/com/cloud/resourcelimit/CheckedReservation.java @@ -62,13 +62,32 @@ public static String getResourceReservationContextParameterKey(final ResourceTyp return String.format("%s-%s", ResourceReservation.class.getSimpleName(), type.getName()); } - protected void checkLimitAndPersistReservation(Account account, ResourceType resourceType, String tag, Long amount) throws ResourceAllocationException { - resourceLimitService.checkResourceLimitWithTag(account, resourceType, tag, amount); + protected void checkLimitAndPersistReservations(Account account, ResourceType resourceType, Long resourceId, List resourceLimitTags, Long amount) throws ResourceAllocationException { + checkLimitAndPersistReservation(account, resourceType, resourceId, null, amount); + if (CollectionUtils.isNotEmpty(resourceLimitTags)) { + for (String tag : resourceLimitTags) { + checkLimitAndPersistReservation(account, resourceType, resourceId, tag, amount); + } + } + } + + protected void checkLimitAndPersistReservation(Account account, ResourceType resourceType, Long resourceId, String tag, Long amount) throws ResourceAllocationException { + if (amount > 0) { + resourceLimitService.checkResourceLimitWithTag(account, resourceType, tag, amount); + } ReservationVO reservationVO = new ReservationVO(account.getAccountId(), account.getDomainId(), resourceType, tag, amount); + if (resourceId != null) { + reservationVO.setResourceId(resourceId); + } ResourceReservation reservation = reservationDao.persist(reservationVO); this.reservations.add(reservation); } + public CheckedReservation(Account account, ResourceType resourceType, List resourceLimitTags, Long amount, + ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { + this(account, resourceType, null, resourceLimitTags, amount, reservationDao, resourceLimitService); + } + /** * - check if adding a reservation is allowed * - create DB entry for reservation @@ -77,8 +96,8 @@ protected void checkLimitAndPersistReservation(Account account, ResourceType res * @param amount positive number of the resource type to reserve * @throws ResourceAllocationException */ - public CheckedReservation(Account account, ResourceType resourceType, List resourceLimitTags, Long amount, - ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { + public CheckedReservation(Account account, ResourceType resourceType, Long resourceId, List resourceLimitTags, Long amount, + ReservationDao reservationDao, ResourceLimitService resourceLimitService) throws ResourceAllocationException { this.reservationDao = reservationDao; this.resourceLimitService = resourceLimitService; this.account = account; @@ -86,36 +105,28 @@ public CheckedReservation(Account account, ResourceType resourceType, List(); this.resourceLimitTags = resourceLimitTags; - setGlobalLock(); - if (this.amount != null && this.amount <= 0) { - if(logger.isDebugEnabled()){ - logger.debug(String.format("not reserving no amount of resources for %s in domain %d, type: %s, %s ", account.getAccountName(), account.getDomainId(), resourceType, amount)); - } - this.amount = null; - } - if (this.amount != null) { - if(quotaLimitLock.lock(TRY_TO_GET_LOCK_TIME)) { - try { - checkLimitAndPersistReservation(account, resourceType, null, amount); - if (CollectionUtils.isNotEmpty(resourceLimitTags)) { - for (String tag: resourceLimitTags) { - checkLimitAndPersistReservation(account, resourceType, tag, amount); - } + if (this.amount != null && this.amount != 0) { + if (amount > 0) { + setGlobalLock(); + if (quotaLimitLock.lock(TRY_TO_GET_LOCK_TIME)) { + try { + checkLimitAndPersistReservations(account, resourceType, resourceId, resourceLimitTags, amount); + CallContext.current().putContextParameter(getContextParameterKey(), getIds()); + } catch (NullPointerException npe) { + throw new CloudRuntimeException("not enough means to check limits", npe); + } finally { + quotaLimitLock.unlock(); } - CallContext.current().putContextParameter(getContextParameterKey(), getIds()); - } catch (NullPointerException npe) { - throw new CloudRuntimeException("not enough means to check limits", npe); - } finally { - quotaLimitLock.unlock(); + } else { + throw new ResourceAllocationException(String.format("unable to acquire resource reservation \"%s\"", quotaLimitLock.getName()), resourceType); } } else { - throw new ResourceAllocationException(String.format("unable to acquire resource reservation \"%s\"", quotaLimitLock.getName()), resourceType); + checkLimitAndPersistReservations(account, resourceType, resourceId, resourceLimitTags, amount); } } else { - if(logger.isDebugEnabled()) { - logger.debug(String.format("not reserving no amount of resources for %s in domain %d, type: %s, tag: %s", account.getAccountName(), account.getDomainId(), resourceType, getResourceLimitTagsAsString())); - } + logger.debug("not reserving any amount of resources for {} in domain {}, type: {}, tag: {}", + account.getAccountName(), account.getDomainId(), resourceType, getResourceLimitTagsAsString()); } } diff --git a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java index e61d52f2f0c1..6181c4059e6d 100644 --- a/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java +++ b/server/src/main/java/com/cloud/resourcelimit/ResourceLimitManagerImpl.java @@ -45,6 +45,7 @@ import org.apache.cloudstack.framework.config.Configurable; import org.apache.cloudstack.framework.config.dao.ConfigurationDao; import org.apache.cloudstack.managed.context.ManagedContextRunnable; +import org.apache.cloudstack.reservation.ReservationVO; import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreDao; import org.apache.cloudstack.storage.datastore.db.SnapshotDataStoreVO; @@ -1273,9 +1274,11 @@ protected List getVmsWithAccountAndTag(long accountId, String tag) if (CollectionUtils.isEmpty(offerings) && CollectionUtils.isEmpty(templates)) { return new ArrayList<>(); } - return _userVmJoinDao.listByAccountServiceOfferingTemplateAndNotInState(accountId, states, + + return _userVmJoinDao.listByAccountServiceOfferingTemplateAndNotInState(accountId, states, offerings.stream().map(ServiceOfferingVO::getId).collect(Collectors.toList()), - templates.stream().map(VMTemplateVO::getId).collect(Collectors.toList())); + templates.stream().map(VMTemplateVO::getId).collect(Collectors.toList()) + ); } protected List getVmsWithAccount(long accountId) { @@ -1293,12 +1296,26 @@ protected List getVolumesWithAccountAndTag(long accountId, String tag) vrIds); } + private long calculateReservedResources(List vms, long accountId, ResourceType type, String tag) { + Set vmIds = vms.stream().map(UserVmJoinVO::getId).collect(Collectors.toSet()); + List reservations = reservationDao.getReservationsForAccount(accountId, type, tag); + long reserved = 0; + for (ReservationVO reservation : reservations) { + if (vmIds.contains(reservation.getResourceId()) ? reservation.getReservedAmount() > 0 : reservation.getReservedAmount() < 0) { + reserved += reservation.getReservedAmount(); + } + } + return reserved; + } + protected long calculateVmCountForAccount(long accountId, String tag) { if (StringUtils.isEmpty(tag)) { return _userVmDao.countAllocatedVMsForAccount(accountId, VirtualMachineManager.ResourceCountRunningVMsonly.value()); } + List vms = getVmsWithAccountAndTag(accountId, tag); - return vms.size(); + long reservedVMs = calculateReservedResources(vms, accountId, ResourceType.user_vm, tag); + return vms.size() - reservedVMs; } protected long calculateVolumeCountForAccount(long accountId, String tag) { @@ -1316,10 +1333,12 @@ protected long calculateVmCpuCountForAccount(long accountId, String tag) { } long cputotal = 0; List vms = getVmsWithAccountAndTag(accountId, tag); + for (UserVmJoinVO vm : vms) { cputotal += vm.getCpu(); } - return cputotal; + long reservedCpus = calculateReservedResources(vms, accountId, ResourceType.cpu, tag); + return cputotal - reservedCpus; } protected long calculateVmMemoryCountForAccount(long accountId, String tag) { @@ -1328,10 +1347,12 @@ protected long calculateVmMemoryCountForAccount(long accountId, String tag) { } long memory = 0; List vms = getVmsWithAccountAndTag(accountId, tag); + for (UserVmJoinVO vm : vms) { memory += vm.getRamSize(); } - return memory; + long reservedMemory = calculateReservedResources(vms, accountId, ResourceType.memory, tag); + return memory - reservedMemory; } public long countCpusForAccount(long accountId) { @@ -1340,7 +1361,8 @@ public long countCpusForAccount(long accountId) { for (UserVmJoinVO vm : userVms) { cputotal += vm.getCpu(); } - return cputotal; + long reservedCpuTotal = calculateReservedResources(userVms, accountId, ResourceType.cpu, null); + return cputotal - reservedCpuTotal; } public long calculateMemoryForAccount(long accountId) { @@ -1349,7 +1371,8 @@ public long calculateMemoryForAccount(long accountId) { for (UserVmJoinVO vm : userVms) { ramtotal += vm.getRamSize(); } - return ramtotal; + long reservedRamTotal = calculateReservedResources(userVms, accountId, ResourceType.memory, null); + return ramtotal - reservedRamTotal; } public long calculateSecondaryStorageForAccount(long accountId) { @@ -1625,32 +1648,44 @@ public void checkVolumeResourceLimit(Account owner, Boolean display, Long size, } } + @DB @Override public void incrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) { - List tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering); - if (CollectionUtils.isEmpty(tags)) { - return; - } - for (String tag : tags) { - incrementResourceCountWithTag(accountId, ResourceType.volume, tag); - if (size != null) { - incrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, size); + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + List tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering); + if (CollectionUtils.isEmpty(tags)) { + return; + } + for (String tag : tags) { + incrementResourceCountWithTag(accountId, ResourceType.volume, tag); + if (size != null) { + incrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, size); + } + } } - } + }); } + @DB @Override public void decrementVolumeResourceCount(long accountId, Boolean display, Long size, DiskOffering diskOffering) { - List tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering); - if (CollectionUtils.isEmpty(tags)) { - return; - } - for (String tag : tags) { - decrementResourceCountWithTag(accountId, ResourceType.volume, tag); - if (size != null) { - decrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, size); + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + List tags = getResourceLimitStorageTagsForResourceCountOperation(display, diskOffering); + if (CollectionUtils.isEmpty(tags)) { + return; + } + for (String tag : tags) { + decrementResourceCountWithTag(accountId, ResourceType.volume, tag); + if (size != null) { + decrementResourceCountWithTag(accountId, ResourceType.primary_storage, tag, size); + } + } } - } + }); } @Override @@ -1711,32 +1746,43 @@ public void checkVmResourceLimit(Account owner, Boolean display, ServiceOffering @Override public void incrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) { - List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); - if (CollectionUtils.isEmpty(tags)) { - return; - } - Long cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L; - Long ram = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L; - for (String tag : tags) { - incrementResourceCountWithTag(accountId, ResourceType.user_vm, tag); - incrementResourceCountWithTag(accountId, ResourceType.cpu, tag, cpu); - incrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram); - } + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); + if (CollectionUtils.isEmpty(tags)) { + return; + } + Long cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L; + Long ram = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L; + for (String tag : tags) { + incrementResourceCountWithTag(accountId, ResourceType.user_vm, tag); + incrementResourceCountWithTag(accountId, ResourceType.cpu, tag, cpu); + incrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram); + } + } + }); } @Override - public void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, VirtualMachineTemplate template) { - List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); - if (CollectionUtils.isEmpty(tags)) { - return; - } - Long cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L; - Long ram = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L; - for (String tag : tags) { - decrementResourceCountWithTag(accountId, ResourceType.user_vm, tag); - decrementResourceCountWithTag(accountId, ResourceType.cpu, tag, cpu); - decrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram); - } + public void decrementVmResourceCount(long accountId, Boolean display, ServiceOffering serviceOffering, + VirtualMachineTemplate template) { + Transaction.execute(new TransactionCallbackNoReturn() { + @Override + public void doInTransactionWithoutResult(TransactionStatus status) { + List tags = getResourceLimitHostTagsForResourceCountOperation(display, serviceOffering, template); + if (CollectionUtils.isEmpty(tags)) { + return; + } + Long cpu = serviceOffering.getCpu() != null ? Long.valueOf(serviceOffering.getCpu()) : 0L; + Long ram = serviceOffering.getRamSize() != null ? Long.valueOf(serviceOffering.getRamSize()) : 0L; + for (String tag : tags) { + decrementResourceCountWithTag(accountId, ResourceType.user_vm, tag); + decrementResourceCountWithTag(accountId, ResourceType.cpu, tag, cpu); + decrementResourceCountWithTag(accountId, ResourceType.memory, tag, ram); + } + } + }); } @Override diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java index 0ccb6c84fb83..81071db38108 100644 --- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java @@ -1198,10 +1198,72 @@ public void doInTransactionWithoutResult(TransactionStatus status) { } _networkOfferingDao.persistDefaultL2NetworkOfferings(); + + // Offering #9 - network offering for NSX provider - NATTED mode + createAndPersistDefaultNsxOffering(NetworkOffering.DEFAULT_NAT_NSX_OFFERING, "Offering for NSX enabled networks - NAT mode", + NetworkOffering.NsxMode.NATTED, false, true); + + // Offering #10 - network offering for NSX provider - ROUTED mode + createAndPersistDefaultNsxOffering(NetworkOffering.DEFAULT_ROUTED_NSX_OFFERING, "Offering for NSX enabled networks - ROUTED mode", + NetworkOffering.NsxMode.ROUTED, false, true); + + // Offering #11 - network offering for NSX provider for VPCs - NATTED mode + createAndPersistDefaultNsxOffering(NetworkOffering.DEFAULT_NAT_NSX_OFFERING_FOR_VPC, "Offering for NSX enabled networks on VPCs - NAT mode", + NetworkOffering.NsxMode.NATTED, true, true); + + // Offering #12 - network offering for NSX provider for VPCs - ROUTED mode + createAndPersistDefaultNsxOffering(NetworkOffering.DEFAULT_ROUTED_NSX_OFFERING_FOR_VPC, "Offering for NSX enabled networks on VPCs - ROUTED mode", + NetworkOffering.NsxMode.ROUTED, true, true); + + // Offering #13 - network offering for NSX provider for VPCs with Internal LB - NATTED mode + createAndPersistDefaultNsxOffering(NetworkOffering.DEFAULT_NAT_NSX_OFFERING_FOR_VPC_WITH_ILB, "Offering for NSX enabled networks on VPCs with internal LB - NAT mode", + NetworkOffering.NsxMode.NATTED, true, false); } }); } + private void createAndPersistDefaultNsxOffering(String name, String displayText, NetworkOffering.NsxMode nsxMode, + boolean forVpc, boolean publicLB) { + NetworkOfferingVO defaultNatNSXNetworkOffering = + new NetworkOfferingVO(name, displayText, TrafficType.Guest, false, false, null, + null, true, Availability.Optional, null, GuestType.Isolated, false, + false, false, false, false, forVpc); + defaultNatNSXNetworkOffering.setPublicLb(publicLB); + defaultNatNSXNetworkOffering.setInternalLb(!publicLB); + defaultNatNSXNetworkOffering.setForNsx(true); + defaultNatNSXNetworkOffering.setNsxMode(nsxMode.name()); + defaultNatNSXNetworkOffering.setState(NetworkOffering.State.Enabled); + defaultNatNSXNetworkOffering = _networkOfferingDao.persistDefaultNetworkOffering(defaultNatNSXNetworkOffering); + + Map serviceProviderMap = getServicesAndProvidersForNSXNetwork(nsxMode, forVpc, publicLB); + for (Map.Entry service : serviceProviderMap.entrySet()) { + NetworkOfferingServiceMapVO offService = + new NetworkOfferingServiceMapVO(defaultNatNSXNetworkOffering.getId(), service.getKey(), service.getValue()); + _ntwkOfferingServiceMapDao.persist(offService); + logger.trace("Added service for the network offering: " + offService); + } + } + + private Map getServicesAndProvidersForNSXNetwork(NetworkOffering.NsxMode nsxMode, boolean forVpc, boolean publicLB) { + final Map serviceProviderMap = new HashMap<>(); + Provider routerProvider = forVpc ? Provider.VPCVirtualRouter : Provider.VirtualRouter; + serviceProviderMap.put(Service.Dhcp, routerProvider); + serviceProviderMap.put(Service.Dns, routerProvider); + serviceProviderMap.put(Service.UserData, routerProvider); + if (forVpc) { + serviceProviderMap.put(Service.NetworkACL, Provider.Nsx); + } else { + serviceProviderMap.put(Service.Firewall, Provider.Nsx); + } + if (nsxMode == NetworkOffering.NsxMode.NATTED) { + serviceProviderMap.put(Service.SourceNat, Provider.Nsx); + serviceProviderMap.put(Service.StaticNat, Provider.Nsx); + serviceProviderMap.put(Service.PortForwarding, Provider.Nsx); + serviceProviderMap.put(Service.Lb, Provider.Nsx); + } + return serviceProviderMap; + } + private void createDefaultNetworks() { List zones = _dataCenterDao.listAll(); long id = 1; diff --git a/server/src/main/java/com/cloud/server/ManagementServerImpl.java b/server/src/main/java/com/cloud/server/ManagementServerImpl.java index 0406ba04f8ce..705e86f71869 100644 --- a/server/src/main/java/com/cloud/server/ManagementServerImpl.java +++ b/server/src/main/java/com/cloud/server/ManagementServerImpl.java @@ -839,6 +839,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServer, Configurable { protected StateMachine2 _stateMachine; + static final String FOR_SYSTEMVMS = "forsystemvms"; static final ConfigKey vmPasswordLength = new ConfigKey("Advanced", Integer.class, "vm.password.length", "6", "Specifies the length of a randomly generated password", false); static final ConfigKey sshKeyLength = new ConfigKey("Advanced", Integer.class, "ssh.key.length", "2048", "Specifies custom SSH key length (bit)", true, ConfigKey.Scope.Global); static final ConfigKey humanReadableSizes = new ConfigKey("Advanced", Boolean.class, "display.human.readable.sizes", "true", "Enables outputting human readable byte sizes to logs and usage records.", false, ConfigKey.Scope.Global); @@ -2568,7 +2569,7 @@ private void buildParameters(final SearchBuilder sb, final ListPubl sb.and("vpcId", sb.entity().getVpcId(), SearchCriteria.Op.EQ); sb.and("state", sb.entity().getState(), SearchCriteria.Op.EQ); sb.and("display", sb.entity().isDisplay(), SearchCriteria.Op.EQ); - sb.and("forsystemvms", sb.entity().isForSystemVms(), SearchCriteria.Op.EQ); + sb.and(FOR_SYSTEMVMS, sb.entity().isForSystemVms(), SearchCriteria.Op.EQ); if (forLoadBalancing != null && forLoadBalancing) { final SearchBuilder lbSearch = _loadbalancerDao.createSearchBuilder(); @@ -2614,6 +2615,7 @@ protected void setParameters(SearchCriteria sc, final ListPublicIpA final Boolean staticNat = cmd.isStaticNat(); final Boolean forDisplay = cmd.getDisplay(); final String state = cmd.getState(); + final Boolean forSystemVms = cmd.getForSystemVMs(); final Map tags = cmd.getTags(); sc.setJoinParameters("vlanSearch", "vlanType", vlanType); @@ -2675,7 +2677,9 @@ protected void setParameters(SearchCriteria sc, final ListPublicIpA } if (IpAddressManagerImpl.getSystemvmpublicipreservationmodestrictness().value() && IpAddress.State.Free.name().equalsIgnoreCase(state)) { - sc.setParameters("forsystemvms", false); + sc.setParameters(FOR_SYSTEMVMS, false); + } else { + sc.setParameters(FOR_SYSTEMVMS, forSystemVms); } } diff --git a/server/src/main/java/com/cloud/usage/UsageServiceImpl.java b/server/src/main/java/com/cloud/usage/UsageServiceImpl.java index 6d6a05cac19e..3398e3ba571b 100644 --- a/server/src/main/java/com/cloud/usage/UsageServiceImpl.java +++ b/server/src/main/java/com/cloud/usage/UsageServiceImpl.java @@ -27,6 +27,7 @@ import javax.naming.ConfigurationException; import com.cloud.domain.Domain; +import com.cloud.utils.DateUtil; import org.apache.cloudstack.api.command.admin.usage.GenerateUsageRecordsCmd; import org.apache.cloudstack.api.command.admin.usage.ListUsageRecordsCmd; import org.apache.cloudstack.api.command.admin.usage.RemoveRawUsageRecordsCmd; @@ -36,6 +37,7 @@ import org.apache.cloudstack.usage.Usage; import org.apache.cloudstack.usage.UsageService; import org.apache.cloudstack.usage.UsageTypes; +import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.springframework.stereotype.Component; @@ -97,7 +99,7 @@ public class UsageServiceImpl extends ManagerBase implements UsageService, Manag private ConfigurationDao _configDao; @Inject private ProjectManager _projectMgr; - private TimeZone _usageTimezone; + private TimeZone _usageTimezone = TimeZone.getTimeZone("GMT"); @Inject private AccountService _accountService; @Inject @@ -127,10 +129,7 @@ public UsageServiceImpl() { @Override public boolean configure(String name, Map params) throws ConfigurationException { super.configure(name, params); - String timeZoneStr = _configDao.getValue(Config.UsageAggregationTimezone.toString()); - if (timeZoneStr == null) { - timeZoneStr = "GMT"; - } + String timeZoneStr = ObjectUtils.defaultIfNull(_configDao.getValue(Config.UsageAggregationTimezone.toString()), "GMT"); _usageTimezone = TimeZone.getTimeZone(timeZoneStr); return true; } @@ -211,10 +210,10 @@ public Pair, Integer> getUsageRecords(ListUsageRecordsCmd Date adjustedStartDate = computeAdjustedTime(startDate, usageTZ); Date adjustedEndDate = computeAdjustedTime(endDate, usageTZ); - if (logger.isDebugEnabled()) { - logger.debug("getting usage records for account: " + accountId + ", domainId: " + domainId + ", between " + adjustedStartDate + " and " + adjustedEndDate + - ", using pageSize: " + cmd.getPageSizeVal() + " and startIndex: " + cmd.getStartIndex()); - } + logger.debug("Getting usage records for account ID [{}], domain ID [{}] between [{}] and [{}] using page size [{}] and start index [{}].", + accountId, domainId, DateUtil.displayDateInTimezone(_usageTimezone, adjustedStartDate), + DateUtil.displayDateInTimezone(_usageTimezone, adjustedEndDate), cmd.getPageSizeVal(), + cmd.getStartIndex()); Filter usageFilter = new Filter(UsageVO.class, "id", true, cmd.getStartIndex(), cmd.getPageSizeVal()); diff --git a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java index c2ba8b59bf55..e5fe25378915 100644 --- a/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/main/java/com/cloud/vm/UserVmManagerImpl.java @@ -51,6 +51,9 @@ import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.ParserConfigurationException; +import com.cloud.kubernetes.cluster.KubernetesClusterHelper; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.element.NsxProviderVO; import com.cloud.utils.exception.ExceptionProxyObject; import org.apache.cloudstack.acl.ControlledEntity; import org.apache.cloudstack.acl.ControlledEntity.ACLType; @@ -589,6 +592,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir @Inject VMScheduleManager vmScheduleManager; + @Inject + NsxProviderDao nsxProviderDao; private ScheduledExecutorService _executor = null; private ScheduledExecutorService _vmIpFetchExecutor = null; @@ -597,6 +602,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private boolean _dailyOrHourly = false; private int capacityReleaseInterval; private ExecutorService _vmIpFetchThreadExecutor; + private List kubernetesClusterHelpers; private String _instance; @@ -610,6 +616,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir private static final int NUM_OF_2K_BLOCKS = 512; private static final int MAX_HTTP_POST_LENGTH = NUM_OF_2K_BLOCKS * MAX_USER_DATA_LENGTH_BYTES; + public List getKubernetesClusterHelpers() { + return kubernetesClusterHelpers; + } + + public void setKubernetesClusterHelpers(final List kubernetesClusterHelpers) { + this.kubernetesClusterHelpers = kubernetesClusterHelpers; + } + @Inject private OrchestrationService _orchSrvc; @@ -2562,11 +2576,15 @@ private boolean cleanupVmResources(long vmId) { } // cleanup port forwarding rules - if (_rulesMgr.revokePortForwardingRulesForVm(vmId)) { - logger.debug("Port forwarding rules are removed successfully as a part of vm id=" + vmId + " expunge"); - } else { - success = false; - logger.warn("Fail to remove port forwarding rules as a part of vm id=" + vmId + " expunge"); + VMInstanceVO vmInstanceVO = _vmInstanceDao.findById(vmId); + NsxProviderVO nsx = nsxProviderDao.findByZoneId(vmInstanceVO.getDataCenterId()); + if (Objects.isNull(nsx) || Objects.isNull(kubernetesClusterHelpers.get(0).findByVmId(vmId))) { + if (_rulesMgr.revokePortForwardingRulesForVm(vmId)) { + logger.debug("Port forwarding rules are removed successfully as a part of vm id=" + vmId + " expunge"); + } else { + success = false; + logger.warn("Fail to remove port forwarding rules as a part of vm id=" + vmId + " expunge"); + } } // cleanup load balancer rules @@ -5682,37 +5700,47 @@ public UserVm destroyVm(long vmId, boolean expunge) throws ResourceUnavailableEx boolean status; State vmState = vm.getState(); - try { - VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); - status = vmEntity.destroy(expunge); - } catch (CloudException e) { - CloudRuntimeException ex = new CloudRuntimeException("Unable to destroy with specified vmId", e); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; - } + Account owner = _accountMgr.getAccount(vm.getAccountId()); - if (status) { - // Mark the account's volumes as destroyed - List volumes = _volsDao.findByInstance(vmId); - for (VolumeVO volume : volumes) { - if (volume.getVolumeType().equals(Volume.Type.ROOT)) { - UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), - Volume.class.getName(), volume.getUuid(), volume.isDisplayVolume()); - } + ServiceOfferingVO offering = serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()); + + try (CheckedReservation vmReservation = new CheckedReservation(owner, ResourceType.user_vm, vmId, null, -1L, reservationDao, resourceLimitService); + CheckedReservation cpuReservation = new CheckedReservation(owner, ResourceType.cpu, vmId, null, -1 * Long.valueOf(offering.getCpu()), reservationDao, resourceLimitService); + CheckedReservation memReservation = new CheckedReservation(owner, ResourceType.memory, vmId, null, -1 * Long.valueOf(offering.getRamSize()), reservationDao, resourceLimitService); + ) { + try { + VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); + status = vmEntity.destroy(expunge); + } catch (CloudException e) { + CloudRuntimeException ex = new CloudRuntimeException("Unable to destroy with specified vmId", e); + ex.addProxyObject(vm.getUuid(), "vmId"); + throw ex; } - if (vmState != State.Error) { - // Get serviceOffering and template for Virtual Machine - ServiceOfferingVO offering = serviceOfferingDao.findByIdIncludingRemoved(vm.getId(), vm.getServiceOfferingId()); - VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); - //Update Resource Count for the given account - resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(),offering, template); + if (status) { + // Mark the account's volumes as destroyed + List volumes = _volsDao.findByInstance(vmId); + for (VolumeVO volume : volumes) { + if (volume.getVolumeType().equals(Volume.Type.ROOT)) { + UsageEventUtils.publishUsageEvent(EventTypes.EVENT_VOLUME_DELETE, volume.getAccountId(), volume.getDataCenterId(), volume.getId(), volume.getName(), + Volume.class.getName(), volume.getUuid(), volume.isDisplayVolume()); + } + } + + if (vmState != State.Error) { + // Get serviceOffering and template for Virtual Machine + VMTemplateVO template = _templateDao.findByIdIncludingRemoved(vm.getTemplateId()); + //Update Resource Count for the given account + resourceCountDecrement(vm.getAccountId(), vm.isDisplayVm(), offering, template); + } + return _vmDao.findById(vmId); + } else { + CloudRuntimeException ex = new CloudRuntimeException("Failed to destroy vm with specified vmId"); + ex.addProxyObject(vm.getUuid(), "vmId"); + throw ex; } - return _vmDao.findById(vmId); - } else { - CloudRuntimeException ex = new CloudRuntimeException("Failed to destroy vm with specified vmId"); - ex.addProxyObject(vm.getUuid(), "vmId"); - throw ex; + } catch (Exception e) { + throw new CloudRuntimeException("Failed to destroy vm with specified vmId", e); } } diff --git a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java index c09775ddaf66..2dea5a4223fa 100644 --- a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java +++ b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinition.java @@ -19,9 +19,14 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; +import com.cloud.dc.DataCenter; +import com.cloud.dc.Vlan; import com.cloud.network.dao.NetworkDetailVO; import com.cloud.network.dao.NetworkDetailsDao; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.element.NsxProviderVO; import com.cloud.network.router.VirtualRouter; import com.cloud.storage.DiskOfferingVO; import com.cloud.storage.dao.DiskOfferingDao; @@ -87,6 +92,7 @@ public class RouterDeploymentDefinition { protected NetworkDao networkDao; protected DomainRouterDao routerDao; + protected NsxProviderDao nsxProviderDao; protected PhysicalNetworkServiceProviderDao physicalProviderDao; protected NetworkModel networkModel; protected VirtualRouterProviderDao vrProviderDao; @@ -384,8 +390,19 @@ protected void executeDeployment() protected void findSourceNatIP() throws InsufficientAddressCapacityException, ConcurrentOperationException { sourceNatIp = null; + DataCenter zone = dest.getDataCenter(); + Long zoneId = null; + if (Objects.nonNull(zone)) { + zoneId = zone.getId(); + } + NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId); + if (isPublicNetwork) { - sourceNatIp = ipAddrMgr.assignSourceNatIpAddressToGuestNetwork(owner, guestNetwork); + if (Objects.isNull(nsxProvider)) { + sourceNatIp = ipAddrMgr.assignSourceNatIpAddressToGuestNetwork(owner, guestNetwork); + } else { + sourceNatIp = ipAddrMgr.assignPublicIpAddress(zoneId, getPodId(), owner, Vlan.VlanType.VirtualNetwork, null, null, false, true); + } } } diff --git a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionBuilder.java b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionBuilder.java index aab097115b95..227ae8d641eb 100644 --- a/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionBuilder.java +++ b/server/src/main/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionBuilder.java @@ -23,6 +23,7 @@ import javax.inject.Inject; import com.cloud.network.dao.NetworkDetailsDao; +import com.cloud.network.dao.NsxProviderDao; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -63,6 +64,8 @@ public class RouterDeploymentDefinitionBuilder { @Inject private DomainRouterDao routerDao; @Inject + private NsxProviderDao nsxProviderDao; + @Inject private PhysicalNetworkServiceProviderDao physicalProviderDao; @Inject private NetworkModel networkModel; @@ -125,6 +128,7 @@ protected RouterDeploymentDefinition injectDependencies( routerDeploymentDefinition.networkDao = networkDao; routerDeploymentDefinition.routerDao = routerDao; + routerDeploymentDefinition.nsxProviderDao = nsxProviderDao; routerDeploymentDefinition.physicalProviderDao = physicalProviderDao; routerDeploymentDefinition.networkModel = networkModel; routerDeploymentDefinition.vrProviderDao = vrProviderDao; diff --git a/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java b/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java index 8b964a3ba80c..aa44f29efcdb 100644 --- a/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java +++ b/server/src/main/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinition.java @@ -19,7 +19,12 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.Objects; +import com.cloud.dc.DataCenter; +import com.cloud.dc.Vlan; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.element.NsxProviderVO; import com.cloud.dc.dao.VlanDao; import com.cloud.deploy.DataCenterDeployment; @@ -118,8 +123,26 @@ protected boolean prepareDeployment() { @Override protected void findSourceNatIP() throws InsufficientAddressCapacityException, ConcurrentOperationException { sourceNatIp = null; + DataCenter zone = dest.getDataCenter(); + Long zoneId = null; + if (Objects.nonNull(zone)) { + zoneId = zone.getId(); + } + NsxProviderVO nsxProvider = nsxProviderDao.findByZoneId(zoneId); + if (isPublicNetwork) { - sourceNatIp = vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc); + if (Objects.isNull(nsxProvider)) { + sourceNatIp = vpcMgr.assignSourceNatIpAddressToVpc(owner, vpc); + } else { + // NSX deploys VRs with Public NIC != to the source NAT, the source NAT IP is on the NSX Public range + sourceNatIp = ipAddrMgr.assignPublicIpAddress(zoneId, getPodId(), owner, Vlan.VlanType.VirtualNetwork, null, null, false, true); + if (vpc != null) { + IPAddressVO routerPublicIp = ipAddressDao.findByIp(sourceNatIp.getAddress().toString()); + routerPublicIp.setVpcId(vpc.getId()); + routerPublicIp.setSourceNat(true); + ipAddressDao.persist(routerPublicIp); + } + } } } diff --git a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml index 7227264e2297..c80c2947a997 100644 --- a/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml +++ b/server/src/main/resources/META-INF/cloudstack/core/spring-server-core-managers-context.xml @@ -106,8 +106,9 @@ - - + + + + diff --git a/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java b/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java index 958a39be4103..f7606a9a9628 100644 --- a/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java +++ b/server/src/test/java/com/cloud/configuration/ConfigurationManagerImplTest.java @@ -16,12 +16,40 @@ // under the License. package com.cloud.configuration; +import com.cloud.capacity.dao.CapacityDao; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.VlanVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.DataCenterIpAddressDao; +import com.cloud.dc.dao.DedicatedResourceDao; +import com.cloud.dc.dao.HostPodDao; +import com.cloud.dc.dao.VlanDao; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.host.dao.HostDao; +import com.cloud.network.Network; +import com.cloud.network.NetworkModel; +import com.cloud.network.NetworkService; +import com.cloud.network.Networks; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.NsxProviderDao; +import com.cloud.network.dao.PhysicalNetworkDao; +import com.cloud.network.element.NsxProviderVO; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.storage.StorageManager; +import com.cloud.storage.dao.VMTemplateZoneDao; +import com.cloud.storage.dao.VolumeDao; import com.cloud.utils.net.NetUtils; +import com.cloud.vm.dao.VMInstanceDao; +import org.apache.cloudstack.annotation.dao.AnnotationDao; +import org.apache.cloudstack.api.command.admin.network.CreateNetworkOfferingCmd; +import org.apache.cloudstack.api.command.admin.zone.DeleteZoneCmd; +import org.apache.cloudstack.engine.subsystem.api.storage.ZoneScope; import org.apache.cloudstack.framework.config.ConfigDepot; import org.apache.cloudstack.framework.config.ConfigKey; -import com.cloud.dc.dao.DataCenterDao; +import org.apache.cloudstack.framework.config.dao.ConfigurationDao; +import org.apache.cloudstack.storage.datastore.db.ImageStoreDao; import com.cloud.domain.Domain; import com.cloud.domain.dao.DomainDao; import com.cloud.offering.DiskOffering; @@ -41,12 +69,28 @@ import org.mockito.MockedStatic; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.Collections; import org.mockito.InjectMocks; import org.mockito.Spy; import java.util.ArrayList; import java.util.List; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.anyBoolean; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.nullable; +import static org.mockito.Mockito.anyMap; +import static org.mockito.Mockito.anyList; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.times; @RunWith(MockitoJUnitRunner.class) public class ConfigurationManagerImplTest { @@ -65,8 +109,6 @@ public class ConfigurationManagerImplTest { @Mock Domain domainMock; @Mock - DataCenterDao zoneDaoMock; - @Mock DomainDao domainDaoMock; @Mock EntityManager entityManagerMock; @@ -77,6 +119,48 @@ public class ConfigurationManagerImplTest { @Mock UpdateDiskOfferingCmd updateDiskOfferingCmdMock; + @Mock + NsxProviderDao nsxProviderDao; + @Mock + DataCenterDao zoneDao; + @Mock + HostDao hostDao; + @Mock + HostPodDao podDao; + @Mock + DataCenterIpAddressDao ipAddressDao; + @Mock + IPAddressDao publicIpAddressDao; + @Mock + VMInstanceDao vmInstanceDao; + @Mock + VolumeDao volumeDao; + @Mock + PhysicalNetworkDao physicalNetworkDao; + @Mock + ImageStoreDao imageStoreDao; + @Mock + VlanDao vlanDao; + @Mock + VMTemplateZoneDao vmTemplateZoneDao; + @Mock + CapacityDao capacityDao; + @Mock + DedicatedResourceDao dedicatedResourceDao; + @Mock + AnnotationDao annotationDao; + @Mock + ConfigurationDao configDao; + @Mock + NetworkOfferingDao networkOfferingDao; + @Mock + NetworkService networkService; + @Mock + NetworkModel networkModel; + + DeleteZoneCmd deleteZoneCmd; + CreateNetworkOfferingCmd createNetworkOfferingCmd; + Long validId = 1L; Long invalidId = 100L; List filteredZoneIds = List.of(1L, 2L, 3L); @@ -90,6 +174,28 @@ public class ConfigurationManagerImplTest { @Before public void setUp() throws Exception { configurationManagerImplSpy._configDepot = configDepot; + configurationManagerImplSpy.nsxProviderDao = nsxProviderDao; + configurationManagerImplSpy._zoneDao = zoneDao; + configurationManagerImplSpy._hostDao = hostDao; + configurationManagerImplSpy._podDao = podDao; + configurationManagerImplSpy._privateIpAddressDao = ipAddressDao; + configurationManagerImplSpy._publicIpAddressDao = publicIpAddressDao; + configurationManagerImplSpy._vmInstanceDao = vmInstanceDao; + configurationManagerImplSpy._volumeDao = volumeDao; + configurationManagerImplSpy._physicalNetworkDao = physicalNetworkDao; + configurationManagerImplSpy._imageStoreDao = imageStoreDao; + configurationManagerImplSpy._vlanDao = vlanDao; + configurationManagerImplSpy._capacityDao = capacityDao; + configurationManagerImplSpy._dedicatedDao = dedicatedResourceDao; + configurationManagerImplSpy._configDao = configDao; + configurationManagerImplSpy._networkOfferingDao = networkOfferingDao; + configurationManagerImplSpy._networkSvc = networkService; + configurationManagerImplSpy._networkModel = networkModel; + ReflectionTestUtils.setField(configurationManagerImplSpy, "templateZoneDao", vmTemplateZoneDao); + ReflectionTestUtils.setField(configurationManagerImplSpy, "annotationDao", annotationDao); + + deleteZoneCmd = Mockito.mock(DeleteZoneCmd.class); + createNetworkOfferingCmd = Mockito.mock(CreateNetworkOfferingCmd.class); } @Test @@ -300,6 +406,57 @@ public void testValidateIpAddressRelatedConfigValuesValidIpRange() { configurationManagerImplSpy.validateIpAddressRelatedConfigValues("config.iprange", "192.168.1.1-192.168.1.100"); } + @Test + public void testDeleteZoneInvokesDeleteNsxProviderWhenNSXIsEnabled() { + NsxProviderVO nsxProviderVO = Mockito.mock(NsxProviderVO.class); + DataCenterVO dataCenterVO = Mockito.mock(DataCenterVO.class); + + when(nsxProviderDao.findByZoneId(anyLong())).thenReturn(nsxProviderVO); + when(zoneDao.findById(anyLong())).thenReturn(dataCenterVO); + lenient().when(hostDao.findByDataCenterId(anyLong())).thenReturn(Collections.emptyList()); + when(podDao.listByDataCenterId(anyLong())).thenReturn(Collections.emptyList()); + when(ipAddressDao.countIPs(anyLong(), anyBoolean())).thenReturn(0); + when(publicIpAddressDao.countIPs(anyLong(), anyBoolean())).thenReturn(0); + when(vmInstanceDao.listByZoneId(anyLong())).thenReturn(Collections.emptyList()); + when(volumeDao.findByDc(anyLong())).thenReturn(Collections.emptyList()); + when(physicalNetworkDao.listByZone(anyLong())).thenReturn(Collections.emptyList()); + when(imageStoreDao.findByZone(any(ZoneScope.class), nullable(Boolean.class))).thenReturn(Collections.emptyList()); + when(vlanDao.listByZone(anyLong())).thenReturn(List.of(Mockito.mock(VlanVO.class))); + when(nsxProviderVO.getId()).thenReturn(1L); + when(zoneDao.remove(anyLong())).thenReturn(true); + when(capacityDao.removeBy(nullable(Short.class), anyLong(), nullable(Long.class), nullable(Long.class), nullable(Long.class))).thenReturn(true); + when(dedicatedResourceDao.findByZoneId(anyLong())).thenReturn(null); + lenient().when(annotationDao.removeByEntityType(anyString(), anyString())).thenReturn(true); + + configurationManagerImplSpy.deleteZone(deleteZoneCmd); + + verify(nsxProviderDao, times(1)).remove(anyLong()); + } + + @Test + public void testCreateNetworkOfferingForNsx() { + NetworkOfferingVO offeringVO = Mockito.mock(NetworkOfferingVO.class); + + when(createNetworkOfferingCmd.isForNsx()).thenReturn(true); + when(createNetworkOfferingCmd.getNsxMode()).thenReturn(NetworkOffering.NsxMode.NATTED.name()); + when(createNetworkOfferingCmd.getTraffictype()).thenReturn(Networks.TrafficType.Guest.name()); + when(createNetworkOfferingCmd.getGuestIpType()).thenReturn(Network.GuestType.Isolated.name()); + when(createNetworkOfferingCmd.getAvailability()).thenReturn(NetworkOffering.Availability.Optional.name()); + lenient().when(configurationManagerImplSpy.createNetworkOffering(anyString(), anyString(), any(Networks.TrafficType.class), anyString(), + anyBoolean(), any(NetworkOffering.Availability.class), anyInt(), anyMap(), anyBoolean(), any(Network.GuestType.class), + anyBoolean(), anyLong(), anyBoolean(), anyMap(), anyBoolean(), anyBoolean(), anyMap(), anyBoolean(), anyInt(), + anyBoolean(), anyBoolean(), anyBoolean(), anyBoolean(), anyString(), anyList(), anyList(), anyBoolean(), any(NetUtils.InternetProtocol.class))) + .thenReturn(offeringVO); + when(configDao.getValue(anyString())).thenReturn("1000"); + lenient().when(networkOfferingDao.persist(any(NetworkOfferingVO.class), anyMap())).thenReturn(offeringVO); + doNothing().when(networkService).validateIfServiceOfferingIsActiveAndSystemVmTypeIsDomainRouter(anyLong()); + doNothing().when(networkModel).canProviderSupportServices(anyMap()); + + NetworkOffering offering = configurationManagerImplSpy.createNetworkOffering(createNetworkOfferingCmd); + + Assert.assertNotNull(offering); + } + @Test public void validateDomainTestInvalidIdThrowException() { Mockito.doReturn(null).when(domainDaoMock).findById(invalidId); @@ -308,7 +465,7 @@ public void validateDomainTestInvalidIdThrowException() { @Test public void validateZoneTestInvalidIdThrowException() { - Mockito.doReturn(null).when(zoneDaoMock).findById(invalidId); + Mockito.doReturn(null).when(zoneDao).findById(invalidId); Assert.assertThrows(InvalidParameterValueException.class, () -> configurationManagerImplSpy.validateZone(List.of(invalidId))); } diff --git a/server/src/test/java/com/cloud/network/NetworkModelImplTest.java b/server/src/test/java/com/cloud/network/NetworkModelImplTest.java index 0bbead6a4686..6eb3e5d186b4 100644 --- a/server/src/test/java/com/cloud/network/NetworkModelImplTest.java +++ b/server/src/test/java/com/cloud/network/NetworkModelImplTest.java @@ -17,34 +17,80 @@ package com.cloud.network; import com.cloud.dc.DataCenter; +import com.cloud.dc.VlanVO; import com.cloud.exception.InvalidParameterValueException; +import com.cloud.network.addr.PublicIp; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.NetworkServiceMapDao; +import com.cloud.network.dao.NetworkServiceMapVO; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.element.NetworkElement; +import com.cloud.network.element.VpcVirtualRouterElement; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; import com.cloud.utils.Pair; +import com.cloud.utils.net.Ip; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; + +import org.mockito.ArgumentMatchers; +import org.junit.runner.RunWith; import org.mockito.InjectMocks; +import org.mockito.Mock; import org.mockito.Mockito; +import org.springframework.test.util.ReflectionTestUtils; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import org.mockito.junit.MockitoJUnitRunner; +@RunWith(MockitoJUnitRunner.class) 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"}; + final String[] ip4Dns1 = {"5.5.5.5", "6.6.6.6", "9.9.9.9"}; + final String[] ip4Dns2 = {"7.7.7.7", "8.8.8.8", "10.10.10.10"}; + final String[] ip6Dns1 = {"2001:4860:4860::5555", "2001:4860:4860::6666", "2001:4860:4860::9999"}; + final String[] ip6Dns2 = {"2001:4860:4860::7777", "2001:4860:4860::8888", "2001:4860:4860::AAAA"}; + + @Mock + private VpcDao vpcDao; @InjectMocks private NetworkModelImpl networkModel = new NetworkModelImpl(); - private void prepareMocks(boolean isIp6, Network network, DataCenter zone, - String dns1, String dns2, String dns3, String dns4) { + private NetworkOfferingDao networkOfferingDao; + private NetworkServiceMapDao networkServiceMapDao; + @Before + public void setUp() { + networkOfferingDao = Mockito.mock(NetworkOfferingDao.class); + networkServiceMapDao = Mockito.mock(NetworkServiceMapDao.class); + networkModel._networkOfferingDao = networkOfferingDao; + networkModel._ntwkSrvcDao = networkServiceMapDao; + } + + private void prepareMocks(boolean isIp6, Network network, DataCenter zone, VpcVO vpc, + String networkDns1, String zoneDns1, String networkDns2, String zoneDns2, + String vpcDns1, String vpcDns2) { 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); + Mockito.when(network.getIp6Dns1()).thenReturn(networkDns1); + Mockito.when(zone.getIp6Dns1()).thenReturn(zoneDns1); + Mockito.when(network.getIp6Dns2()).thenReturn(networkDns2); + Mockito.when(zone.getIp6Dns2()).thenReturn(zoneDns2); + Mockito.when(vpc.getIp6Dns1()).thenReturn(vpcDns1); + Mockito.when(vpc.getIp6Dns2()).thenReturn(vpcDns2); } 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); + Mockito.when(network.getDns1()).thenReturn(networkDns1); + Mockito.when(zone.getDns1()).thenReturn(zoneDns1); + Mockito.when(network.getDns2()).thenReturn(networkDns2); + Mockito.when(zone.getDns2()).thenReturn(zoneDns2); + Mockito.when(vpc.getIp4Dns1()).thenReturn(vpcDns1); + Mockito.when(vpc.getIp4Dns2()).thenReturn(vpcDns2); } } @@ -53,38 +99,53 @@ private void testDnsCases(boolean isIp6) { 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]); + VpcVO vpc = Mockito.mock(VpcVO.class); + Mockito.when(network.getVpcId()).thenReturn(1L); + Mockito.doReturn(vpc).when(vpcDao).findById(ArgumentMatchers.anyLong()); + // network, vpc and zone have valid dns + prepareMocks(isIp6, network, zone, vpc, dns1[0], dns1[1], dns2[0], dns2[1], dns1[2], dns2[2]); 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); + // Network has valid dns and vpc/zone don't + prepareMocks(isIp6, network, zone, vpc, dns1[0], null, dns2[0], null, null, 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]); + // Vpc has valid dns and network/zone don't + prepareMocks(isIp6, network, zone, vpc, null, null, null, null, dns1[2], dns2[2]); + result = isIp6 ? networkModel.getNetworkIp6Dns(network, zone) : + networkModel.getNetworkIp4Dns(network, zone); + Assert.assertEquals(dns1[2], result.first()); + Assert.assertEquals(dns2[2], result.second()); + // Zone has a valid dns and network/vpc don't + prepareMocks(isIp6, network, zone, vpc, null, dns1[1], null, dns2[1], null, null); 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]); + // Zone/vpc has a valid dns and network has only first dns + prepareMocks(isIp6, network, zone, vpc, dns1[0], dns1[1], null, dns2[1], dns1[2], dns2[2]); 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); + // network don't have a valid dns, vpc has only first dns, Zone has a valid dns + prepareMocks(isIp6, network, zone, vpc, null, dns1[1], null, dns2[1], dns1[2], null); + result = isIp6 ? networkModel.getNetworkIp6Dns(network, zone) : + networkModel.getNetworkIp4Dns(network, zone); + Assert.assertEquals(dns1[2], result.first()); + Assert.assertNull(result.second()); + // network/vpc/zone only have the first dns + prepareMocks(isIp6, network, zone, vpc, dns1[0], dns1[1], null, null, dns1[2], 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); + // network/vpc and zone dns are null + prepareMocks(isIp6, network, zone, vpc, null, null, null, null, null, null); result = isIp6 ? networkModel.getNetworkIp6Dns(network, zone) : networkModel.getNetworkIp4Dns(network, zone); Assert.assertNull(result.first()); @@ -140,4 +201,35 @@ public void testVerifyIp6DnsPairInvalidDns2Failure() { public void testVerifyIp6DnsPairValid() { networkModel.verifyIp6DnsPair(ip6Dns1[0], ip6Dns1[1]); } + + @Test + public void testGetProviderToIpList() { + Set services1 = new HashSet<>(List.of(Network.Service.Firewall)); + Set services2 = new HashSet<>(List.of(Network.Service.SourceNat)); + Ip ip1 = new Ip("10.10.10.10"); + Ip ip2 = new Ip("10.10.10.10"); + IPAddressVO ipAddressVO1 = new IPAddressVO(ip1, 1L, 0x0ac00000L, 2L, true); + IPAddressVO ipAddressVO2 = new IPAddressVO(ip2, 1L, 0x0ac00000L, 2L, true); + VlanVO vlanVO = new VlanVO(); + vlanVO.setNetworkId(15L); + PublicIpAddress publicIpAddress1 = new PublicIp(ipAddressVO1, vlanVO, 0x0ac00000L); + PublicIpAddress publicIpAddress2 = new PublicIp(ipAddressVO2, vlanVO, 0x0ac00000L); + NetworkOfferingVO networkOfferingVO = new NetworkOfferingVO(); + networkOfferingVO.setForVpc(true); + networkOfferingVO.setForNsx(false); + Network network = new NetworkVO(); + List networkServiceMapVOs = new ArrayList<>(); + networkServiceMapVOs.add(new NetworkServiceMapVO(15L, Network.Service.Firewall, Network.Provider.VPCVirtualRouter)); + networkServiceMapVOs.add(new NetworkServiceMapVO(15L, Network.Service.SourceNat, Network.Provider.VPCVirtualRouter)); + NetworkElement element = new VpcVirtualRouterElement(); + + ReflectionTestUtils.setField(networkModel, "networkElements", List.of(element)); + Mockito.when(networkOfferingDao.findById(ArgumentMatchers.anyLong())).thenReturn(networkOfferingVO); + Mockito.when(networkServiceMapDao.getServicesInNetwork(ArgumentMatchers.anyLong())).thenReturn(networkServiceMapVOs); + Map> ipToServices = new HashMap<>(); + ipToServices.put(publicIpAddress1, services1); + ipToServices.put(publicIpAddress2, services2); + Map> result = networkModel.getProviderToIpList(network, ipToServices); + Assert.assertNotNull(result); + } } diff --git a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java index c993f7b7095e..f2ab2a08ae75 100644 --- a/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java +++ b/server/src/test/java/com/cloud/network/NetworkServiceImplTest.java @@ -40,6 +40,7 @@ import com.cloud.domain.Domain; import com.cloud.domain.DomainVO; import com.cloud.domain.dao.DomainDao; +import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.dao.PublicIpQuarantineDao; import com.cloud.network.vo.PublicIpQuarantineVO; import com.cloud.user.dao.AccountDao; @@ -212,6 +213,8 @@ public class NetworkServiceImplTest { @Mock private Ip ipMock; + @Mock + private NsxProviderDao nsxProviderDao; private static Date beforeDate; @@ -295,6 +298,7 @@ public void setup() throws Exception { service.commandSetupHelper = commandSetupHelper; service.networkHelper = networkHelper; service._ipAddrMgr = ipAddressManagerMock; + service.nsxProviderDao = nsxProviderDao; callContextMocked = Mockito.mockStatic(CallContext.class); CallContext callContextMock = Mockito.mock(CallContext.class); callContextMocked.when(CallContext::current).thenReturn(callContextMock); 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 9818b43e6503..50d8af5e520d 100644 --- a/server/src/test/java/com/cloud/network/guru/DirectNetworkGuruTest.java +++ b/server/src/test/java/com/cloud/network/guru/DirectNetworkGuruTest.java @@ -142,7 +142,7 @@ public void testCanDesign() { when(networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Network.Service.SecurityGroup)).thenReturn(true); - assertNotNull(guru.design(offering, plan, network, owner)); + assertNotNull(guru.design(offering, plan, network, "", 1L, owner)); } @Test @@ -159,7 +159,7 @@ public void testDesignDns() { when(networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Network.Service.SecurityGroup)).thenReturn(false); - Network config = guru.design(offering, plan, network, owner); + Network config = guru.design(offering, plan, network, "", 1L, owner); assertNotNull(config); assertEquals(ip4Dns[0], config.getDns1()); assertEquals(ip4Dns[1], config.getDns2()); diff --git a/server/src/test/java/com/cloud/network/guru/ExternalGuestNetworkGuruTest.java b/server/src/test/java/com/cloud/network/guru/ExternalGuestNetworkGuruTest.java index 3286ee54b3be..bcb39b6b3b5f 100644 --- a/server/src/test/java/com/cloud/network/guru/ExternalGuestNetworkGuruTest.java +++ b/server/src/test/java/com/cloud/network/guru/ExternalGuestNetworkGuruTest.java @@ -75,7 +75,7 @@ public void testDesignDns() { 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); + Network config = guru.design(networkOffering, plan, network, "", 1L, owner); assertNotNull(config); assertEquals(ip4Dns[0], config.getDns1()); assertEquals(ip4Dns[1], config.getDns2()); diff --git a/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java b/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java index f03ae9d62b29..37fa13baf98b 100644 --- a/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java +++ b/server/src/test/java/com/cloud/network/router/CommandSetupHelperTest.java @@ -16,21 +16,93 @@ // under the License. package com.cloud.network.router; +import com.cloud.agent.api.Command; import com.cloud.agent.api.routing.VmDataCommand; +import com.cloud.agent.manager.Commands; +import com.cloud.configuration.ConfigurationManager; +import com.cloud.dc.DataCenter; +import com.cloud.dc.DataCenterVO; +import com.cloud.dc.VlanVO; +import com.cloud.dc.dao.DataCenterDao; +import com.cloud.dc.dao.VlanDao; +import com.cloud.network.NetworkModel; +import com.cloud.network.PublicIpAddress; +import com.cloud.network.addr.PublicIp; +import com.cloud.network.dao.IPAddressDao; +import com.cloud.network.dao.IPAddressVO; +import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NetworkDetailsDao; +import com.cloud.network.dao.NetworkVO; +import com.cloud.network.vpc.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; +import com.cloud.offering.NetworkOffering; +import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.offerings.dao.NetworkOfferingDao; +import com.cloud.offerings.dao.NetworkOfferingDetailsDao; +import com.cloud.utils.net.Ip; +import com.cloud.vm.NicVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.dao.NicDao; import org.junit.Assert; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; @RunWith(MockitoJUnitRunner.class) public class CommandSetupHelperTest { @InjectMocks protected CommandSetupHelper commandSetupHelper = new CommandSetupHelper(); + @Mock + NicDao nicDao; + @Mock + NetworkDao networkDao; + @Mock + IPAddressDao ipAddressDao; + @Mock + VlanDao vlanDao; + @Mock + NetworkModel networkModel; + @Mock + NetworkOfferingDao networkOfferingDao; + @Mock + ConfigurationManager configurationManager; + @Mock + NetworkOfferingDetailsDao networkOfferingDetailsDao; + @Mock + NetworkDetailsDao networkDetailsDao; + @Mock + VpcDao vpcDao; + @Mock + RouterControlHelper routerControlHelper; + @Mock + DataCenterDao dcDao; + + @Before + public void setUp() { + ReflectionTestUtils.setField(commandSetupHelper, "_nicDao", nicDao); + ReflectionTestUtils.setField(commandSetupHelper, "_networkDao", networkDao); + ReflectionTestUtils.setField(commandSetupHelper, "_ipAddressDao", ipAddressDao); + ReflectionTestUtils.setField(commandSetupHelper, "_vlanDao", vlanDao); + ReflectionTestUtils.setField(commandSetupHelper, "_networkModel", networkModel); + ReflectionTestUtils.setField(commandSetupHelper, "_networkOfferingDao", networkOfferingDao); + ReflectionTestUtils.setField(commandSetupHelper, "networkOfferingDetailsDao", networkOfferingDetailsDao); + ReflectionTestUtils.setField(commandSetupHelper, "networkDetailsDao", networkDetailsDao); + ReflectionTestUtils.setField(commandSetupHelper, "_vpcDao", vpcDao); + ReflectionTestUtils.setField(commandSetupHelper, "_routerControlHelper", routerControlHelper); + ReflectionTestUtils.setField(commandSetupHelper, "_dcDao", dcDao); + } @Test public void testUserDataDetails() { @@ -79,4 +151,46 @@ public void testUserDataDetailsWithWhiteSpaces() { Assert.assertEquals("value1", metadataFile1[2]); Assert.assertEquals("value2", metadataFile2[2]); } + + @Test + public void testCreateVpcAssociatePublicIP() { + VirtualRouter router = Mockito.mock(VirtualRouter.class); + Ip ip = new Ip("10.10.10.10"); + IPAddressVO ipAddressVO = new IPAddressVO(ip, 1L, 0x0ac00000L, 2L, true); + VlanVO vlanVO = new VlanVO(); + vlanVO.setNetworkId(15L); + PublicIpAddress publicIpAddress = new PublicIp(ipAddressVO, vlanVO, 0x0ac00000L); + List pubIpList = new ArrayList<>(1); + pubIpList.add(publicIpAddress); + Commands commands = new Commands(Command.OnError.Stop); + Map vlanMacAddress = new HashMap<>(); + NicVO nicVO = new NicVO("nic", 1L, 2L, VirtualMachine.Type.User); + NetworkVO networkVO = new NetworkVO(); + networkVO.setNetworkOfferingId(12L); + List userIps = List.of(ipAddressVO); + NetworkOfferingVO networkOfferingVO = new NetworkOfferingVO(); + Map details = new HashMap<>(); + VpcVO vpc = new VpcVO(); + DataCenterVO dc = new DataCenterVO(1L, null, null, null, null, null, null, null, null, null, DataCenter.NetworkType.Advanced, null, null); + + Mockito.when(router.getId()).thenReturn(14L); + Mockito.when(router.getDataCenterId()).thenReturn(4L); + Mockito.when(nicDao.listByVmId(ArgumentMatchers.anyLong())).thenReturn(List.of(nicVO)); + Mockito.when(networkDao.findById(ArgumentMatchers.anyLong())).thenReturn(networkVO); + Mockito.when(ipAddressDao.listByAssociatedVpc(ArgumentMatchers.anyLong(), ArgumentMatchers.nullable(Boolean.class))).thenReturn(userIps); + Mockito.when(vlanDao.findById(ArgumentMatchers.anyLong())).thenReturn(vlanVO); + Mockito.when(networkModel.getNetworkRate(ArgumentMatchers.anyLong(), ArgumentMatchers.anyLong())).thenReturn(1200); + Mockito.when(networkModel.getNetwork(ArgumentMatchers.anyLong())).thenReturn(networkVO); + Mockito.when(networkOfferingDao.findById(ArgumentMatchers.anyLong())).thenReturn(networkOfferingVO); + Mockito.when(configurationManager.getNetworkOfferingNetworkRate(ArgumentMatchers.anyLong(), ArgumentMatchers.anyLong())).thenReturn(1200); + Mockito.when(networkModel.isSecurityGroupSupportedInNetwork(networkVO)).thenReturn(false); + Mockito.when(networkOfferingDetailsDao.getNtwkOffDetails(ArgumentMatchers.anyLong())).thenReturn(details); + Mockito.when(networkDetailsDao.findDetail(ArgumentMatchers.anyLong(), ArgumentMatchers.anyString())).thenReturn(null); + Mockito.when(vpcDao.findById(ArgumentMatchers.anyLong())).thenReturn(vpc); + Mockito.when(routerControlHelper.getRouterControlIp(ArgumentMatchers.anyLong())).thenReturn("10.1.11.101"); + Mockito.when(dcDao.findById(ArgumentMatchers.anyLong())).thenReturn(dc); + + commandSetupHelper.createVpcAssociatePublicIPCommands(router, pubIpList, commands, vlanMacAddress); + Assert.assertEquals(2, commands.size()); + } } diff --git a/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java b/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java index 8e1408d49983..a729af071068 100644 --- a/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java +++ b/server/src/test/java/com/cloud/network/router/NetworkHelperImplTest.java @@ -23,6 +23,11 @@ import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.OperationTimedoutException; import com.cloud.exception.ResourceUnavailableException; +import com.cloud.network.NetworkModel; +import com.cloud.network.dao.NetworkDao; +import com.cloud.vm.dao.NicDao; +import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -51,6 +56,20 @@ public class NetworkHelperImplTest { @InjectMocks protected NetworkHelperImpl nwHelper = new NetworkHelperImpl(); + @Mock + NetworkOrchestrationService networkOrchestrationService; + @Mock + NetworkDao networkDao; + @Mock + NetworkModel networkModel; + @Mock + NicDao nicDao; + + @Before + public void setUp() { + nwHelper._networkDao = networkDao; + nwHelper._networkModel = networkModel; + } @Test(expected=ResourceUnavailableException.class) public void testSendCommandsToRouterWrongRouterVersion() @@ -168,5 +187,4 @@ public void testSendCommandsToRouterWithNoAnswers() verify(answer1, times(0)).getResult(); assertFalse(result); } - } diff --git a/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java b/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java index bce081d43198..b24136972ad8 100644 --- a/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java +++ b/server/src/test/java/com/cloud/network/vpc/NetworkACLServiceImplTest.java @@ -17,10 +17,12 @@ package com.cloud.network.vpc; +import static org.mockito.ArgumentMatchers.nullable; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.isNull; -import static org.mockito.ArgumentMatchers.nullable; +import static org.mockito.ArgumentMatchers.eq; + import static org.mockito.Mockito.times; import java.util.ArrayList; @@ -29,7 +31,9 @@ import java.util.List; import java.util.Map; +import com.cloud.dc.DataCenter; import com.cloud.exception.PermissionDeniedException; +import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.vpc.dao.VpcDao; import com.cloud.utils.net.NetUtils; import org.apache.cloudstack.acl.SecurityChecker.AccessType; @@ -104,6 +108,8 @@ public class NetworkACLServiceImplTest { private NetworkACLVO networkACLVOMock; @Mock private UpdateNetworkACLListCmd updateNetworkACLListCmdMock; + @Mock + private NsxProviderDao nsxProviderDao; private Long networkAclMockId = 5L; private Long networkOfferingMockId = 2L; @@ -130,6 +136,8 @@ public class NetworkACLServiceImplTest { @Mock private VpcVO vpcVOMock; + @Mock + DataCenter dataCenterVO; @Mock private Account accountMock; @@ -174,7 +182,9 @@ public void createNetworkACLItemTestAclNumberNotNull() { private void createNetworkACLItemTestForNumberAndExecuteTest(Integer number) { Mockito.when(createNetworkAclCmdMock.getNumber()).thenReturn(number); - + Mockito.when(vpcDaoMock.findById(anyLong())).thenReturn(vpcVOMock); + Mockito.when(entityManagerMock.findById(any(), anyLong())).thenReturn(dataCenterVO); + Mockito.when(nsxProviderDao.findByZoneId(anyLong())).thenReturn(null); Mockito.doReturn(networkAclMockId).when(networkAclServiceImpl).createAclListIfNeeded(createNetworkAclCmdMock); Mockito.when(networkAclManagerMock.getNetworkACL(networkAclMockId)).thenReturn(networkAclMock); @@ -711,6 +721,9 @@ public void validateIcmpTypeAndCodeTestIcmpTypeValidAndIcmpCodeValid() { @Test public void updateNetworkACLItemTest() throws ResourceUnavailableException { Mockito.when(networkAclItemVoMock.getAclId()).thenReturn(networkAclMockId); + Mockito.when(vpcDaoMock.findById(anyLong())).thenReturn(vpcVOMock); + Mockito.when(entityManagerMock.findById(any(), anyLong())).thenReturn(dataCenterVO); + Mockito.when(nsxProviderDao.findByZoneId(anyLong())).thenReturn(null); Mockito.doReturn(networkAclItemVoMock).when(networkAclServiceImpl).validateNetworkAclRuleIdAndRetrieveIt(updateNetworkACLItemCmdMock); Mockito.doReturn(networkAclMock).when(networkAclManagerMock).getNetworkACL(networkAclMockId); Mockito.doNothing().when(networkAclServiceImpl).validateNetworkAcl(Mockito.eq(networkAclMock)); diff --git a/server/src/test/java/com/cloud/resourcelimit/CheckedReservationTest.java b/server/src/test/java/com/cloud/resourcelimit/CheckedReservationTest.java index e44fa17330c1..ffd6063722f7 100644 --- a/server/src/test/java/com/cloud/resourcelimit/CheckedReservationTest.java +++ b/server/src/test/java/com/cloud/resourcelimit/CheckedReservationTest.java @@ -19,7 +19,6 @@ package com.cloud.resourcelimit; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.when; @@ -108,9 +107,10 @@ public void getId() { @Test public void getNoAmount() { + Mockito.when(reservationDao.persist(Mockito.any())).thenReturn(reservation); try (CheckedReservation cr = new CheckedReservation(account, Resource.ResourceType.cpu,-11l, reservationDao, resourceLimitService) ) { Long amount = cr.getReservedAmount(); - assertNull(amount); + assertEquals(Long.valueOf(-11L), amount); } catch (NullPointerException npe) { fail("NPE caught"); } catch (ResourceAllocationException rae) { diff --git a/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java b/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java index 97a4b12f84e9..3d31561f2680 100644 --- a/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java +++ b/server/src/test/java/com/cloud/resourcelimit/ResourceLimitManagerImplTest.java @@ -27,6 +27,7 @@ import org.apache.cloudstack.api.response.DomainResponse; import org.apache.cloudstack.api.response.TaggedResourceLimitAndCountResponse; import org.apache.cloudstack.framework.config.ConfigKey; +import org.apache.cloudstack.reservation.dao.ReservationDao; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.LogManager; @@ -106,6 +107,8 @@ public class ResourceLimitManagerImplTest extends TestCase { @Mock ResourceCountDao resourceCountDao; @Mock + private ReservationDao reservationDao; + @Mock UserVmJoinDao userVmJoinDao; @Mock ServiceOfferingDao serviceOfferingDao; @@ -675,7 +678,9 @@ public void testCalculateVmCountForAccount() { Assert.assertEquals(2L, resourceLimitManager.calculateVmCountForAccount(accountId, tag)); tag = "tag"; - Mockito.doReturn(List.of(UserVmJoinVO.class)).when(resourceLimitManager).getVmsWithAccountAndTag(accountId, tag); + UserVmJoinVO vm = Mockito.mock(UserVmJoinVO.class); + Mockito.when(vm.getId()).thenReturn(1L); + Mockito.doReturn(List.of(vm)).when(resourceLimitManager).getVmsWithAccountAndTag(accountId, tag); Assert.assertEquals(1L, resourceLimitManager.calculateVmCountForAccount(accountId, tag)); } diff --git a/server/src/test/java/com/cloud/server/ManagementServerImplTest.java b/server/src/test/java/com/cloud/server/ManagementServerImplTest.java index ba9fff5ad40c..4cb2b61f94d3 100644 --- a/server/src/test/java/com/cloud/server/ManagementServerImplTest.java +++ b/server/src/test/java/com/cloud/server/ManagementServerImplTest.java @@ -252,7 +252,7 @@ public void setParametersTestWhenStateIsFreeAndSystemVmPublicIsFalse() throws No Mockito.verify(sc, Mockito.times(1)).setParameters("display", false); Mockito.verify(sc, Mockito.times(1)).setParameters("sourceNetworkId", 10L); Mockito.verify(sc, Mockito.times(1)).setParameters("state", "Free"); - Mockito.verify(sc, Mockito.never()).setParameters("forsystemvms", false); + Mockito.verify(sc, Mockito.times(1)).setParameters("forsystemvms", false); } @Test @@ -274,7 +274,7 @@ public void setParametersTestWhenStateIsNullAndSystemVmPublicIsFalse() throws No Mockito.verify(sc, Mockito.times(1)).setJoinParameters("vlanSearch", "vlanType", VlanType.VirtualNetwork); Mockito.verify(sc, Mockito.times(1)).setParameters("display", false); Mockito.verify(sc, Mockito.times(1)).setParameters("sourceNetworkId", 10L); - Mockito.verify(sc, Mockito.never()).setParameters("forsystemvms", false); + Mockito.verify(sc, Mockito.times(1)).setParameters("forsystemvms", false); } @Test @@ -296,7 +296,7 @@ public void setParametersTestWhenStateIsNullAndSystemVmPublicIsTrue() throws NoS Mockito.verify(sc, Mockito.times(1)).setJoinParameters("vlanSearch", "vlanType", VlanType.VirtualNetwork); Mockito.verify(sc, Mockito.times(1)).setParameters("display", false); Mockito.verify(sc, Mockito.times(1)).setParameters("sourceNetworkId", 10L); - Mockito.verify(sc, Mockito.never()).setParameters("forsystemvms", false); + Mockito.verify(sc, Mockito.times(1)).setParameters("forsystemvms", false); } @Test diff --git a/server/src/test/java/com/cloud/user/AccountManagerImplVolumeDeleteEventTest.java b/server/src/test/java/com/cloud/user/AccountManagerImplVolumeDeleteEventTest.java index c4e54b738250..4004321b8e90 100644 --- a/server/src/test/java/com/cloud/user/AccountManagerImplVolumeDeleteEventTest.java +++ b/server/src/test/java/com/cloud/user/AccountManagerImplVolumeDeleteEventTest.java @@ -37,6 +37,7 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; @@ -123,10 +124,10 @@ protected void defineMocksBehavior() throws AgentUnavailableException, Concurren DomainVO domain = new DomainVO(); VirtualMachineEntity vmEntity = mock(VirtualMachineEntity.class); - when(_orchSrvc.getVirtualMachine(nullable(String.class))).thenReturn(vmEntity); - when(vmEntity.destroy(nullable(Boolean.class))).thenReturn(true); + lenient().when(_orchSrvc.getVirtualMachine(nullable(String.class))).thenReturn(vmEntity); + lenient().when(vmEntity.destroy(nullable(Boolean.class))).thenReturn(true); - Mockito.lenient().doReturn(vm).when(_vmDao).findById(nullable(Long.class)); + lenient().doReturn(vm).when(_vmDao).findById(nullable(Long.class)); VolumeVO vol = new VolumeVO(VOLUME_UUID, 1l, 1l, 1l, 1l, 1l, "folder", "path", null, 50, Type.ROOT); vol.setDisplayVolume(true); @@ -136,20 +137,20 @@ protected void defineMocksBehavior() throws AgentUnavailableException, Concurren lenient().when(securityChecker.checkAccess(Mockito.eq(account), nullable(ControlledEntity.class), nullable(AccessType.class), nullable(String.class))).thenReturn(true); - when(_userVmDao.findById(nullable(Long.class))).thenReturn(vm); + lenient().when(_userVmDao.findById(nullable(Long.class))).thenReturn(vm); lenient().when(_userVmDao.listByAccountId(ACCOUNT_ID)).thenReturn(Arrays.asList(vm)); lenient().when(_userVmDao.findByUuid(nullable(String.class))).thenReturn(vm); - when(_volumeDao.findByInstance(nullable(Long.class))).thenReturn(volumes); + lenient().when(_volumeDao.findByInstance(nullable(Long.class))).thenReturn(volumes); ServiceOfferingVO offering = mock(ServiceOfferingVO.class); lenient().when(offering.getCpu()).thenReturn(500); lenient().when(offering.getId()).thenReturn(1l); - when(serviceOfferingDao.findByIdIncludingRemoved(nullable(Long.class), nullable(Long.class))).thenReturn(offering); + lenient().when(serviceOfferingDao.findByIdIncludingRemoved(nullable(Long.class), nullable(Long.class))).thenReturn(offering); lenient().when(_domainMgr.getDomain(nullable(Long.class))).thenReturn(domain); - Mockito.lenient().doReturn(true).when(_vmMgr).expunge(any(UserVmVO.class)); + Mockito.doReturn(true).when(_vmMgr).expunge(any(UserVmVO.class)); } @@ -190,22 +191,22 @@ protected List deleteUserAccountRootVolumeUsageEvents(boolean vmDe // If the VM is already destroyed, no events should get emitted public void destroyedVMRootVolumeUsageEvent() throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException { - Mockito.lenient().doReturn(vm).when(_vmMgr).destroyVm(nullable(Long.class), nullable(Boolean.class)); + lenient().doReturn(vm).when(_vmMgr).destroyVm(nullable(Long.class), nullable(Boolean.class)); List emittedEvents = deleteUserAccountRootVolumeUsageEvents(true); Assert.assertEquals(0, emittedEvents.size()); } + @Ignore() @Test // If the VM is running, we should see one emitted event for the root // volume. public void runningVMRootVolumeUsageEvent() throws SecurityException, IllegalArgumentException, ReflectiveOperationException, AgentUnavailableException, ConcurrentOperationException, CloudException { Mockito.doNothing().when(vmStatsDaoMock).removeAllByVmId(Mockito.anyLong()); - Mockito.lenient().when(_vmMgr.destroyVm(nullable(Long.class), nullable(Boolean.class))).thenReturn(vm); + Mockito.when(_vmMgr.destroyVm(nullable(Long.class), nullable(Boolean.class))).thenReturn(vm); List emittedEvents = deleteUserAccountRootVolumeUsageEvents(false); UsageEventVO event = emittedEvents.get(0); Assert.assertEquals(EventTypes.EVENT_VOLUME_DELETE, event.getType()); Assert.assertEquals(VOLUME_UUID, event.getResourceName()); - } } diff --git a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java index 1d9b65afd1bd..f9f665506573 100644 --- a/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockConfigurationManagerImpl.java @@ -546,7 +546,7 @@ public NetworkOfferingVO createNetworkOffering(String name, String displayText, Integer networkRate, Map> serviceProviderMap, boolean isDefault, GuestType type, boolean systemOnly, Long serviceOfferingId, boolean conserveMode, Map> serviceCapabilityMap, boolean specifyIpRanges, boolean isPersistent, Map details, boolean egressDefaultPolicy, Integer maxconn, boolean enableKeepAlive, Boolean forVpc, - Boolean forTungsten, List domainIds, List zoneIds, boolean enableOffering, NetUtils.InternetProtocol internetProtocol) { + Boolean forTungsten, boolean forNsx, String mode, List domainIds, List zoneIds, boolean enableOffering, NetUtils.InternetProtocol internetProtocol) { // TODO Auto-generated method stub return null; } @@ -556,7 +556,7 @@ public NetworkOfferingVO createNetworkOffering(String name, String displayText, */ @Override public Vlan createVlanAndPublicIpRange(long zoneId, long networkId, long physicalNetworkId, boolean forVirtualNetwork, boolean forSystemVms, Long podId, String startIP, String endIP, - String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanGatewayv6, String vlanCidrv6) + String vlanGateway, String vlanNetmask, String vlanId, boolean bypassVlanOverlapCheck, Domain domain, Account vlanOwner, String startIPv6, String endIPv6, String vlanGatewayv6, String vlanCidrv6, boolean forNsx) throws InsufficientCapacityException, ConcurrentOperationException, InvalidParameterValueException { // TODO Auto-generated method stub return null; diff --git a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java index 434c644f5969..106fc7fa5439 100644 --- a/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/src/test/java/com/cloud/vpc/MockNetworkManagerImpl.java @@ -26,6 +26,7 @@ import com.cloud.dc.DataCenter; import com.cloud.network.PublicIpQuarantine; +import com.cloud.network.VirtualRouterProvider; import com.cloud.utils.fsm.NoTransitionException; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.api.command.admin.address.ReleasePodIpCmdByAdmin; @@ -46,6 +47,7 @@ import org.apache.cloudstack.api.command.user.vm.ListNicsCmd; import org.apache.cloudstack.api.response.AcquirePodIpCmdResponse; import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; +import org.apache.cloudstack.network.element.InternalLoadBalancerElementService; import org.springframework.stereotype.Component; import com.cloud.deploy.DataCenterDeployment; @@ -181,6 +183,11 @@ public IpAddress reserveIpAddress(Account account, Boolean displayIp, Long ipAdd return null; } + @Override + public IpAddress reserveIpAddressWithVlanDetail(Account account, DataCenter zone, Boolean displayIp, String vlanDetailKey) throws ResourceAllocationException { + return null; + } + @Override public boolean releaseReservedIpAddress(long ipAddressId) throws InsufficientAddressCapacityException { return false; @@ -1081,4 +1088,24 @@ public PublicIpQuarantine updatePublicIpAddressInQuarantine(UpdateQuarantinedIpC public void removePublicIpAddressFromQuarantine(RemoveQuarantinedIpCmd cmd) { } + + @Override + public InternalLoadBalancerElementService getInternalLoadBalancerElementByType(VirtualRouterProvider.Type type) { + return null; + } + + @Override + public InternalLoadBalancerElementService getInternalLoadBalancerElementByNetworkServiceProviderId(long networkProviderId) { + return null; + } + + @Override + public InternalLoadBalancerElementService getInternalLoadBalancerElementById(long providerId) { + return null; + } + + @Override + public List getInternalLoadBalancerElements() { + return null; + } } diff --git a/server/src/test/java/com/cloud/vpc/NetworkACLServiceTest.java b/server/src/test/java/com/cloud/vpc/NetworkACLServiceTest.java index d2f00c409ae9..6579a892ec6b 100644 --- a/server/src/test/java/com/cloud/vpc/NetworkACLServiceTest.java +++ b/server/src/test/java/com/cloud/vpc/NetworkACLServiceTest.java @@ -18,6 +18,7 @@ import com.cloud.exception.InvalidParameterValueException; import com.cloud.network.NetworkModel; import com.cloud.network.dao.NetworkDao; +import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.vpc.NetworkACLItemDao; import com.cloud.network.vpc.NetworkACLItemVO; import com.cloud.network.vpc.NetworkACLManager; @@ -78,6 +79,8 @@ public class NetworkACLServiceTest extends TestCase { private NetworkACLItemDao _networkACLItemDao; @Inject private EntityManager _entityMgr; + @Inject + private NsxProviderDao nsxProviderDao; private NetworkACLVO acl; private NetworkACLItemVO aclItem; @@ -184,6 +187,9 @@ public VpcService vpcService() { return Mockito.mock(VpcService.class); } + @Bean + public NsxProviderDao nsxProviderDao() { return Mockito.mock(NsxProviderDao.class); } + public static class Library implements TypeFilter { @Override public boolean match(MetadataReader mdr, MetadataReaderFactory arg1) throws IOException { diff --git a/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java b/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java index f9d6d0dfcbae..679324fed2f9 100644 --- a/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java +++ b/server/src/test/java/org/apache/cloudstack/network/router/deployment/RouterDeploymentDefinitionTest.java @@ -31,6 +31,7 @@ import com.cloud.network.VirtualRouterProvider.Type; import com.cloud.network.addr.PublicIp; import com.cloud.network.dao.NetworkVO; +import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderVO; import com.cloud.network.element.VirtualRouterProviderVO; import com.cloud.network.router.VirtualRouter.Role; @@ -73,6 +74,8 @@ public class RouterDeploymentDefinitionTest extends RouterDeploymentDefinitionTe @Mock protected NetworkVO mockNw; + @Mock + protected NsxProviderDao nsxProviderDao; protected RouterDeploymentDefinition deployment; diff --git a/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java b/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java index 969fb1f209c6..a355ad21f2ba 100644 --- a/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java +++ b/server/src/test/java/org/apache/cloudstack/network/router/deployment/VpcRouterDeploymentDefinitionTest.java @@ -24,6 +24,7 @@ import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; import com.cloud.network.addr.PublicIp; +import com.cloud.network.dao.NsxProviderDao; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkServiceProviderDao; import com.cloud.network.router.NicProfileHelper; @@ -62,6 +63,8 @@ public class VpcRouterDeploymentDefinitionTest extends RouterDeploymentDefinitio @Mock protected VpcDao mockVpcDao; @Mock + protected NsxProviderDao nsxProviderDao; + @Mock protected PhysicalNetworkDao mockPhNwDao; protected PhysicalNetworkServiceProviderDao mockPhProviderDao; diff --git a/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java b/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java index d0b7ace47112..838bb3dadcb4 100644 --- a/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java +++ b/server/src/test/java/org/apache/cloudstack/networkoffering/CreateNetworkOfferingTest.java @@ -43,6 +43,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import com.cloud.configuration.ConfigurationManager; +import com.cloud.dc.dao.VlanDetailsDao; import com.cloud.event.dao.UsageEventDao; import com.cloud.event.dao.UsageEventDetailsDao; import com.cloud.exception.InvalidParameterValueException; @@ -103,6 +104,8 @@ public class CreateNetworkOfferingTest extends TestCase { @Inject AnnotationDao annotationDao; + @Inject + VlanDetailsDao vlanDetailsDao; @Inject PublicIpQuarantineDao publicIpQuarantineDao; @@ -135,7 +138,7 @@ public void tearDown() { public void createSharedNtwkOffWithVlan() { NetworkOfferingVO off = configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, true, Availability.Optional, 200, null, false, Network.GuestType.Shared, false, - null, false, null, true, false, null, false, null, true, false, false, null, null, false, null); + null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null); assertNotNull("Shared network offering with specifyVlan=true failed to create ", off); } @@ -143,7 +146,7 @@ public void createSharedNtwkOffWithVlan() { public void createSharedNtwkOffWithNoVlan() { NetworkOfferingVO off = configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, false, Availability.Optional, 200, null, false, Network.GuestType.Shared, - false, null, false, null, true, false, null, false, null, true, false, false, null, null, false, null); + false, null, false, null, true, false, null, false, null, true, false, false, false, null, null,null, false, null); assertNotNull("Shared network offering with specifyVlan=false was created", off); } @@ -151,7 +154,7 @@ public void createSharedNtwkOffWithNoVlan() { public void createSharedNtwkOffWithSpecifyIpRanges() { NetworkOfferingVO off = configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, true, Availability.Optional, 200, null, false, Network.GuestType.Shared, false, - null, false, null, true, false, null, false, null, true, false, false, null, null, false, null); + null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null); assertNotNull("Shared network offering with specifyIpRanges=true failed to create ", off); } @@ -160,7 +163,7 @@ public void createSharedNtwkOffWithSpecifyIpRanges() { public void createSharedNtwkOffWithoutSpecifyIpRanges() { NetworkOfferingVO off = configMgr.createNetworkOffering("shared", "shared", TrafficType.Guest, null, true, Availability.Optional, 200, null, false, Network.GuestType.Shared, - false, null, false, null, false, false, null, false, null, true, false, false, null, null, false, null); + false, null, false, null, false, false, null, false, null, true, false, false, false, null,null, null, false, null); assertNull("Shared network offering with specifyIpRanges=false was created", off); } @@ -173,7 +176,7 @@ public void createIsolatedNtwkOffWithNoVlan() { serviceProviderMap.put(Network.Service.SourceNat, vrProvider); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, false, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, false, null, null, null, false, null); assertNotNull("Isolated network offering with specifyIpRanges=false failed to create ", off); } @@ -186,7 +189,7 @@ public void createIsolatedNtwkOffWithVlan() { serviceProviderMap.put(Network.Service.SourceNat, vrProvider); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, true, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, false, false, false, null,null, null, false, null); assertNotNull("Isolated network offering with specifyVlan=true wasn't created", off); } @@ -199,7 +202,7 @@ public void createIsolatedNtwkOffWithSpecifyIpRangesAndSourceNat() { serviceProviderMap.put(Network.Service.SourceNat, vrProvider); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, false, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null); assertNull("Isolated network offering with specifyIpRanges=true and source nat service enabled, was created", off); } @@ -210,7 +213,7 @@ public void createIsolatedNtwkOffWithSpecifyIpRangesAndNoSourceNat() { Set vrProvider = new HashSet(); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, false, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, true, false, null, false, null, true, false, false, false, null,null, null, false, null); assertNotNull("Isolated network offering with specifyIpRanges=true and with no sourceNatService, failed to create", off); } @@ -228,7 +231,7 @@ public void createVpcNtwkOff() { serviceProviderMap.put(Network.Service.Lb, vrProvider); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, true, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, false, null); // System.out.println("Creating Vpc Network Offering"); assertNotNull("Vpc Isolated network offering with Vpc provider ", off); } @@ -248,7 +251,7 @@ public void createVpcNtwkOffWithNetscaler() { serviceProviderMap.put(Network.Service.Lb, lbProvider); NetworkOfferingVO off = configMgr.createNetworkOffering("isolated", "isolated", TrafficType.Guest, null, true, Availability.Optional, 200, serviceProviderMap, false, - Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, null, null, false, null); + Network.GuestType.Isolated, false, null, false, null, false, false, null, false, null, true, true, false, false, null, null, null, false, null); // System.out.println("Creating Vpc Network Offering"); assertNotNull("Vpc Isolated network offering with Vpc and Netscaler provider ", off); } diff --git a/server/src/test/resources/createNetworkOffering.xml b/server/src/test/resources/createNetworkOffering.xml index 28e602720e86..5ee4f1768476 100644 --- a/server/src/test/resources/createNetworkOffering.xml +++ b/server/src/test/resources/createNetworkOffering.xml @@ -1,19 +1,19 @@ - @@ -72,5 +72,8 @@ + + + diff --git a/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py b/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py index a934862c224e..41b8b6441867 100755 --- a/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py +++ b/systemvm/debian/opt/cloud/bin/cs/CsGuestNetwork.py @@ -40,7 +40,7 @@ def get_dns(self): return self.config.get_dns() dns = [] - if 'router_guest_gateway' in self.data and not self.config.use_extdns(): + if 'router_guest_gateway' in self.data and not self.config.use_extdns() and 'is_vr_guest_gateway' not in self.data: dns.append(self.data['router_guest_gateway']) if 'dns' in self.data: diff --git a/tools/apidoc/gen_toc.py b/tools/apidoc/gen_toc.py index b971d2449417..9df6bf9efc5c 100644 --- a/tools/apidoc/gen_toc.py +++ b/tools/apidoc/gen_toc.py @@ -134,6 +134,9 @@ 'removeTungstenFabricNetworkGatewayFromLogicalRouter': 'Tungsten', 'updateTungstenFabricLBHealthMonitor': 'Tungsten', 'listTungstenFabricLBHealthMonitor': 'Tungsten', + 'listNsxControllers': 'NSX', + 'addNsxController': 'NSX', + 'deleteNsxController': 'NSX', 'Vpn': 'VPN', 'Limit': 'Limit', 'ResourceCount': 'Limit', diff --git a/ui/public/locales/en.json b/ui/public/locales/en.json index 1fa2318c37c7..08d7d82b1a18 100644 --- a/ui/public/locales/en.json +++ b/ui/public/locales/en.json @@ -15,8 +15,8 @@ "error.release.dedicate.zone": "Failed to release dedicated zone.", "error.unable.to.proceed": "Unable to proceed. Please contact your administrator.", "firewall.close": "Firewall", -"icmp.code.desc": "Please specify -1 if you want to allow all ICMP codes.", -"icmp.type.desc": "Please specify -1 if you want to allow all ICMP types.", +"icmp.code.desc": "Please specify -1 if you want to allow all ICMP codes (except NSX zones).", +"icmp.type.desc": "Please specify -1 if you want to allow all ICMP types (except NSX zones).", "inline": "Inline", "label.about": "About", "label.about.app": "About CloudStack", @@ -913,6 +913,8 @@ "label.forceencap": "Force UDP encapsulation of ESP packets", "label.forgedtransmits": "Forged transmits", "label.format": "Format", +"label.fornsx": "NSX", +"label.forvpc": "VPC", "label.free": "Free", "label.french.azerty.keyboard": "French AZERTY keyboard", "label.friday": "Friday", @@ -1051,7 +1053,7 @@ "label.internaldns2": "Internal DNS 2", "label.internallb.description": "Brief description of the internal LB.", "label.internallb.name.description": "Unique name for internal LB.", -"label.internallb.sourceip.description": "Brief description of the internal LB.", +"label.internallb.sourceip.description": "Source IP address the network traffic will be load balanced from", "label.internallbvm": "InternalLbVm", "label.internetprotocol": "Internet protocol", "label.interval": "Polling interval (in sec)", @@ -1340,6 +1342,7 @@ "label.minorsequence": "Minor Sequence", "label.minsize": "Minimum size", "label.minute.past.hour": "minute(s) past the hour", +"label.mode": "Mode", "label.monday": "Monday", "label.monitor": "Monitor", "label.monitor.expected.code": "Expected HTTP Status Code", @@ -1434,6 +1437,19 @@ "label.not.found": "Not found", "label.not.suitable": "Not suitable", "label.notifications": "Notifications", +"label.nsx": "NSX", +"label.nsxmode": "NSX Mode", +"label.nsx.provider": "NSX Provider", +"label.nsx.provider.name": "NSX provider name", +"label.nsx.provider.hostname": "NSX provider hostname", +"label.nsx.provider.port": "NSX provider port", +"label.nsx.provider.username": "NSX provider username", +"label.nsx.provider.password": "NSX provider password", +"label.nsx.provider.edgecluster": "NSX provider edge cluster", +"label.nsx.provider.tier0gateway": "NSX provider tier-0 gateway", +"label.nsx.provider.transportzone": "NSX provider transport zone", +"label.nsx.supports.internal.lb": "Enable NSX internal LB service", +"label.nsx.supports.lb": "Enable NSX LB service", "label.num.cpu.cores": "# of CPU cores", "label.number": "#Rule", "label.numretries": "Number of retries", @@ -1621,6 +1637,7 @@ "label.public.ips": "Public IP addresses", "label.public.lb": "Public LB", "label.public.traffic": "Public traffic", +"label.public.traffic.nsx": "NSX Public traffic", "label.publicinterface": "Public interface", "label.publicip": "IP address", "label.publicipid": "IP address ID", @@ -2045,7 +2062,9 @@ "label.systemvm": "System VM", "label.systemvmtype": "System VM type", "label.tag": "Tag", +"label.tag.nsx": "nsx", "label.tag.key": "Tag key", +"label.tag.systemvm": "systemvm", "label.tag.value": "Tag value", "label.tagged": "Tagged", "label.tagged.limits": "Tagged limits", @@ -2498,11 +2517,12 @@ "message.remove.ip.v6.firewall.rule.failed": "Failed to remove IPv6 firewall rule", "message.remove.ip.v6.firewall.rule.processing": "Removing IPv6 firewall rule...", "message.remove.ip.v6.firewall.rule.success": "Removed IPv6 firewall rule", -"message.add.network": "Add a new Network for zone: ", -"message.add.network.acl.failed": "Adding Network ACL list failed.", -"message.add.network.acl.processing": "Adding Network ACL list...", -"message.add.network.failed": "Adding Network failed.", -"message.add.network.processing": "Adding Network...", +"message.add.nsx.controller": "Add NSX Provider", +"message.add.network": "Add a new network for zone: ", +"message.add.network.acl.failed": "Adding network ACL list failed.", +"message.add.network.acl.processing": "Adding network ACL list...", +"message.add.network.failed": "Adding network failed.", +"message.add.network.processing": "Adding network...", "message.add.new.gateway.to.vpc": "Please specify the information to add a new gateway to this VPC.", "message.add.physical.network.failed": "Adding physical network failed", "message.add.physical.network.processing": "Adding a new physical network...", @@ -2575,6 +2595,7 @@ "message.configuring.guest.traffic": "Configuring guest traffic", "message.configuring.physical.networks": "Configuring physical Networks", "message.configuring.public.traffic": "Configuring public traffic", +"message.configuring.nsx.public.traffic": "Configuring NSX public traffic", "message.configuring.storage.traffic": "Configuring storage traffic", "message.confirm.action.force.reconnect": "Please confirm that you want to force reconnect this host.", "message.confirm.add.router.table.to.instance": "Please confirm that you want to add Route Table to this NIC", @@ -2916,6 +2937,7 @@ "message.import.running.instance.warning": "The selected VM is powered-on on the VMware Datacenter. The recommended state to convert a VMware VM into KVM is powered-off after a graceful shutdown of the guest OS.", "message.info.cloudian.console": "Cloudian Management Console should open in another window.", "message.installwizard.cloudstack.helptext.website": " * Project website:\t ", +"message.infra.setup.nsx.description": "This zone must contain an NSX provider because the isolation method is NSX", "message.infra.setup.tungsten.description": "This zone must contain a Tungsten-Fabric provider because the isolation method is TF", "message.installwizard.cloudstack.helptext.document": " * Documentation:\t ", "message.installwizard.cloudstack.helptext.header": "\nYou can find more information about Apache CloudStackâ„¢ on the pages listed below.\n", @@ -2932,6 +2954,12 @@ "message.installwizard.tooltip.configureguesttraffic.guestgateway": "The gateway that the guests should use.", "message.installwizard.tooltip.configureguesttraffic.guestnetmask": "The netmask in use on the subnet that the guests should use.", "message.installwizard.tooltip.configureguesttraffic.gueststartip": "The range of IP addresses that will be available for allocation to guests in this zone. If one NIC is used, these IPs should be in the same CIDR as the pod CIDR.", +"message.installwizard.tooltip.nsx.provider.hostname": "NSX Provider hostname / IP address not provided", +"message.installwizard.tooltip.nsx.provider.username": "NSX Provider username not provided", +"message.installwizard.tooltip.nsx.provider.password": "NSX Provider password not provided", +"message.installwizard.tooltip.nsx.provider.edgecluster": "NSX Provider edge cluster information not provided", +"message.installwizard.tooltip.nsx.provider.tier0gateway": "NSX Provider tier-0 gateway information not provided", +"message.installwizard.tooltip.nsx.provider.transportZone": "NSX Provider transport zone information not provided", "message.installwizard.tooltip.tungsten.provider.gateway": "Tungsten provider gateway is required", "message.installwizard.tooltip.tungsten.provider.hostname": "Tungsten provider hostname is required", "message.installwizard.tooltip.tungsten.provider.introspectport": "Tungsten provider introspect port is required", @@ -2952,6 +2980,7 @@ "message.kubernetes.cluster.stop": "Please confirm that you want to stop the cluster.", "message.kubernetes.cluster.upgrade": "Please select new Kubernetes version.", "message.kubernetes.version.delete": "Please confirm that you want to delete this Kubernetes version.", +"message.l2.network.unsupported.for.nsx": "L2 networks aren't supported for NSX enabled zones", "message.launch.zone": "Zone is ready to launch; please proceed to the next step.", "message.launch.zone.description": "Zone is ready to launch; please proceed to the next step.", "message.launch.zone.hint": "Configure Network components and traffic including IP addresses.", @@ -3102,6 +3131,7 @@ "message.setup.physical.network.during.zone.creation": "When adding a zone, you need to set up one or more physical networks. Each physical network can carry one or more types of traffic, with certain restrictions on how they may be combined. Add or remove one or more traffic types onto each physical network.", "message.setup.physical.network.during.zone.creation.basic": "When adding a basic zone, you can set up one physical Network, which corresponds to a NIC on the hypervisor. The Network carries several types of traffic.

You may also add other traffic types onto the physical Network.", "message.shared.network.offering.warning": "Domain admins and regular Users can only create shared Networks from Network offering with the setting specifyvlan=false. Please contact an administrator to create a Network offering if this list is empty.", +"message.shared.network.unsupported.for.nsx": "Shared networks aren't supported for NSX enabled zones", "message.shutdown.triggered": "A shutdown has been triggered. CloudStack will not accept new jobs", "message.snapshot.additional.zones": "Snapshots will always be created in its native zone - %x, here you can select additional zone(s) where it will be copied to at creation time", "message.sourcenatip.change.warning": "WARNING: Changing the sourcenat IP address of the network will cause connectivity downtime for the Instances with NICs in the Network.", diff --git a/ui/src/components/CheckBoxSelectPair.vue b/ui/src/components/CheckBoxSelectPair.vue index de6aed473cbc..4fba1da25565 100644 --- a/ui/src/components/CheckBoxSelectPair.vue +++ b/ui/src/components/CheckBoxSelectPair.vue @@ -21,6 +21,7 @@ {{ checkBoxLabel }} @@ -30,7 +31,8 @@ v-if="reversed !== checked" :label="selectLabel">

- {{ service.name }} : {{ service.provider[0].name }} + {{ service.name }} : {{ service.provider?.[0]?.name }}
diff --git a/ui/src/components/view/ResourceView.vue b/ui/src/components/view/ResourceView.vue index 367c58961bc8..2c1764da1437 100644 --- a/ui/src/components/view/ResourceView.vue +++ b/ui/src/components/view/ResourceView.vue @@ -114,15 +114,7 @@ export default { handler (newItem, oldItem) { if (newItem.id === oldItem.id) return - if (this.resource.associatednetworkid) { - api('listNetworks', { id: this.resource.associatednetworkid, listall: true }).then(response => { - if (response && response.listnetworksresponse && response.listnetworksresponse.network) { - this.networkService = response.listnetworksresponse.network[0] - } else { - this.networkService = {} - } - }) - } + this.fetchData() } }, '$route.fullPath': function () { @@ -140,8 +132,20 @@ export default { window.addEventListener('popstate', function () { self.setActiveTab() }) + this.fetchData() }, methods: { + fetchData () { + if (this.resource.associatednetworkid) { + api('listNetworks', { id: this.resource.associatednetworkid, listall: true }).then(response => { + if (response && response.listnetworksresponse && response.listnetworksresponse.network) { + this.networkService = response.listnetworksresponse.network[0] + } else { + this.networkService = {} + } + }) + } + }, onTabChange (key) { this.activeTab = key const query = Object.assign({}, this.$route.query) diff --git a/ui/src/config/section/offering.js b/ui/src/config/section/offering.js index 65daaf53ae09..75ba6f5361b2 100644 --- a/ui/src/config/section/offering.js +++ b/ui/src/config/section/offering.js @@ -269,7 +269,7 @@ export default { docHelp: 'adminguide/networking.html#network-offerings', permission: ['listNetworkOfferings'], columns: ['name', 'state', 'guestiptype', 'traffictype', 'networkrate', 'domain', 'zone', 'order'], - details: ['name', 'id', 'displaytext', 'guestiptype', 'traffictype', 'internetprotocol', 'networkrate', 'ispersistent', 'egressdefaultpolicy', 'availability', 'conservemode', 'specifyvlan', 'specifyipranges', 'supportspublicaccess', 'supportsstrechedl2subnet', 'service', 'tags', 'domain', 'zone'], + details: ['name', 'id', 'displaytext', 'guestiptype', 'traffictype', 'internetprotocol', 'networkrate', 'ispersistent', 'egressdefaultpolicy', 'availability', 'conservemode', 'specifyvlan', 'specifyipranges', 'supportspublicaccess', 'supportsstrechedl2subnet', 'forvpc', 'fornsx', 'nsxmode', 'service', 'tags', 'domain', 'zone'], resourceType: 'NetworkOffering', tabs: [ { @@ -362,7 +362,7 @@ export default { permission: ['listVPCOfferings'], resourceType: 'VpcOffering', columns: ['name', 'state', 'displaytext', 'domain', 'zone', 'order'], - details: ['name', 'id', 'displaytext', 'internetprotocol', 'distributedvpcrouter', 'tags', 'service', 'domain', 'zone', 'created'], + details: ['name', 'id', 'displaytext', 'internetprotocol', 'distributedvpcrouter', 'tags', 'service', 'fornsx', 'nsxmode', 'domain', 'zone', 'created'], related: [{ name: 'vpc', title: 'label.vpc', diff --git a/ui/src/views/infra/network/ServiceProvidersTab.vue b/ui/src/views/infra/network/ServiceProvidersTab.vue index 4985389ba605..b01f543478ba 100644 --- a/ui/src/views/infra/network/ServiceProvidersTab.vue +++ b/ui/src/views/infra/network/ServiceProvidersTab.vue @@ -1056,6 +1056,50 @@ export default { columns: ['name', 'tungstenproviderhostname', 'tungstenproviderport', 'tungstengateway', 'tungstenprovidervrouterport', 'tungstenproviderintrospectport'] } ] + }, + { + title: 'Nsx', + details: ['name', 'state', 'id', 'physicalnetworkid', 'servicelist'], + actions: [ + { + api: 'updateNetworkServiceProvider', + icon: 'stop-outlined', + listView: true, + label: 'label.disable.provider', + confirm: 'message.confirm.disable.provider', + // show: (record) => { return record && record.id && record.state === 'Enabled' }, + mapping: { + state: { + value: (record) => { return 'Disabled' } + } + } + }, + { + api: 'updateNetworkServiceProvider', + icon: 'play-circle-outlined', + listView: true, + label: 'label.enable.provider', + confirm: 'message.confirm.enable.provider', + // show: (record) => { return record && record.id && record.state === 'Disabled' }, + mapping: { + state: { + value: (record) => { return 'Enabled' } + } + } + } + ], + lists: [ + { + title: 'label.nsx.controller', + api: 'listNsxControllers', + mapping: { + zoneid: { + value: (record) => { return record.zoneid } + } + }, + columns: ['name', 'hostname', 'port', 'tier0gateway', 'edgecluster', 'transportzone'] + } + ] } ] } @@ -1096,6 +1140,7 @@ export default { this.fetchLoading = true api('listNetworkServiceProviders', { physicalnetworkid: this.resource.id, name: name }).then(json => { const sps = json.listnetworkserviceprovidersresponse.networkserviceprovider || [] + console.log(sps) if (sps.length > 0) { for (const sp of sps) { this.nsps[sp.name] = sp diff --git a/ui/src/views/infra/zone/IpAddressRangeForm.vue b/ui/src/views/infra/zone/IpAddressRangeForm.vue index 22332952622f..c39534d7b93e 100644 --- a/ui/src/views/infra/zone/IpAddressRangeForm.vue +++ b/ui/src/views/infra/zone/IpAddressRangeForm.vue @@ -31,9 +31,15 @@ :pagination="false" style="margin-bottom: 24px; width: 100%" >