| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| package com.cloud.network.guru; |
| |
| |
| import com.cloud.dc.DataCenter; |
| import com.cloud.dc.DataCenter.NetworkType; |
| import com.cloud.dc.dao.DataCenterDao; |
| import com.cloud.deploy.DeployDestination; |
| import com.cloud.deploy.DeploymentPlan; |
| import com.cloud.event.ActionEventUtils; |
| import com.cloud.event.EventTypes; |
| import com.cloud.event.EventVO; |
| import com.cloud.exception.InsufficientAddressCapacityException; |
| import com.cloud.exception.InsufficientVirtualNetworkCapacityException; |
| import com.cloud.network.IpAddressManager; |
| import com.cloud.network.Network; |
| import com.cloud.network.Network.GuestType; |
| import com.cloud.network.Network.State; |
| import com.cloud.network.Networks.BroadcastDomainType; |
| import com.cloud.network.PhysicalNetwork; |
| import com.cloud.network.PhysicalNetwork.IsolationMethod; |
| import com.cloud.network.dao.FirewallRulesCidrsDao; |
| import com.cloud.network.dao.FirewallRulesCidrsVO; |
| import com.cloud.network.dao.FirewallRulesDao; |
| import com.cloud.network.dao.IPAddressDao; |
| import com.cloud.network.dao.IPAddressVO; |
| import com.cloud.network.dao.NetworkDao; |
| import com.cloud.network.dao.NetworkVO; |
| import com.cloud.network.rules.FirewallRule; |
| import com.cloud.network.rules.FirewallRuleVO; |
| import com.cloud.network.rules.PortForwardingRuleVO; |
| import com.cloud.network.rules.dao.PortForwardingRulesDao; |
| import com.cloud.offering.NetworkOffering; |
| import com.cloud.user.Account; |
| import com.cloud.utils.db.DB; |
| import com.cloud.utils.exception.CloudRuntimeException; |
| import com.cloud.utils.net.Ip; |
| import com.cloud.utils.net.NetUtils; |
| import com.cloud.vm.Nic.ReservationStrategy; |
| import com.cloud.vm.NicProfile; |
| import com.cloud.vm.NicVO; |
| import com.cloud.vm.ReservationContext; |
| import com.cloud.vm.VirtualMachineProfile; |
| import org.apache.cloudstack.context.CallContext; |
| import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService; |
| import org.apache.log4j.Logger; |
| |
| import javax.inject.Inject; |
| import java.util.List; |
| |
| public class ExternalGuestNetworkGuru extends GuestNetworkGuru { |
| private static final Logger s_logger = Logger.getLogger(ExternalGuestNetworkGuru.class); |
| @Inject |
| NetworkOrchestrationService _networkMgr; |
| @Inject |
| NetworkDao _networkDao; |
| @Inject |
| DataCenterDao _zoneDao; |
| @Inject |
| PortForwardingRulesDao _pfRulesDao; |
| @Inject |
| IPAddressDao _ipAddressDao; |
| @Inject |
| IpAddressManager _ipAddrMgr; |
| @Inject |
| FirewallRulesDao _fwRulesDao; |
| @Inject |
| FirewallRulesCidrsDao _fwRulesCidrDao; |
| |
| public ExternalGuestNetworkGuru() { |
| super(); |
| _isolationMethods = new IsolationMethod[] {new IsolationMethod("GRE"), new IsolationMethod("L3"), new IsolationMethod("VLAN")}; |
| } |
| |
| @Override |
| protected boolean canHandle(NetworkOffering offering, final NetworkType networkType, final PhysicalNetwork physicalNetwork) { |
| // This guru handles only Guest Isolated network that supports Source |
| // nat service |
| if (networkType == NetworkType.Advanced && isMyTrafficType(offering.getTrafficType()) |
| && (offering.getGuestType() == Network.GuestType.Isolated || offering.getGuestType() == GuestType.L2) |
| && isMyIsolationMethod(physicalNetwork) && !offering.isSystemOnly()) { |
| return true; |
| } else { |
| s_logger.trace("We only take care of Guest networks of type " + GuestType.Isolated + " in zone of type " + NetworkType.Advanced); |
| return false; |
| } |
| } |
| |
| @Override |
| public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { |
| |
| if (_networkModel.areServicesSupportedByNetworkOffering(offering.getId(), Network.Service.Connectivity)) { |
| return null; |
| } |
| |
| NetworkVO config = (NetworkVO)super.design(offering, plan, userSpecified, owner); |
| if (config == null) { |
| return null; |
| } else if (_networkModel.networkIsConfiguredForExternalNetworking(plan.getDataCenterId(), config.getId())) { |
| /* In order to revert userSpecified network setup */ |
| config.setState(State.Allocated); |
| } |
| |
| return config; |
| } |
| |
| @Override |
| public Network implement(Network config, NetworkOffering offering, DeployDestination dest, ReservationContext context) |
| throws InsufficientVirtualNetworkCapacityException { |
| assert (config.getState() == State.Implementing) : "Why are we implementing " + config; |
| |
| if (_networkModel.areServicesSupportedInNetwork(config.getId(), Network.Service.Connectivity)) { |
| return null; |
| } |
| |
| if (!_networkModel.networkIsConfiguredForExternalNetworking(config.getDataCenterId(), config.getId())) { |
| return super.implement(config, offering, dest, context); |
| } |
| |
| DataCenter zone = dest.getDataCenter(); |
| NetworkVO implemented = |
| new NetworkVO(config.getTrafficType(), config.getMode(), config.getBroadcastDomainType(), config.getNetworkOfferingId(), State.Allocated, |
| config.getDataCenterId(), config.getPhysicalNetworkId(), offering.isRedundantRouter()); |
| |
| // Get a vlan tag |
| int vlanTag; |
| if (config.getBroadcastUri() == null) { |
| String vnet = |
| _dcDao.allocateVnet(zone.getId(), config.getPhysicalNetworkId(), config.getAccountId(), context.getReservationId(), |
| UseSystemGuestVlans.valueIn(config.getAccountId())); |
| |
| try { |
| // when supporting more types of networks this need to become |
| // int vlantag = Integer.parseInt(BroadcastDomainType.getValue(vnet)); |
| vlanTag = Integer.parseInt(vnet); |
| } catch (NumberFormatException e) { |
| throw new CloudRuntimeException("Obtained an invalid guest vlan tag. Exception: " + e.getMessage()); |
| } |
| |
| implemented.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vlanTag)); |
| ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), config.getAccountId(), EventVO.LEVEL_INFO, |
| EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone Vlan: " + vnet + " Network Id: " + config.getId(), 0); |
| } else { |
| vlanTag = Integer.parseInt(BroadcastDomainType.getValue(config.getBroadcastUri())); |
| implemented.setBroadcastUri(config.getBroadcastUri()); |
| } |
| |
| // Determine the new gateway and CIDR |
| String[] oldCidr = config.getCidr().split("/"); |
| String oldCidrAddress = oldCidr[0]; |
| int cidrSize = Integer.parseInt(oldCidr[1]); |
| long newCidrAddress = (NetUtils.ip2Long(oldCidrAddress)); |
| // if the implementing network is for vpc, no need to generate newcidr, use the cidr that came from super cidr |
| if (config.getVpcId() != null) { |
| implemented.setGateway(config.getGateway()); |
| implemented.setCidr(config.getCidr()); |
| implemented.setState(State.Implemented); |
| } else { |
| // Determine the offset from the lowest vlan tag |
| int offset = getVlanOffset(config.getPhysicalNetworkId(), vlanTag); |
| cidrSize = getGloballyConfiguredCidrSize(); |
| // If the offset has more bits than there is room for, return null |
| long bitsInOffset = 32 - Integer.numberOfLeadingZeros(offset); |
| if (bitsInOffset > (cidrSize - 8)) { |
| throw new CloudRuntimeException("The offset " + offset + " needs " + bitsInOffset + " bits, but only have " + (cidrSize - 8) + " bits to work with."); |
| } |
| newCidrAddress = (NetUtils.ip2Long(oldCidrAddress) & 0xff000000) | (offset << (32 - cidrSize)); |
| implemented.setGateway(NetUtils.long2Ip(newCidrAddress + 1)); |
| implemented.setCidr(NetUtils.long2Ip(newCidrAddress) + "/" + cidrSize); |
| implemented.setState(State.Implemented); |
| } |
| |
| // Mask the Ipv4 address of all nics that use this network with the new guest VLAN offset |
| List<NicVO> nicsInNetwork = _nicDao.listByNetworkId(config.getId()); |
| for (NicVO nic : nicsInNetwork) { |
| if (nic.getIPv4Address() != null) { |
| long ipMask = getIpMask(nic.getIPv4Address(), cidrSize); |
| nic.setIPv4Address(NetUtils.long2Ip(newCidrAddress | ipMask)); |
| _nicDao.persist(nic); |
| } |
| } |
| |
| // Mask the destination address of all port forwarding rules in this network with the new guest VLAN offset |
| List<PortForwardingRuleVO> pfRulesInNetwork = _pfRulesDao.listByNetwork(config.getId()); |
| for (PortForwardingRuleVO pfRule : pfRulesInNetwork) { |
| if (pfRule.getDestinationIpAddress() != null) { |
| long ipMask = getIpMask(pfRule.getDestinationIpAddress().addr(), cidrSize); |
| String maskedDestinationIpAddress = NetUtils.long2Ip(newCidrAddress | ipMask); |
| pfRule.setDestinationIpAddress(new Ip(maskedDestinationIpAddress)); |
| _pfRulesDao.update(pfRule.getId(), pfRule); |
| } |
| } |
| // Mask the destination address of all static nat rules in this network with the new guest VLAN offset |
| // Here the private ip of the nic get updated. When secondary ip are present the gc will not triggered |
| List<IPAddressVO> ipAddrsOfNw = _ipAddressDao.listStaticNatPublicIps(config.getId()); |
| for (IPAddressVO ip : ipAddrsOfNw) { |
| if (ip.getVmIp() != null) { |
| long ipMask = getIpMask(ip.getVmIp(), cidrSize); |
| String maskedVmIp = NetUtils.long2Ip(newCidrAddress | ipMask); |
| ip.setVmIp(maskedVmIp); |
| _ipAddressDao.update(ip.getId(), ip); |
| } |
| } |
| |
| //Egress rules cidr is subset of guest nework cidr, we need to change |
| List <FirewallRuleVO> fwEgressRules = _fwRulesDao.listByNetworkPurposeTrafficType(config.getId(), FirewallRule.Purpose.Firewall, FirewallRule.TrafficType.Egress); |
| |
| for (FirewallRuleVO rule: fwEgressRules) { |
| //get the cidr list for this rule |
| List<FirewallRulesCidrsVO> fwRuleCidrsVo = _fwRulesCidrDao.listByFirewallRuleId(rule.getId()); |
| |
| for (FirewallRulesCidrsVO ruleCidrvo: fwRuleCidrsVo) { |
| String cidr = ruleCidrvo.getCidr(); |
| String cidrAddr = cidr.split("/")[0]; |
| String size = cidr.split("/")[1]; |
| |
| long ipMask = getIpMask(cidrAddr, cidrSize); |
| String newIp = NetUtils.long2Ip(newCidrAddress | ipMask); |
| String updatedCidr = newIp+"/"+size; |
| |
| ruleCidrvo.setSourceCidrList(updatedCidr); |
| _fwRulesCidrDao.update(ruleCidrvo.getId(), ruleCidrvo); |
| } |
| |
| } |
| |
| |
| return implemented; |
| } |
| |
| @Override |
| public NicProfile allocate(Network config, NicProfile nic, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, |
| InsufficientAddressCapacityException { |
| |
| if (_networkModel.networkIsConfiguredForExternalNetworking(config.getDataCenterId(), config.getId()) && nic != null && nic.getRequestedIPv4() != null) { |
| throw new CloudRuntimeException("Does not support custom ip allocation at this time: " + nic); |
| } |
| |
| NicProfile profile = super.allocate(config, nic, vm); |
| |
| if (_networkModel.networkIsConfiguredForExternalNetworking(config.getDataCenterId(), config.getId())) { |
| profile.setReservationStrategy(ReservationStrategy.Start); |
| /* We won't clear IP address, because router may set gateway as it IP, and it would be updated properly later */ |
| //profile.setIp4Address(null); |
| profile.setIPv4Gateway(null); |
| profile.setIPv4Netmask(null); |
| } |
| |
| return profile; |
| } |
| |
| @Override |
| @DB |
| public void deallocate(Network config, NicProfile nic, VirtualMachineProfile vm) { |
| super.deallocate(config, nic, vm); |
| |
| if (_networkModel.networkIsConfiguredForExternalNetworking(config.getDataCenterId(), config.getId())) { |
| nic.setIPv4Address(null); |
| nic.setIPv4Gateway(null); |
| nic.setIPv4Netmask(null); |
| nic.setBroadcastUri(null); |
| nic.setIsolationUri(null); |
| } |
| } |
| |
| @Override |
| public void reserve(NicProfile nic, Network config, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) |
| throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { |
| assert (nic.getReservationStrategy() == ReservationStrategy.Start) : "What can I do for nics that are not allocated at start? "; |
| |
| DataCenter dc = _dcDao.findById(config.getDataCenterId()); |
| |
| if (_networkModel.networkIsConfiguredForExternalNetworking(config.getDataCenterId(), config.getId())) { |
| nic.setBroadcastUri(config.getBroadcastUri()); |
| nic.setIsolationUri(config.getBroadcastUri()); |
| nic.setIPv4Dns1(dc.getDns1()); |
| nic.setIPv4Dns2(dc.getDns2()); |
| nic.setIPv4Netmask(NetUtils.cidr2Netmask(config.getCidr())); |
| long cidrAddress = NetUtils.ip2Long(config.getCidr().split("/")[0]); |
| int cidrSize = getGloballyConfiguredCidrSize(); |
| nic.setIPv4Gateway(config.getGateway()); |
| |
| if (nic.getIPv4Address() == null) { |
| if (!_networkModel.listNetworkOfferingServices(config.getNetworkOfferingId()).isEmpty()) { |
| String guestIp = _ipAddrMgr.acquireGuestIpAddress(config, null); |
| if (guestIp == null) { |
| throw new InsufficientVirtualNetworkCapacityException("Unable to acquire guest IP address for network " + config, DataCenter.class, dc.getId()); |
| } |
| |
| nic.setIPv4Address(guestIp); |
| } |
| } else { |
| long ipMask = NetUtils.ip2Long(nic.getIPv4Address()) & ~(0xffffffffffffffffl << (32 - cidrSize)); |
| nic.setIPv4Address(NetUtils.long2Ip(cidrAddress | ipMask)); |
| } |
| } else { |
| super.reserve(nic, config, vm, dest, context); |
| } |
| } |
| |
| @Override |
| public boolean release(NicProfile nic, VirtualMachineProfile vm, String reservationId) { |
| |
| NetworkVO network = _networkDao.findById(nic.getNetworkId()); |
| |
| if (network != null && _networkModel.networkIsConfiguredForExternalNetworking(network.getDataCenterId(), network.getId())) { |
| return true; |
| } else { |
| return super.release(nic, vm, reservationId); |
| } |
| } |
| |
| private long getIpMask(String ipAddress, long cidrSize) { |
| return NetUtils.ip2Long(ipAddress) & ~(0xffffffffffffffffl << (32 - cidrSize)); |
| } |
| |
| } |