// 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.network.contrail.management; import java.io.IOException; import java.net.URI; import java.util.List; import javax.inject.Inject; import org.apache.cloudstack.network.contrail.model.InstanceIpModel; import org.apache.cloudstack.network.contrail.model.VMInterfaceModel; import org.apache.cloudstack.network.contrail.model.VirtualMachineModel; import org.apache.cloudstack.network.contrail.model.VirtualNetworkModel; import org.apache.log4j.Logger; 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.exception.ConcurrentOperationException; 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.State; import com.cloud.network.NetworkProfile; import com.cloud.network.Networks.AddressFormat; import com.cloud.network.Networks.BroadcastDomainType; import com.cloud.network.Networks.Mode; import com.cloud.network.Networks.TrafficType; import com.cloud.network.PhysicalNetwork; import com.cloud.network.addr.PublicIp; import com.cloud.network.dao.IPAddressDao; import com.cloud.network.dao.IPAddressVO; import com.cloud.network.dao.NetworkDao; import com.cloud.network.dao.NetworkVO; import com.cloud.network.dao.PhysicalNetworkDao; import com.cloud.network.dao.PhysicalNetworkVO; import com.cloud.network.guru.NetworkGuru; import com.cloud.offering.NetworkOffering; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.utils.component.AdapterBase; import com.cloud.utils.exception.CloudRuntimeException; 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.VMInstanceVO; import com.cloud.vm.VirtualMachineProfile; import com.cloud.vm.dao.NicDao; import net.juniper.contrail.api.types.MacAddressesType; import net.juniper.contrail.api.types.VirtualMachineInterface; public class ContrailGuru extends AdapterBase implements NetworkGuru { @Inject NetworkDao _networkDao; @Inject ContrailManager _manager; @Inject NicDao _nicDao; @Inject IPAddressDao _ipAddressDao; @Inject AccountManager _accountMgr; @Inject IpAddressManager _ipAddrMgr; @Inject PhysicalNetworkDao _physicalNetworkDao; @Inject DataCenterDao _dcDao; private static final Logger s_logger = Logger.getLogger(ContrailGuru.class); private static final TrafficType[] TrafficTypes = {TrafficType.Guest}; private boolean canHandle(NetworkOffering offering, NetworkType networkType, PhysicalNetwork physicalNetwork) { if (physicalNetwork == null) { // Physical network can be false for system network during initial setup of CloudStack return false; } if (_manager.getRouterOffering() == null || _manager.getVpcRouterOffering() == null) { // FIXME The resource is apparently not configured, we need another way to check this. return false; } if (networkType == NetworkType.Advanced && (offering.getId() == _manager.getRouterOffering().getId() || offering.getId() == _manager.getVpcRouterOffering().getId()) && isMyTrafficType(offering.getTrafficType()) && offering.getGuestType() == Network.GuestType.Isolated && physicalNetwork.getIsolationMethods().contains("L3VPN")) return true; return false; } @Override public String getName() { return "ContrailGuru"; } @Override public Network design(NetworkOffering offering, DeploymentPlan plan, Network userSpecified, Account owner) { // Check of the isolation type of the related physical network is L3VPN PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); DataCenter dc = _dcDao.findById(plan.getDataCenterId()); if (!canHandle(offering, dc.getNetworkType(),physnet)) { s_logger.debug("Refusing to design this network"); return null; } NetworkVO network = new NetworkVO(offering.getTrafficType(), Mode.Dhcp, BroadcastDomainType.Lswitch, offering.getId(), State.Allocated, plan.getDataCenterId(), plan.getPhysicalNetworkId(), offering.getRedundantRouter()); if (userSpecified.getCidr() != null) { network.setCidr(userSpecified.getCidr()); network.setGateway(userSpecified.getGateway()); } s_logger.debug("Allocated network " + userSpecified.getName() + (network.getCidr() == null ? "" : " subnet: " + network.getCidr())); return network; } @Override public Network implement(Network network, NetworkOffering offering, DeployDestination destination, ReservationContext context) throws InsufficientVirtualNetworkCapacityException { s_logger.debug("Implement network: " + network.getName() + ", traffic type: " + network.getTrafficType()); VirtualNetworkModel vnModel = _manager.getDatabase().lookupVirtualNetwork(network.getUuid(), _manager.getCanonicalName(network), network.getTrafficType()); if (vnModel == null) { vnModel = new VirtualNetworkModel(network, network.getUuid(), _manager.getCanonicalName(network), network.getTrafficType()); vnModel.setProperties(_manager.getModelController(), network); } try { if (!vnModel.verify(_manager.getModelController())) { vnModel.update(_manager.getModelController()); } } catch (Exception ex) { s_logger.warn("virtual-network update: ", ex); return network; } _manager.getDatabase().getVirtualNetworks().add(vnModel); if (network.getVpcId() != null) { List<IPAddressVO> ips = _ipAddressDao.listByAssociatedVpc(network.getVpcId(), true); if (ips.isEmpty()) { s_logger.debug("Creating a source nat ip for network " + network); Account owner = _accountMgr.getAccount(network.getAccountId()); try { PublicIp publicIp = _ipAddrMgr.assignSourceNatIpAddressToGuestNetwork(owner, network); IPAddressVO ip = publicIp.ip(); ip.setVpcId(network.getVpcId()); _ipAddressDao.acquireInLockTable(ip.getId()); _ipAddressDao.update(ip.getId(), ip); _ipAddressDao.releaseFromLockTable(ip.getId()); } catch (Exception e) { s_logger.error("Unable to allocate source nat ip: " + e); } } } return network; } /** * Allocate the NicProfile object. * At this point the UUID of the nic is not yet known. We defer allocating the VMI and instance-ip objects * until the reserve API is called because of this reason. */ @Override public NicProfile allocate(Network network, NicProfile profile, VirtualMachineProfile vm) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException { s_logger.debug("allocate NicProfile on " + network.getName()); if (profile != null && profile.getRequestedIPv4() != null) { throw new CloudRuntimeException("Does not support custom ip allocation at this time: " + profile); } if (profile == null) { profile = new NicProfile(ReservationStrategy.Create, null, null, null, null); } profile.setReservationStrategy(ReservationStrategy.Start); URI broadcastUri = null; try { broadcastUri = new URI("vlan://untagged"); } catch (Exception e) { s_logger.warn("unable to instantiate broadcast URI: " + e); } profile.setBroadcastUri(broadcastUri); return profile; } /** * Allocate the ip address (and mac) for the specified VM device. */ @Override public void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException, ConcurrentOperationException { s_logger.debug("reserve NicProfile on network id: " + network.getId() + " " + network.getName()); s_logger.debug("deviceId: " + nic.getDeviceId()); NicVO nicVO = _nicDao.findById(nic.getId()); assert nicVO != null; VirtualNetworkModel vnModel = _manager.getDatabase().lookupVirtualNetwork(network.getUuid(), _manager.getCanonicalName(network), network.getTrafficType()); /* Network must have been implemented */ assert vnModel != null; VirtualMachineModel vmModel = _manager.getDatabase().lookupVirtualMachine(vm.getUuid()); if (vmModel == null) { VMInstanceVO vmVo = (VMInstanceVO)vm.getVirtualMachine(); vmModel = new VirtualMachineModel(vmVo, vm.getUuid()); vmModel.setProperties(_manager.getModelController(), vmVo); } VMInterfaceModel vmiModel = vmModel.getVMInterface(nicVO.getUuid()); if (vmiModel == null) { vmiModel = new VMInterfaceModel(nicVO.getUuid()); vmiModel.addToVirtualMachine(vmModel); vmiModel.addToVirtualNetwork(vnModel); } try { vmiModel.build(_manager.getModelController(), (VMInstanceVO)vm.getVirtualMachine(), nicVO); vmiModel.setActive(); } catch (IOException ex) { s_logger.error("virtual-machine-interface set", ex); return; } InstanceIpModel ipModel = vmiModel.getInstanceIp(); if (ipModel == null) { ipModel = new InstanceIpModel(vm.getInstanceName(), nic.getDeviceId()); ipModel.addToVMInterface(vmiModel); } else { s_logger.debug("Reuse existing instance-ip object on " + ipModel.getName()); } if (nic.getIPv4Address() != null) { s_logger.debug("Nic using existing IP address " + nic.getIPv4Address()); ipModel.setAddress(nic.getIPv4Address()); } try { vmModel.update(_manager.getModelController()); } catch (Exception ex) { s_logger.warn("virtual-machine update", ex); return; } _manager.getDatabase().getVirtualMachines().add(vmModel); VirtualMachineInterface vmi = vmiModel.getVMInterface(); // allocate mac address if (nic.getMacAddress() == null) { MacAddressesType macs = vmi.getMacAddresses(); if (macs == null) { s_logger.debug("no mac address is allocated for Nic " + nicVO.getUuid()); } else { s_logger.info("VMI " + _manager.getVifNameByVmUuid(vm.getUuid(), nicVO.getDeviceId()) + " got mac address: " + macs.getMacAddress().get(0)); nic.setMacAddress(macs.getMacAddress().get(0)); } } if (nic.getIPv4Address() == null) { s_logger.debug("Allocated IP address " + ipModel.getAddress()); nic.setIPv4Address(ipModel.getAddress()); if (network.getCidr() != null) { nic.setIPv4Netmask(NetUtils.cidr2Netmask(network.getCidr())); } nic.setIPv4Gateway(network.getGateway()); nic.setFormat(AddressFormat.Ip4); } } /** * When a VM is stopped this API is called to release transient resources. */ @Override public boolean release(NicProfile nic, VirtualMachineProfile vm, String reservationId) { s_logger.debug("release NicProfile " + nic.getId()); return true; } /** * Release permanent resources of a Nic (VMI and addresses). */ @Override public void deallocate(Network network, NicProfile nic, VirtualMachineProfile vm) { s_logger.debug("deallocate NicProfile " + nic.getId() + " on " + network.getName()); NicVO nicVO = _nicDao.findById(nic.getId()); assert nicVO != null; VirtualMachineModel vmModel = _manager.getDatabase().lookupVirtualMachine(vm.getUuid()); if (vmModel == null) { return; } VMInterfaceModel vmiModel = vmModel.getVMInterface(nicVO.getUuid()); if (vmiModel == null) { return; } try { vmiModel.destroy(_manager.getModelController()); } catch (IOException ex) { return; } vmModel.removeSuccessor(vmiModel); if (!vmModel.hasDescendents()) { _manager.getDatabase().getVirtualMachines().remove(vmModel); try { vmModel.delete(_manager.getModelController()); } catch (IOException ex) { s_logger.warn("virtual-machine delete", ex); return; } } } @Override public void updateNicProfile(NicProfile profile, Network network) { // TODO Auto-generated method stub s_logger.debug("update NicProfile " + profile.getId() + " on " + network.getName()); } @Override public void shutdown(NetworkProfile network, NetworkOffering offering) { s_logger.debug("NetworkGuru shutdown"); VirtualNetworkModel vnModel = _manager.getDatabase().lookupVirtualNetwork(network.getUuid(), _manager.getCanonicalName(network), network.getTrafficType()); if (vnModel == null) { return; } try { _manager.getDatabase().getVirtualNetworks().remove(vnModel); vnModel.delete(_manager.getModelController()); } catch (IOException e) { s_logger.warn("virtual-network delete", e); } } @Override public boolean trash(Network network, NetworkOffering offering) { // TODO Auto-generated method stub s_logger.debug("NetworkGuru trash"); return true; } @Override public void updateNetworkProfile(NetworkProfile networkProfile) { // TODO Auto-generated method stub s_logger.debug("NetworkGuru updateNetworkProfile"); } @Override public TrafficType[] getSupportedTrafficType() { return TrafficTypes; } @Override public boolean isMyTrafficType(TrafficType type) { for (TrafficType t : TrafficTypes) { if (t == type) { return true; } } return false; } }