mirror of
https://github.com/apache/cloudstack.git
synced 2025-11-03 04:12:31 +01:00
[NSX] Add SNAT support (#8100)
* In progress add source NAT * Fix after merge * Fix tests * Fix NPE on isolated network deletion * Reserve source NAT IP when its not passed for NSX VPC * Create source NAT rule on VR NIC allocation * Fix update VPC and remove VPC to update and remove SNAT rule * Fix packaging * Address review comment * Fix build * fix build - unused import * Add defensive checks * Add missing design to NSX public guru --------- Co-authored-by: Pearl Dsilva <pearl1594@gmail.com>
This commit is contained in:
parent
72bdc38381
commit
a4be3eb380
@ -19,6 +19,7 @@ package com.cloud.network;
|
||||
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;
|
||||
@ -85,6 +86,8 @@ public interface NetworkService {
|
||||
|
||||
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;
|
||||
|
||||
@ -39,6 +39,8 @@ import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.dc.VlanDetailsVO;
|
||||
import com.cloud.dc.dao.VlanDetailsDao;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
import org.apache.cloudstack.annotation.AnnotationService;
|
||||
import org.apache.cloudstack.annotation.dao.AnnotationDao;
|
||||
@ -57,6 +59,7 @@ import org.apache.cloudstack.managed.context.ManagedContextRunnable;
|
||||
import org.apache.cloudstack.network.dao.NetworkPermissionDao;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
@ -340,6 +343,8 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
||||
Ipv6Service ipv6Service;
|
||||
@Inject
|
||||
RouterNetworkDao routerNetworkDao;
|
||||
@Inject
|
||||
private VlanDetailsDao vlanDetailsDao;
|
||||
|
||||
List<NetworkGuru> networkGurus;
|
||||
|
||||
@ -1029,6 +1034,12 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
||||
if (profile == null) {
|
||||
return null;
|
||||
}
|
||||
// TODO: Fix with the public network PR
|
||||
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);
|
||||
@ -1062,6 +1073,27 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
|
||||
return new Pair<NicProfile, Integer>(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;
|
||||
}
|
||||
IPAddressVO ip = _ipAddressDao.findByIp(requested.getIPv4Address());
|
||||
if (ip == null) {
|
||||
return false;
|
||||
}
|
||||
VlanDetailsVO vlanDetail = vlanDetailsDao.findDetail(ip.getVlanId(), ApiConstants.NSX_DETAIL_KEY);
|
||||
if (vlanDetail == null) {
|
||||
return false;
|
||||
}
|
||||
boolean isForNsx = vlanDetail.getValue().equalsIgnoreCase("true");
|
||||
return isForNsx && ip.isSourceNat() && !ip.isForSystemVms();
|
||||
}
|
||||
|
||||
private void setMtuDetailsInVRNic(final Pair<NetworkVO, VpcVO> networks, Network network, NicVO vo) {
|
||||
if (TrafficType.Public == network.getTrafficType()) {
|
||||
if (networks == null) {
|
||||
|
||||
@ -19,16 +19,20 @@ 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) {
|
||||
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() {
|
||||
@ -43,6 +47,10 @@ public class CreateNsxTier1GatewayCommand extends NsxCommand {
|
||||
return networkResourceName;
|
||||
}
|
||||
|
||||
public boolean isSourceNatEnabled() {
|
||||
return sourceNatEnabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
// 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 CreateNsxTier1NatRuleCommand extends NsxCommand {
|
||||
|
||||
private String tier1GatewayName;
|
||||
private String action;
|
||||
private String translatedIpAddress;
|
||||
private String natRuleId;
|
||||
|
||||
public CreateNsxTier1NatRuleCommand(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;
|
||||
}
|
||||
}
|
||||
@ -41,6 +41,7 @@ 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.CreateNsxTier1NatRuleCommand;
|
||||
import org.apache.cloudstack.agent.api.DeleteNsxLoadBalancerRuleCommand;
|
||||
import org.apache.cloudstack.agent.api.DeleteNsxSegmentCommand;
|
||||
import org.apache.cloudstack.agent.api.DeleteNsxNatRuleCommand;
|
||||
@ -110,6 +111,8 @@ public class NsxResource implements ServerResource {
|
||||
return executeRequest((CreateNsxTier1GatewayCommand) cmd);
|
||||
} else if (cmd instanceof CreateNsxDhcpRelayConfigCommand) {
|
||||
return executeRequest((CreateNsxDhcpRelayConfigCommand) cmd);
|
||||
} else if (cmd instanceof CreateNsxTier1NatRuleCommand) {
|
||||
return executeRequest((CreateNsxTier1NatRuleCommand) cmd);
|
||||
} else if (cmd instanceof CreateNsxStaticNatCommand) {
|
||||
return executeRequest((CreateNsxStaticNatCommand) cmd);
|
||||
} else if (cmd instanceof DeleteNsxNatRuleCommand) {
|
||||
@ -226,6 +229,22 @@ public class NsxResource implements ServerResource {
|
||||
return true;
|
||||
}
|
||||
|
||||
private Answer executeRequest(CreateNsxTier1NatRuleCommand 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 zoneId = cmd.getZoneId();
|
||||
long domainId = cmd.getDomainId();
|
||||
@ -271,8 +290,9 @@ public class NsxResource implements ServerResource {
|
||||
|
||||
private Answer executeRequest(CreateNsxTier1GatewayCommand cmd) {
|
||||
String name = NsxControllerUtils.getTier1GatewayName(cmd.getDomainId(), cmd.getAccountId(), cmd.getZoneId(), cmd.getNetworkResourceId(), cmd.isResourceVpc());
|
||||
boolean sourceNatEnabled = cmd.isSourceNatEnabled();
|
||||
try {
|
||||
nsxApiClient.createTier1Gateway(name, tier0Gateway, edgeCluster);
|
||||
nsxApiClient.createTier1Gateway(name, tier0Gateway, edgeCluster, sourceNatEnabled);
|
||||
return new NsxAnswer(cmd, true, "");
|
||||
} catch (CloudRuntimeException e) {
|
||||
String msg = String.format("Cannot create tier 1 gateway %s (%s: %s): %s", name,
|
||||
|
||||
@ -16,7 +16,6 @@
|
||||
// under the License.
|
||||
package org.apache.cloudstack.service;
|
||||
|
||||
import com.cloud.exception.InvalidParameterValueException;
|
||||
import com.cloud.network.Network;
|
||||
import com.cloud.utils.exception.CloudRuntimeException;
|
||||
import com.vmware.nsx.model.TransportZone;
|
||||
@ -46,6 +45,7 @@ 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.PolicyNatRule;
|
||||
import com.vmware.nsx_policy.model.PolicyNatRuleListResult;
|
||||
import com.vmware.nsx_policy.model.Segment;
|
||||
import com.vmware.nsx_policy.model.SegmentSubnet;
|
||||
import com.vmware.nsx_policy.model.ServiceListResult;
|
||||
@ -68,6 +68,7 @@ import org.apache.cloudstack.utils.NsxControllerUtils;
|
||||
import org.apache.commons.collections.CollectionUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
@ -162,6 +163,16 @@ public class NsxApiClient {
|
||||
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<String> addresses) {
|
||||
try {
|
||||
DhcpRelayConfigs service = (DhcpRelayConfigs) nsxService.apply(DhcpRelayConfigs.class);
|
||||
@ -238,13 +249,26 @@ public class NsxApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
public void createTier1Gateway(String name, String tier0Gateway, String edgeCluster) {
|
||||
private List<String> getRouterAdvertisementTypeList(boolean sourceNatEnabled) {
|
||||
List<String> types = new ArrayList<>();
|
||||
types.add(RouteAdvertisementType.TIER1_IPSEC_LOCAL_ENDPOINT.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) {
|
||||
String tier0GatewayPath = TIER_0_GATEWAY_PATH_PREFIX + tier0Gateway;
|
||||
Tier1 tier1 = getTier1Gateway(name);
|
||||
if (tier1 != null) {
|
||||
throw new InvalidParameterValueException(String.format("VPC network with name %s exists in NSX zone", name));
|
||||
LOGGER.info(String.format("VPC network with name %s exists in NSX zone", name));
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> routeAdvertisementTypes = getRouterAdvertisementTypeList(sourceNatEnabled);
|
||||
|
||||
Tier1s tier1service = (Tier1s) nsxService.apply(Tier1s.class);
|
||||
tier1 = new Tier1.Builder()
|
||||
.setTier0Path(tier0GatewayPath)
|
||||
@ -252,7 +276,7 @@ public class NsxApiClient {
|
||||
.setPoolAllocation(PoolAllocation.ROUTING.name())
|
||||
.setHaMode(HAMode.ACTIVE_STANDBY.name())
|
||||
.setFailoverMode(FailoverMode.PREEMPTIVE.name())
|
||||
.setRouteAdvertisementTypes(List.of(RouteAdvertisementType.TIER1_CONNECTED.name(), RouteAdvertisementType.TIER1_IPSEC_LOCAL_ENDPOINT.name()))
|
||||
.setRouteAdvertisementTypes(routeAdvertisementTypes)
|
||||
.setId(name)
|
||||
.setDisplayName(name)
|
||||
.build();
|
||||
@ -270,11 +294,28 @@ public class NsxApiClient {
|
||||
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);
|
||||
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);
|
||||
String natId = "USER";
|
||||
PolicyNatRuleListResult result = natRulesService.list(tier1Id, natId, null, false, null, null, null, null);
|
||||
List<PolicyNatRule> 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, natId, natRule.getId());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public SiteListResult getSites() {
|
||||
try {
|
||||
Sites sites = (Sites) nsxService.apply(Sites.class);
|
||||
@ -327,7 +368,7 @@ public class NsxApiClient {
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteSegment(long zoneId, long domainId, long accountId, long vpcId, long networkId, String segmentName) {
|
||||
public void deleteSegment(long zoneId, long domainId, long accountId, Long vpcId, long networkId, String segmentName) {
|
||||
try {
|
||||
Segments segmentService = (Segments) nsxService.apply(Segments.class);
|
||||
LOGGER.debug(String.format("Removing the segment with ID %s", segmentName));
|
||||
|
||||
@ -67,6 +67,7 @@ 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;
|
||||
@ -129,6 +130,7 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, Dns
|
||||
@Inject
|
||||
DomainDao domainDao;
|
||||
@Inject
|
||||
protected VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
|
||||
IPAddressDao ipAddressDao;
|
||||
@Inject
|
||||
VMInstanceDao vmInstanceDao;
|
||||
@ -331,9 +333,7 @@ public class NsxElement extends AdapterBase implements DhcpServiceProvider, Dns
|
||||
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.createVpcNetwork(vpc.getZoneId(), account.getId(), domain.getId(), vpc.getId(), vpc.getName());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -0,0 +1,143 @@
|
||||
// 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.dao.IPAddressVO;
|
||||
import com.cloud.network.dao.NetworkVO;
|
||||
import com.cloud.network.guru.PublicNetworkGuru;
|
||||
import com.cloud.network.vpc.VpcVO;
|
||||
import com.cloud.network.vpc.dao.VpcDao;
|
||||
import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
|
||||
import com.cloud.offering.NetworkOffering;
|
||||
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.CreateNsxTier1NatRuleCommand;
|
||||
import org.apache.cloudstack.api.ApiConstants;
|
||||
import org.apache.cloudstack.utils.NsxControllerUtils;
|
||||
import org.apache.cloudstack.utils.NsxHelper;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
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;
|
||||
|
||||
private static final Logger s_logger = Logger.getLogger(NsxPublicNetworkGuru.class);
|
||||
|
||||
public NsxPublicNetworkGuru() {
|
||||
super();
|
||||
}
|
||||
|
||||
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) {
|
||||
NetworkVO ntwk =
|
||||
new NetworkVO(offering.getTrafficType(), Networks.Mode.Static, network.getBroadcastDomainType(), offering.getId(), Network.State.Setup, plan.getDataCenterId(),
|
||||
plan.getPhysicalNetworkId(), offering.isRedundantRouter());
|
||||
return ntwk;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public NicProfile allocate(Network network, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException {
|
||||
s_logger.debug("NSX Public network guru: allocate");
|
||||
// NicProfile createdProfile = super.allocate(network, nic, vm);
|
||||
|
||||
IPAddressVO ipAddress = _ipAddressDao.findByIp(nic.getIPv4Address());
|
||||
if (ipAddress == null) {
|
||||
String err = String.format("Cannot find the IP address %s", nic.getIPv4Address());
|
||||
s_logger.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
Long vpcId = ipAddress.getVpcId();
|
||||
if (vpcId == null) {
|
||||
// TODO: Pass network.getId() to support isolated networks
|
||||
throw new CloudRuntimeException("TODO: Add support for isolated networks public network");
|
||||
}
|
||||
VpcVO vpc = vpcDao.findById(vpcId);
|
||||
if (vpc == null) {
|
||||
String err = String.format("Cannot find a VPC with ID %s", vpcId);
|
||||
s_logger.error(err);
|
||||
throw new CloudRuntimeException(err);
|
||||
}
|
||||
|
||||
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();
|
||||
boolean isForVpc = true;
|
||||
long resourceId = isForVpc ? vpc.getId() : network.getId();
|
||||
Network.Service[] services = { Network.Service.SourceNat };
|
||||
boolean sourceNatEnabled = vpcOfferingServiceMapDao.areServicesSupportedByVpcOffering(vpc.getVpcOfferingId(), services);
|
||||
|
||||
s_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());
|
||||
s_logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
|
||||
String tier1GatewayName = NsxControllerUtils.getTier1GatewayName(domainId, accountId, dataCenterId, resourceId, isForVpc);
|
||||
String translatedIp = ipAddress.getAddress().addr();
|
||||
s_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);
|
||||
CreateNsxTier1NatRuleCommand cmd = NsxHelper.createNsxNatRuleCommand(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);
|
||||
s_logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nic;
|
||||
}
|
||||
}
|
||||
@ -17,4 +17,6 @@
|
||||
package org.apache.cloudstack.service;
|
||||
|
||||
public interface NsxService {
|
||||
|
||||
boolean createVpcNetwork(Long zoneId, long accountId, long domainId, Long vpcId, String vpcName, boolean sourceNatEnabled);
|
||||
}
|
||||
|
||||
@ -48,16 +48,16 @@ public class NsxServiceImpl implements NsxService {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(NsxServiceImpl.class);
|
||||
|
||||
public boolean createVpcNetwork(Long zoneId, long accountId, long domainId, Long vpcId, String vpcName) {
|
||||
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);
|
||||
new CreateNsxTier1GatewayCommand(domainId, accountId, zoneId, vpcId, vpcName, true, sourceNatEnabled);
|
||||
NsxAnswer result = nsxControllerUtils.sendNsxCommand(createNsxTier1GatewayCommand, zoneId);
|
||||
return result.getResult();
|
||||
}
|
||||
|
||||
public boolean createNetwork(Long zoneId, long accountId, long domainId, Long networkId, String networkName) {
|
||||
CreateNsxTier1GatewayCommand createNsxTier1GatewayCommand =
|
||||
new CreateNsxTier1GatewayCommand(domainId, accountId, zoneId, networkId, networkName, false);
|
||||
new CreateNsxTier1GatewayCommand(domainId, accountId, zoneId, networkId, networkName, false, false);
|
||||
NsxAnswer result = nsxControllerUtils.sendNsxCommand(createNsxTier1GatewayCommand, zoneId);
|
||||
return result.getResult();
|
||||
}
|
||||
|
||||
@ -40,6 +40,11 @@ public class NsxControllerUtils {
|
||||
@Inject
|
||||
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 NsxAnswer sendNsxCommand(NsxCommand cmd, long zoneId) throws IllegalArgumentException {
|
||||
|
||||
NsxProviderVO nsxProviderVO = nsxProviderDao.findByZoneId(zoneId);
|
||||
|
||||
@ -25,6 +25,7 @@ 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.CreateNsxTier1GatewayCommand;
|
||||
import org.apache.cloudstack.agent.api.CreateNsxTier1NatRuleCommand;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -44,6 +45,12 @@ public class NsxHelper {
|
||||
|
||||
public static CreateNsxTier1GatewayCommand createNsxTier1GatewayCommand(DomainVO domain, Account account, DataCenter zone,
|
||||
Long networkResourceId, String networkResourceName, boolean isResourceVpc) {
|
||||
return new CreateNsxTier1GatewayCommand(domain.getId(), account.getId(), zone.getId(), networkResourceId, networkResourceName, isResourceVpc);
|
||||
return new CreateNsxTier1GatewayCommand(domain.getId(), account.getId(), zone.getId(), networkResourceId, networkResourceName, isResourceVpc, false);
|
||||
}
|
||||
|
||||
public static CreateNsxTier1NatRuleCommand createNsxNatRuleCommand(long domainId, long accountId, long zoneId,
|
||||
String tier1Gateway, String action, String ipAddress,
|
||||
String natRuleId) {
|
||||
return new CreateNsxTier1NatRuleCommand(domainId, accountId, zoneId, tier1Gateway, action, ipAddress, natRuleId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,6 +31,9 @@
|
||||
<bean id="nsxGuestNetworkGuru" class="org.apache.cloudstack.service.NsxGuestNetworkGuru">
|
||||
<property name="name" value="NsxGuestNetworkGuru" />
|
||||
</bean>
|
||||
<bean id="NsxPublicNetworkGuru" class="org.apache.cloudstack.service.NsxPublicNetworkGuru">
|
||||
<property name="name" value="NsxPublicNetworkGuru" />
|
||||
</bean>
|
||||
<bean id="nsxProviderService" class="org.apache.cloudstack.service.NsxProviderServiceImpl"/>
|
||||
<bean id="nsxService" class="org.apache.cloudstack.service.NsxServiceImpl"/>
|
||||
<bean id="nsxControllerUtils" class="org.apache.cloudstack.utils.NsxControllerUtils" />
|
||||
|
||||
@ -106,7 +106,7 @@ public class NsxResourceTest {
|
||||
@Test
|
||||
public void testCreateNsxTier1Gateway() {
|
||||
NsxCommand command = new CreateNsxTier1GatewayCommand(1L, 2L,
|
||||
1L, 3L, "VPC01", true);
|
||||
1L, 3L, "VPC01", true, false);
|
||||
|
||||
NsxAnswer answer = (NsxAnswer) nsxResource.executeRequest(command);
|
||||
assertTrue(answer.getResult());
|
||||
|
||||
@ -29,6 +29,7 @@ import com.cloud.network.dao.NetworkDao;
|
||||
import com.cloud.network.dao.PhysicalNetworkDao;
|
||||
import com.cloud.network.dao.PhysicalNetworkVO;
|
||||
import com.cloud.network.vpc.Vpc;
|
||||
import com.cloud.network.vpc.dao.VpcOfferingServiceMapDao;
|
||||
import com.cloud.resource.ResourceManager;
|
||||
import com.cloud.user.Account;
|
||||
import com.cloud.user.AccountManager;
|
||||
@ -74,6 +75,8 @@ public class NsxElementTest {
|
||||
Account account;
|
||||
@Mock
|
||||
DomainVO domain;
|
||||
@Mock
|
||||
private VpcOfferingServiceMapDao vpcOfferingServiceMapDao;
|
||||
|
||||
NsxElement nsxElement;
|
||||
ReservationContext reservationContext;
|
||||
@ -93,6 +96,7 @@ public class NsxElementTest {
|
||||
nsxElement.physicalNetworkDao = physicalNetworkDao;
|
||||
nsxElement.domainDao = domainDao;
|
||||
nsxElement.networkModel = networkModel;
|
||||
nsxElement.vpcOfferingServiceMapDao = vpcOfferingServiceMapDao;
|
||||
reservationContext = mock(ReservationContext.class);
|
||||
deployDestination = mock(DeployDestination.class);
|
||||
|
||||
@ -115,8 +119,6 @@ public class NsxElementTest {
|
||||
|
||||
@Test
|
||||
public void testImplementVpc() throws ResourceUnavailableException, InsufficientCapacityException {
|
||||
when(nsxService.createVpcNetwork(anyLong(), anyLong(), anyLong(), anyLong(), anyString())).thenReturn(true);
|
||||
|
||||
assertTrue(nsxElement.implementVpc(vpc, deployDestination, reservationContext));
|
||||
}
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ public class NsxServiceImplTest {
|
||||
when(nsxControllerUtils.sendNsxCommand(any(CreateNsxTier1GatewayCommand.class), anyLong())).thenReturn(createNsxTier1GatewayAnswer);
|
||||
when(createNsxTier1GatewayAnswer.getResult()).thenReturn(true);
|
||||
|
||||
assertTrue(nsxService.createVpcNetwork(1L, 3L, 2L, 5L, "VPC01"));
|
||||
assertTrue(nsxService.createVpcNetwork(1L, 3L, 2L, 5L, "VPC01", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@ -41,6 +41,8 @@ import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import com.cloud.dc.VlanDetailsVO;
|
||||
import com.cloud.dc.dao.VlanDetailsDao;
|
||||
import com.cloud.offering.ServiceOffering;
|
||||
import com.cloud.service.dao.ServiceOfferingDao;
|
||||
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
|
||||
@ -281,6 +283,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;
|
||||
@ -1134,6 +1138,58 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService, C
|
||||
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());
|
||||
s_logger.error(msg);
|
||||
throw new CloudRuntimeException(msg);
|
||||
}
|
||||
|
||||
List<IPAddressVO> 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());
|
||||
s_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<VlanVO> zoneVlans = _vlanDao.listByZone(zone.getId());
|
||||
for (VlanVO zoneVlan : zoneVlans) {
|
||||
VlanDetailsVO detail = vlanDetailsDao.findDetail(zoneVlan.getId(), vlanDetailKey);
|
||||
if (detail != null && detail.getValue().equalsIgnoreCase("true")) {
|
||||
s_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<AccountVlanMapVO> accountVlanMaps = _accountVlanMapDao.listAccountVlanMapsByVlan(vlanId);
|
||||
if (CollectionUtils.isNotEmpty(accountVlanMaps)) {
|
||||
|
||||
@ -70,7 +70,7 @@ public class PublicNetworkGuru extends AdapterBase implements NetworkGuru {
|
||||
@Inject
|
||||
NetworkOrchestrationService _networkMgr;
|
||||
@Inject
|
||||
IPAddressDao _ipAddressDao;
|
||||
protected IPAddressDao _ipAddressDao;
|
||||
@Inject
|
||||
IpAddressManager _ipAddrMgr;
|
||||
@Inject
|
||||
|
||||
@ -1171,11 +1171,26 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
||||
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) {
|
||||
s_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)) {
|
||||
s_logger.debug(String.format("Reserving a source NAT IP for NSX VPC %s", vpc.getName()));
|
||||
sourceNatIP = reserveSourceNatIpForNsxVpc(account, zone, vpc);
|
||||
}
|
||||
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){
|
||||
@ -1183,6 +1198,11 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
|
||||
}
|
||||
}
|
||||
|
||||
private String reserveSourceNatIpForNsxVpc(Account account, DataCenter zone, Vpc vpc) 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();
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
// under the License.
|
||||
package com.cloud.vpc;
|
||||
|
||||
import com.cloud.dc.DataCenter;
|
||||
import com.cloud.deploy.DataCenterDeployment;
|
||||
import com.cloud.deploy.DeployDestination;
|
||||
import com.cloud.deploy.DeploymentPlan;
|
||||
@ -176,6 +177,11 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
|
||||
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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user