This is an automated email from the ASF dual-hosted git repository. pearl11594 pushed a commit to branch nsx-static-nat in repository https://gitbox.apache.org/repos/asf/cloudstack.git
commit be49197bc5625a1a0ea04e774af3af1a98c4a2bd Author: Pearl Dsilva <[email protected]> AuthorDate: Wed Oct 25 18:42:44 2023 -0400 Support to add and delete Port forward rules --- .../agent/api/CreateNsxPortForwardRuleCommand.java | 36 ++++ .../agent/api/CreateNsxStaticNatCommand.java | 75 +------ .../agent/api/DeleteNsxNatRuleCommand.java | 25 +++ .../agent/api/DeleteNsxStaticNatCommand.java | 43 ---- .../cloudstack/agent/api/NsxNetworkCommand.java | 87 ++++++++ .../apache/cloudstack/resource/NsxNetworkRule.java | 226 +++++++++++++++++++++ .../apache/cloudstack/resource/NsxResource.java | 55 +++-- .../apache/cloudstack/service/NsxApiClient.java | 115 ++++++++++- .../org/apache/cloudstack/service/NsxElement.java | 102 +++++++++- .../apache/cloudstack/service/NsxServiceImpl.java | 49 +++-- .../apache/cloudstack/service/NsxServiceList.java | 17 ++ .../cloudstack/utils/NsxControllerUtils.java | 9 +- .../com/cloud/server/ConfigurationServerImpl.java | 2 + 13 files changed, 691 insertions(+), 150 deletions(-) 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 00000000000..6c5a261db6f --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/CreateNsxPortForwardRuleCommand.java @@ -0,0 +1,36 @@ +package org.apache.cloudstack.agent.api; + +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; + } +} 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 index 9768b9ecb93..f32bc6888c2 100644 --- 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 @@ -1,76 +1,9 @@ package org.apache.cloudstack.agent.api; -import java.util.Objects; +public class CreateNsxStaticNatCommand extends NsxNetworkCommand { -public class CreateNsxStaticNatCommand extends NsxCommand { - - private long vpcId; - private String vpcName; - private long vmId; - private String publicIp; - private String vmIp; - - public CreateNsxStaticNatCommand(long domainId, long accountId, long zoneId, long vpcId, String vpcName, - long vmId, String publicIp, String vmIp) { - super(domainId, accountId, zoneId); - this.vpcId = vpcId; - this.vpcName = vpcName; - this.vmId = vmId; - this.publicIp = publicIp; - this.vmIp = vmIp; - } - - public long getVpcId() { - return vpcId; - } - - public void setVpcId(long vpcId) { - this.vpcId = vpcId; - } - - public String getVpcName() { - return vpcName; - } - - public void setVpcName(String vpcName) { - this.vpcName = vpcName; - } - - 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; - CreateNsxStaticNatCommand that = (CreateNsxStaticNatCommand) o; - return vpcId == that.vpcId && Objects.equals(publicIp, that.publicIp) && Objects.equals(vmIp, that.vmIp); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), vpcId, publicIp, vmIp); + 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/DeleteNsxNatRuleCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxNatRuleCommand.java new file mode 100644 index 00000000000..3d5b17f7ccf --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxNatRuleCommand.java @@ -0,0 +1,25 @@ +package org.apache.cloudstack.agent.api; + +import com.cloud.network.Network; + +public class DeleteNsxNatRuleCommand extends NsxNetworkCommand { + private Long ruleId; + private Network.Service service; + public DeleteNsxNatRuleCommand(long domainId, long accountId, long zoneId, Long networkResourceId, String networkResourceName, + boolean isResourceVpc, Long vmId, Long ruleId, String publicIp, String vmIp) { + super(domainId, accountId, zoneId, networkResourceId, networkResourceName, isResourceVpc, vmId, publicIp, vmIp); + this.ruleId = ruleId; + } + + public Long getRuleId() { + return ruleId; + } + + public Network.Service getService() { + return service; + } + + public void setService(Network.Service service) { + this.service = service; + } +} diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxStaticNatCommand.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxStaticNatCommand.java deleted file mode 100644 index a4c30c1c701..00000000000 --- a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/DeleteNsxStaticNatCommand.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.apache.cloudstack.agent.api; - -import java.util.Objects; - -public class DeleteNsxStaticNatCommand extends NsxCommand { - private long vpcId; - private String vpcName; - public DeleteNsxStaticNatCommand(long domainId, long accountId, long zoneId, long vpcId, String vpcName) { - super(domainId, accountId, zoneId); - this.vpcId = vpcId; - this.vpcName = vpcName; - } - - public long getVpcId() { - return vpcId; - } - - public void setVpcId(long vpcId) { - this.vpcId = vpcId; - } - - public String getVpcName() { - return vpcName; - } - - public void setVpcName(String vpcName) { - this.vpcName = vpcName; - } - - @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; - DeleteNsxStaticNatCommand that = (DeleteNsxStaticNatCommand) o; - return vpcId == that.vpcId && Objects.equals(vpcName, that.vpcName); - } - - @Override - public int hashCode() { - return Objects.hash(super.hashCode(), vpcId, vpcName); - } -} 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 00000000000..82c20fb97bd --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/agent/api/NsxNetworkCommand.java @@ -0,0 +1,87 @@ +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 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/resource/NsxNetworkRule.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java new file mode 100644 index 00000000000..fe2240b0297 --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/resource/NsxNetworkRule.java @@ -0,0 +1,226 @@ +package org.apache.cloudstack.resource; + +public class NsxNetworkRule { + 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; + + 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 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; + + public Builder() { + } + + 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 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); + return rule; + } + } +} + 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 index 7892938ce9a..0b32ac517ab 100644 --- 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 @@ -24,6 +24,7 @@ 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; @@ -35,11 +36,12 @@ import com.vmware.nsx_policy.model.SiteListResult; import org.apache.cloudstack.NsxAnswer; import org.apache.cloudstack.StartupNsxCommand; import org.apache.cloudstack.agent.api.CreateNsxDhcpRelayConfigCommand; +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.DeleteNsxSegmentCommand; -import org.apache.cloudstack.agent.api.DeleteNsxStaticNatCommand; +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; @@ -108,8 +110,10 @@ public class NsxResource implements ServerResource { return executeRequest((CreateNsxDhcpRelayConfigCommand) cmd); } else if (cmd instanceof CreateNsxStaticNatCommand) { return executeRequest((CreateNsxStaticNatCommand) cmd); - } else if (cmd instanceof DeleteNsxStaticNatCommand) { - return executeRequest((DeleteNsxStaticNatCommand) cmd); + } else if (cmd instanceof DeleteNsxNatRuleCommand) { + return executeRequest((DeleteNsxNatRuleCommand) cmd); + } else if (cmd instanceof CreateNsxPortForwardRuleCommand) { + return executeRequest((CreateNsxPortForwardRuleCommand) cmd); } else { return Answer.createUnsupportedCommandAnswer(cmd); } @@ -345,27 +349,52 @@ public class NsxResource implements ServerResource { private NsxAnswer executeRequest(CreateNsxStaticNatCommand cmd) { String staticNatRuleName = NsxControllerUtils.getStaticNatRuleName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), - cmd.getVpcId()); + cmd.getNetworkResourceId(), cmd.isResourceVpc()); String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), - cmd.getVpcId()); + cmd.getNetworkResourceId(), cmd.isResourceVpc()); try { - nsxApiClient.createStaticNatRule(cmd.getVpcName(), tier1GatewayName, staticNatRuleName, cmd.getPublicIp(), cmd.getVmIp()); + 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.getVpcName())); + 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(DeleteNsxStaticNatCommand cmd) { - String staticNatRuleName = NsxControllerUtils.getStaticNatRuleName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), - cmd.getVpcId()); + 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.getVpcId()); + cmd.getNetworkResourceId(), cmd.isResourceVpc()); try { - nsxApiClient.deleteStaticNatRule(cmd.getVpcName(), tier1GatewayName, staticNatRuleName); + nsxApiClient.deleteNatRule(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(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.createNsxInfraService(ruleName, privatePort, cmd.getProtocol()) : + nsxApiClient.getNsxInfraServices(ruleName, privatePort, cmd.getProtocol()); + + nsxApiClient.createPortForwardingRule(ruleName, tier1GatewayName, cmd.getNetworkResourceName(), cmd.getPublicIp(), + cmd.getVmIp(), cmd.getPublicPort(), service); } catch (Exception e) { - LOGGER.error(String.format("Failed to add NSX static NAT rule %s for network: %s", staticNatRuleName, cmd.getVpcName())); + 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); 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 index 3fafff9813b..815dad97ea0 100644 --- 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 @@ -22,6 +22,7 @@ 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.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.sites.EnforcementPoints; @@ -30,10 +31,12 @@ 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.L4PortSetServiceEntry; import com.vmware.nsx_policy.model.LocaleServicesListResult; import com.vmware.nsx_policy.model.PolicyNatRule; 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; @@ -48,10 +51,13 @@ import com.vmware.vapi.internal.protocol.client.rest.authn.BasicAuthenticationAp import com.vmware.vapi.protocol.HttpConfiguration; import com.vmware.vapi.std.errors.Error; import org.apache.cloudstack.utils.NsxControllerUtils; +import org.apache.commons.collections.CollectionUtils; import org.apache.log4j.Logger; import java.util.List; +import java.util.Objects; import java.util.function.Function; +import java.util.stream.Collectors; public class NsxApiClient { @@ -326,17 +332,120 @@ public class NsxApiClient { } } - public void deleteStaticNatRule(String vpcName, String tier1GatewayName, String ruleName) { + public void deleteNatRule(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 (VPC: %s)", ruleName, tier1GatewayName, vpcName)); + LOGGER.debug(String.format("Deleting NSX static NAT rule %s for tier-1 gateway %s (network: %s)", ruleName, tier1GatewayName, networkName)); natService.delete(tier1GatewayName, NatId.USER.name(), ruleName); } 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, vpcName, ae.getErrorMessage()); + 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 delete NSX Port-forward rule %s for network: %s, due to %s", + ruleName, networkName, ae.getErrorMessage()); + LOGGER.error(msg); + throw new CloudRuntimeException(msg); + } + } + + public String getNsxInfraServices(String ruleName, String port, String protocol) { + 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<com.vmware.nsx_policy.model.Service> services = serviceList.getResults(); + List<String> matchedDefaultSvc = services.parallelStream().filter(svc -> + (svc.getServiceEntries().get(0) instanceof L4PortSetServiceEntry) && + ((L4PortSetServiceEntry) svc.getServiceEntries().get(0)).getDestinationPorts().get(0).equals(port) + && (((L4PortSetServiceEntry) svc.getServiceEntries().get(0)).getL4Protocol().equals(protocol))) + .map(svc -> ((L4PortSetServiceEntry) svc.getServiceEntries().get(0)).getDestinationPorts().get(0)) + .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 createNsxInfraService(ruleName, port, protocol); + } 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); + } + } + + public String createNsxInfraService(String ruleName, String port, String protocol) { + try { + String serviceEntryName = ruleName + "-SE-" + port; + String serviceName = ruleName + "-SVC-" + port; + Services service = (Services) nsxService.apply(Services.class); + com.vmware.nsx_policy.model.Service infraService = new com.vmware.nsx_policy.model.Service.Builder() + .setServiceEntries(List.of( + new L4PortSetServiceEntry.Builder() + .setId(serviceEntryName) + .setDisplayName(serviceEntryName) + .setDestinationPorts(List.of(port)) + .setL4Protocol(protocol) + .build() + )) + .setId(serviceName) + .setDisplayName(serviceName) + .build(); + service.patch(serviceName, infraService); + + com.vmware.nsx_policy.model.Service svc = service.get(serviceName); + return svc.getServiceEntries().get(0)._getDataValue().getField("parent_path").toString(); + + } 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; + } } 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 index 008e25e197f..27c2f67759c 100644 --- 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 @@ -24,6 +24,7 @@ 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.api.ApiDBUtils; import com.cloud.dc.DataCenterVO; import com.cloud.dc.dao.DataCenterDao; import com.cloud.deploy.DeployDestination; @@ -51,13 +52,18 @@ import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.element.DhcpServiceProvider; import com.cloud.network.element.DnsServiceProvider; import com.cloud.network.element.IpDeployer; +import com.cloud.network.element.PortForwardingServiceProvider; import com.cloud.network.element.StaticNatServiceProvider; import com.cloud.network.element.VpcProvider; +import com.cloud.network.rules.FirewallRule; +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.VpcVO; +import com.cloud.network.vpc.dao.VpcDao; import com.cloud.offering.NetworkOffering; import com.cloud.resource.ResourceManager; import com.cloud.resource.ResourceStateAdapter; @@ -65,6 +71,7 @@ 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.exception.CloudRuntimeException; @@ -76,6 +83,7 @@ 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.resource.NsxNetworkRule; import org.apache.log4j.Logger; import org.springframework.stereotype.Component; @@ -83,6 +91,7 @@ import javax.inject.Inject; import javax.naming.ConfigurationException; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Set; @@ -90,7 +99,7 @@ import java.util.function.LongFunction; @Component public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsServiceProvider, VpcProvider, - StaticNatServiceProvider, IpDeployer, ResourceStateAdapter, Listener { + StaticNatServiceProvider, IpDeployer, PortForwardingServiceProvider, ResourceStateAdapter, Listener { @Inject AccountManager accountMgr; @@ -114,6 +123,8 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS IPAddressDao ipAddressDao; @Inject VMInstanceDao vmInstanceDao; + @Inject + VpcDao vpcDao; private static final Logger LOGGER = Logger.getLogger(NsxElement.class); @@ -452,14 +463,99 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, DnsS } Nic nic = networkModel.getNicInNetworkIncludingRemoved(vm.getId(), config.getId()); Network publicNetwork = networkModel.getSystemNetworkByZoneAndTrafficType(config.getDataCenterId(), Networks.TrafficType.Public); + Pair<VpcVO, NetworkVO> 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(), - config.getVpcId(), vm.getId(), ipAddressVO.getAddress().addr(), staticNat.getDestIpAddress()); + networkResourceId, networkResourceName, isVpcResource, vm.getId(), + ipAddressVO.getAddress().addr(), staticNat.getDestIpAddress()); } else { return nsxService.deleteStaticNatRule(config.getDataCenterId(), config.getDomainId(), config.getAccountId(), - config.getVpcId()); + networkResourceId, networkResourceName, isVpcResource); } } return false; } + + @Override + public boolean applyPFRules(Network network, List<PortForwardingRule> rules) throws ResourceUnavailableException { + if (!canHandle(network, Network.Service.PortForwarding)) { + return false; + } + for (PortForwardingRule rule : rules) { + IPAddressVO publicIp = ApiDBUtils.findIpAddressById(rule.getSourceIpAddressId()); + UserVm vm = ApiDBUtils.findUserVmById(rule.getVirtualMachineId()); + if (vm == null || networkModel.getNicInNetwork(vm.getId(), network.getId()) == null) { + continue; + } + Pair<VpcVO, NetworkVO> vpcOrNetwork = getVpcOrNetwork(network.getVpcId(), network.getId()); + VpcVO vpc = vpcOrNetwork.first(); + NetworkVO networkVO = vpcOrNetwork.second(); + Long networkResourceId = Objects.nonNull(vpc) ? vpc.getId() : networkVO.getId(); + String networkResourceName = Objects.nonNull(vpc) ? vpc.getName() : networkVO.getName(); + boolean isVpcResource = Objects.nonNull(vpc); + long domainId = Objects.nonNull(vpc) ? vpc.getDomainId() : networkVO.getDomainId(); + long accountId = Objects.nonNull(vpc) ? vpc.getAccountId() : networkVO.getAccountId(); + long zoneId = Objects.nonNull(vpc) ? vpc.getZoneId() : networkVO.getDataCenterId(); + String publicPort = getPublicPortRange(rule); + + String privatePort = getPrivatePortRange(rule); + + // TODO: add builder to reduce signature params ; should we pass port range? + NsxNetworkRule networkRule = new NsxNetworkRule.Builder() + .setDomainId(domainId) + .setAccountId(accountId) + .setZoneId(zoneId) + .setNetworkResourceId(networkResourceId) + .setNetworkResourceName(networkResourceName) + .setVpcResource(isVpcResource) + .setVmId(vm.getId()) + .setVmIp(vm.getPrivateIpAddress()) + .setPublicIp(publicIp.getAddress().addr()) + .setPrivatePort(privatePort) + .setPublicPort(publicPort) + .setRuleId(rule.getId()) + .setProtocol(rule.getProtocol().toUpperCase(Locale.ROOT)) + .build(); + if (rule.getState() == FirewallRule.State.Add) { + return nsxService.createPortForwardRule(networkRule); + } else { + return nsxService.deletePortForwardRule(networkRule); + } + } + return true; + } + + public Pair<VpcVO, NetworkVO> 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 rule.getDestinationPortStart() == rule.getDestinationPortEnd() ? + String.valueOf(rule.getDestinationPortStart()) : + String.valueOf(rule.getDestinationPortStart()).concat("-").concat(String.valueOf(rule.getDestinationPortEnd())); + } + + private static String getPrivatePortRange(PortForwardingRule rule) { + return Objects.equals(rule.getSourcePortStart(), rule.getSourcePortEnd()) ? + String.valueOf(rule.getSourcePortStart()) : + String.valueOf(rule.getSourcePortStart()).concat("-").concat(String.valueOf(rule.getSourcePortEnd())); + } } 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 index 5cd7001c5e7..466b75b78a3 100644 --- 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 @@ -16,16 +16,20 @@ // under the License. package org.apache.cloudstack.service; +import com.cloud.network.Network; +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.utils.exception.CloudRuntimeException; import org.apache.cloudstack.NsxAnswer; +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.DeleteNsxSegmentCommand; -import org.apache.cloudstack.agent.api.DeleteNsxStaticNatCommand; +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.log4j.Logger; @@ -37,6 +41,8 @@ public class NsxServiceImpl implements NsxService { NsxControllerUtils nsxControllerUtils; @Inject VpcDao vpcDao; + @Inject + NetworkDao networkDao; private static final Logger LOGGER = Logger.getLogger(NsxServiceImpl.class); @@ -83,26 +89,39 @@ public class NsxServiceImpl implements NsxService { return result.getResult(); } - public boolean createStaticNatRule(long zoneId, long domainId, long accountId, long vpcId, - long vmId, String publicIp, String vmIp) { - VpcVO vpc = vpcDao.findById(vpcId); - if (Objects.isNull(vpc)) { - throw new CloudRuntimeException(String.format("Failed to find VPC with id: %s", vpcId)); - } + 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, - vpcId, vpc.getName(), vmId, publicIp, vmIp); + 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 vpcId) { - VpcVO vpc = vpcDao.findById(vpcId); - if (Objects.isNull(vpc)) { - throw new CloudRuntimeException(String.format("Failed to find VPC with id: %s", vpcId)); - } - DeleteNsxStaticNatCommand deleteNsxStaticNatCommand = new DeleteNsxStaticNatCommand(domainId, accountId, zoneId, - vpcId, vpc.getName()); + 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(), null, null); + deleteCmd.setService(Network.Service.PortForwarding); + NsxAnswer result = nsxControllerUtils.sendNsxCommand(deleteCmd, netRule.getZoneId()); + return result.getResult(); + } } diff --git a/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceList.java b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceList.java new file mode 100644 index 00000000000..05ea43fde9b --- /dev/null +++ b/plugins/network-elements/nsx/src/main/java/org/apache/cloudstack/service/NsxServiceList.java @@ -0,0 +1,17 @@ +package org.apache.cloudstack.service; + +public enum NsxServiceList { + AD_Server(1024, "/infra/services/AD_Server", "TCP"), + Active_Directory_Server(464, "/infra/services/Active_Directory_Server", "TCP"), + SSH(22, "/infra/services/SSH", "TCP"); + + int port; + String service; + String path; + String protocol; + NsxServiceList(int port, String path, String protocol) { + this.port = port; + this.path = path; + this.protocol = protocol; + } +} 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 index 1ac04200ce3..8c545476a6e 100644 --- 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 @@ -81,8 +81,13 @@ public class NsxControllerUtils { 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 vpcId) { + public static String getStaticNatRuleName(long domainId, long accountId, long zoneId, Long networkResourceId, boolean isVpcResource) { String suffix = "-STATICNAT"; - return getTier1GatewayName(domainId, accountId, zoneId, vpcId) + suffix; + 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; } } diff --git a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java index 610be14dfef..031f622e30d 100644 --- a/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java +++ b/server/src/main/java/com/cloud/server/ConfigurationServerImpl.java @@ -1248,6 +1248,8 @@ public class ConfigurationServerImpl extends ManagerBase implements Configuratio 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; }
