blob: 0d556da9f2c01c0144a807499474bc66f5ed666f [file] [log] [blame]
// 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<String> 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");
}
}
}