| // |
| // 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 java.util.List; |
| |
| import javax.inject.Inject; |
| |
| import org.apache.cloudstack.api.ApiCommandResourceType; |
| import org.apache.cloudstack.context.CallContext; |
| import org.apache.commons.lang3.StringUtils; |
| |
| import com.cloud.agent.AgentManager; |
| import com.cloud.agent.api.CreateBcfAttachmentCommand; |
| import com.cloud.agent.api.CreateBcfRouterCommand; |
| import com.cloud.agent.api.CreateBcfRouterInterfaceCommand; |
| import com.cloud.agent.api.CreateBcfSegmentCommand; |
| import com.cloud.agent.api.DeleteBcfSegmentCommand; |
| import com.cloud.dc.DataCenter; |
| import com.cloud.dc.DataCenter.NetworkType; |
| import com.cloud.dc.VlanVO; |
| import com.cloud.dc.dao.HostPodDao; |
| import com.cloud.dc.dao.VlanDao; |
| 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.host.dao.HostDao; |
| import com.cloud.hypervisor.Hypervisor.HypervisorType; |
| import com.cloud.network.BigSwitchBcfDeviceVO; |
| import com.cloud.network.Network; |
| import com.cloud.network.Network.GuestType; |
| import com.cloud.network.Network.State; |
| import com.cloud.network.NetworkMigrationResponder; |
| import com.cloud.network.NetworkProfile; |
| import com.cloud.network.NetworkService; |
| import com.cloud.network.Networks.BroadcastDomainType; |
| import com.cloud.network.PhysicalNetwork; |
| import com.cloud.network.PhysicalNetwork.IsolationMethod; |
| import com.cloud.network.bigswitch.BigSwitchBcfUtils; |
| import com.cloud.network.dao.BigSwitchBcfDao; |
| import com.cloud.network.dao.FirewallRulesCidrsDao; |
| import com.cloud.network.dao.FirewallRulesDao; |
| import com.cloud.network.dao.IPAddressDao; |
| import com.cloud.network.dao.NetworkVO; |
| import com.cloud.network.dao.PhysicalNetworkDao; |
| import com.cloud.network.dao.PhysicalNetworkVO; |
| import com.cloud.network.vpc.NetworkACLItemCidrsDao; |
| import com.cloud.network.vpc.NetworkACLItemDao; |
| import com.cloud.network.vpc.Vpc; |
| import com.cloud.network.vpc.dao.VpcDao; |
| import com.cloud.offering.NetworkOffering; |
| import com.cloud.user.Account; |
| import com.cloud.user.dao.AccountDao; |
| import com.cloud.utils.db.SearchCriteria; |
| import com.cloud.vm.NicProfile; |
| import com.cloud.vm.ReservationContext; |
| import com.cloud.vm.VirtualMachineProfile; |
| import com.cloud.vm.dao.DomainRouterDao; |
| import com.cloud.vm.dao.VMInstanceDao; |
| |
| /** |
| * BigSwitchBcfGuestNetworkGuru is responsible for creating and removing virtual networks. |
| * Current implementation leverages GuestNetworkGuru to create VLAN based virtual networks |
| * and map it to a Big Switch Big Cloud Fabric (BCF) Segment. It is called by the NetworkOrchestrator. |
| * |
| * When a VM is created and needs to be plugged into a BCF segment network, BigSwitchBcfElement is |
| * called by NetworkOrchestrator to create a "port" and an "attachment" for each nic, and |
| * register them with the controller to be plugged into the corresponding network. It also |
| * removes them when the VM is destroyed. |
| */ |
| public class BigSwitchBcfGuestNetworkGuru extends GuestNetworkGuru implements NetworkMigrationResponder { |
| |
| @Inject |
| PhysicalNetworkDao _physicalNetworkDao; |
| @Inject |
| BigSwitchBcfDao _bigswitchBcfDao; |
| @Inject |
| HostDao _hostDao; |
| @Inject |
| AgentManager _agentMgr; |
| @Inject |
| DomainRouterDao _routerDao; |
| @Inject |
| VMInstanceDao _vmDao; |
| @Inject |
| AccountDao _accountDao; |
| @Inject |
| VpcDao _vpcDao; |
| @Inject |
| IPAddressDao _ipAddressDao; |
| @Inject |
| HostPodDao _podDao; |
| @Inject |
| NetworkService _netService; |
| @Inject |
| VlanDao _vlanDao; |
| @Inject |
| FirewallRulesDao _fwRulesDao; |
| @Inject |
| FirewallRulesCidrsDao _fwCidrsDao; |
| @Inject |
| NetworkACLItemDao _aclItemDao; |
| @Inject |
| NetworkACLItemCidrsDao _aclItemCidrsDao; |
| |
| private BigSwitchBcfUtils _bcfUtils = null; |
| |
| public BigSwitchBcfGuestNetworkGuru() { |
| super(); |
| _isolationMethods = new IsolationMethod[] {new IsolationMethod("BCF_SEGMENT")}; |
| } |
| |
| @Override |
| protected boolean canHandle(NetworkOffering offering, NetworkType networkType, PhysicalNetwork physicalNetwork) { |
| if (networkType == NetworkType.Advanced && isMyTrafficType(offering.getTrafficType()) && offering.getGuestType() == Network.GuestType.Isolated && |
| isMyIsolationMethod(physicalNetwork)) { |
| return true; |
| } else { |
| 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, String name, Long vpcId, Account owner) { |
| // Check if the isolation type of the physical network is BCF_SEGMENT, then delegate GuestNetworkGuru to design |
| PhysicalNetworkVO physnet = _physicalNetworkDao.findById(plan.getPhysicalNetworkId()); |
| if (physnet == null || physnet.getIsolationMethods() == null || !physnet.getIsolationMethods().contains("BCF_SEGMENT")) { |
| logger.debug("Refusing to design this network, the physical isolation type is not BCF_SEGMENT"); |
| return null; |
| } |
| |
| List<BigSwitchBcfDeviceVO> devices = _bigswitchBcfDao.listByPhysicalNetwork(physnet.getId()); |
| if (devices.isEmpty()) { |
| logger.error("No BigSwitch Controller on physical network " + physnet.getName()); |
| return null; |
| } |
| for (BigSwitchBcfDeviceVO d: devices){ |
| logger.debug("BigSwitch Controller " + d.getUuid() |
| + " found on physical network " + physnet.getId()); |
| } |
| |
| logger.debug("Physical isolation type is BCF_SEGMENT, asking GuestNetworkGuru to design this network"); |
| NetworkVO networkObject = (NetworkVO)super.design(offering, plan, userSpecified, name, vpcId, owner); |
| if (networkObject == null) { |
| return null; |
| } |
| // Override the broadcast domain type |
| networkObject.setBroadcastDomainType(BroadcastDomainType.Vlan); |
| |
| return networkObject; |
| } |
| |
| @Override |
| public Network implement(Network network, NetworkOffering offering, DeployDestination dest, ReservationContext context) |
| throws InsufficientVirtualNetworkCapacityException { |
| assert (network.getState() == State.Implementing) : "Why are we implementing " + network; |
| |
| bcfUtilsInit(); |
| |
| long dcId = dest.getDataCenter().getId(); |
| |
| long physicalNetworkId = _networkModel.findPhysicalNetworkId(dcId, offering.getTags(), offering.getTrafficType()); |
| |
| NetworkVO implemented = |
| new NetworkVO(network.getTrafficType(), network.getMode(), network.getBroadcastDomainType(), network.getNetworkOfferingId(), State.Allocated, |
| network.getDataCenterId(), physicalNetworkId, false); |
| |
| if (network.getGateway() != null) { |
| implemented.setGateway(network.getGateway()); |
| } |
| |
| if (network.getCidr() != null) { |
| implemented.setCidr(network.getCidr()); |
| } |
| |
| String vnetId = ""; |
| if (network.getBroadcastUri() == null) { |
| vnetId = _dcDao.allocateVnet(dcId, physicalNetworkId, network.getAccountId(), context.getReservationId(), UseSystemGuestVlans.valueIn(network.getAccountId())); |
| if (vnetId == null) { |
| throw new InsufficientVirtualNetworkCapacityException("Unable to allocate vnet as a " + |
| "part of network " + network + " implement ", DataCenter.class, dcId); |
| } |
| implemented.setBroadcastUri(BroadcastDomainType.Vlan.toUri(vnetId)); |
| ActionEventUtils.onCompletedActionEvent(CallContext.current().getCallingUserId(), network.getAccountId(), |
| EventVO.LEVEL_INFO, EventTypes.EVENT_ZONE_VLAN_ASSIGN, "Assigned Zone Vlan: " + vnetId + " Network Id: " + network.getId(), network.getId(), ApiCommandResourceType.Network.toString(), 0); |
| } else { |
| implemented.setBroadcastUri(network.getBroadcastUri()); |
| } |
| |
| // Name is either the given name or the uuid |
| String name = network.getName(); |
| if (name == null || name.isEmpty()) { |
| name = ((NetworkVO)network).getUuid(); |
| } |
| if (name.length() > 64) { |
| name = name.substring(0, 63); // max length 64 |
| } |
| |
| // update fields in network object |
| NetworkVO networkObject = (NetworkVO) network; |
| |
| // determine whether this is VPC network or stand-alone network |
| Vpc vpc = null; |
| if(network.getVpcId()!=null){ |
| vpc = _vpcDao.acquireInLockTable(network.getVpcId()); |
| } |
| |
| // use uuid of networkVO as network id in BSN |
| String networkId = networkObject.getUuid(); |
| |
| String tenantId; |
| String tenantName; |
| if (vpc != null) { |
| tenantId = vpc.getUuid(); |
| tenantName = vpc.getName(); |
| _vpcDao.releaseFromLockTable(vpc.getId()); |
| } else { |
| // use network in CS as tenant in BSN |
| // for non-VPC networks, use network name and id as tenant name and id |
| tenantId = networkId; |
| tenantName = name; |
| } |
| |
| // store tenantId in networkObject for NetworkOrchestrator implementNetwork use |
| networkObject.setNetworkDomain(tenantId); |
| // store tenant Id in implemented object for future actions (e.g., shutdown) |
| implemented.setNetworkDomain(tenantId); |
| String vlanStr = BroadcastDomainType.getValue(implemented.getBroadcastUri()); |
| |
| // get public net info - needed to set up source nat gateway |
| NetworkVO pubNet = _bcfUtils.getPublicNetwork(physicalNetworkId); |
| |
| // locate subnet info |
| SearchCriteria<VlanVO> sc = _vlanDao.createSearchCriteria(); |
| sc.setParameters("network_id", pubNet.getId()); |
| VlanVO pubVlanVO = _vlanDao.findOneBy(sc); |
| String pubVlanStr = pubVlanVO.getVlanTag(); |
| |
| Integer vlan; |
| if(StringUtils.isNumeric(vlanStr)){ |
| vlan = Integer.valueOf(vlanStr); |
| } else { |
| vlan = 0; |
| } |
| CreateBcfSegmentCommand cmd1 = new CreateBcfSegmentCommand(tenantId, tenantName, networkId, name, vlan); |
| |
| _bcfUtils.sendBcfCommandWithNetworkSyncCheck(cmd1, networkObject); |
| |
| if(_bcfUtils.isNatEnabled()){ |
| Integer pvlan; |
| if(StringUtils.isNumeric(pubVlanStr)){ |
| pvlan = Integer.valueOf(pubVlanStr); |
| } else { |
| pvlan = 0; |
| } |
| CreateBcfSegmentCommand cmd2 = new CreateBcfSegmentCommand("external", "external", "external", "external", pvlan); |
| |
| _bcfUtils.sendBcfCommandWithNetworkSyncCheck(cmd2, networkObject); |
| |
| CreateBcfRouterCommand cmd3 = new CreateBcfRouterCommand(tenantId); |
| |
| _bcfUtils.sendBcfCommandWithNetworkSyncCheck(cmd3, network); |
| |
| CreateBcfRouterInterfaceCommand cmd5 = new CreateBcfRouterInterfaceCommand(tenantId, network.getUuid(), network.getCidr(), |
| network.getGateway(), network.getName()); |
| |
| _bcfUtils.sendBcfCommandWithNetworkSyncCheck(cmd5, network); |
| } |
| return implemented; |
| } |
| |
| |
| |
| @Override |
| public void reserve(NicProfile nic, Network network, VirtualMachineProfile vm, DeployDestination dest, ReservationContext context) |
| throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException { |
| // Have GuestNetworkGuru reserve for us |
| super.reserve(nic, network, vm, dest, context); |
| } |
| |
| @Override |
| public boolean release(NicProfile nic, VirtualMachineProfile vm, String reservationId) { |
| return super.release(nic, vm, reservationId); |
| } |
| |
| @Override |
| public void shutdown(NetworkProfile profile, NetworkOffering offering) { |
| NetworkVO networkObject = _networkDao.findById(profile.getId()); |
| if (networkObject.getBroadcastDomainType() != BroadcastDomainType.Vlan || networkObject.getBroadcastUri() == null) { |
| logger.warn("BroadcastUri is empty or incorrect for guestnetwork " + networkObject.getDisplayText()); |
| return; |
| } |
| |
| bcfUtilsInit(); |
| |
| // tenantId stored in network domain field at creation |
| String tenantId = networkObject.getNetworkDomain(); |
| |
| String networkId = networkObject.getUuid(); |
| |
| DeleteBcfSegmentCommand cmd = new DeleteBcfSegmentCommand(tenantId, networkId); |
| |
| _bcfUtils.sendBcfCommandWithNetworkSyncCheck(cmd, networkObject); |
| |
| super.shutdown(profile, offering); |
| } |
| |
| @Override |
| public boolean trash(Network network, NetworkOffering offering) { |
| return super.trash(network, offering); |
| } |
| |
| @Override |
| public boolean prepareMigration(NicProfile nic, Network network, |
| VirtualMachineProfile vm, DeployDestination dest, |
| ReservationContext context) { |
| bcfUtilsInit(); |
| |
| // get arguments for CreateBcfAttachmentCommand |
| // determine whether this is VPC network or stand-alone network |
| Vpc vpc = null; |
| if(network.getVpcId()!=null){ |
| vpc = _vpcDao.acquireInLockTable(network.getVpcId()); |
| } |
| |
| String networkId = network.getUuid(); |
| |
| String tenantId; |
| String tenantName; |
| if (vpc != null) { |
| tenantId = vpc.getUuid(); |
| tenantName = vpc.getName(); |
| boolean released = _vpcDao.releaseFromLockTable(vpc.getId()); |
| logger.debug("BCF guru release lock vpc id: " + vpc.getId() |
| + " released? " + released); |
| } else { |
| // use network id in CS as tenant in BSN |
| // use network uuid as tenant id for non-VPC networks |
| tenantId = networkId; |
| tenantName = network.getName(); |
| } |
| |
| String hostname = dest.getHost().getName(); |
| long zoneId = network.getDataCenterId(); |
| String vmwareVswitchLabel = _networkModel.getDefaultGuestTrafficLabel(zoneId, HypervisorType.VMware); |
| String[] labelArray = null; |
| String vswitchName = null; |
| if(vmwareVswitchLabel!=null){ |
| labelArray=vmwareVswitchLabel.split(","); |
| vswitchName = labelArray[0]; |
| } |
| |
| // hypervisor type: |
| // kvm: ivs port name |
| // vmware: specific portgroup naming convention |
| String pgName = ""; |
| if (dest.getHost().getHypervisorType() == HypervisorType.KVM){ |
| pgName = hostname; |
| } else if (dest.getHost().getHypervisorType() == HypervisorType.VMware){ |
| pgName = hostname + "-" + vswitchName; |
| } |
| |
| String nicId = nic.getUuid(); |
| Integer vlan = Integer.valueOf(BroadcastDomainType.getValue(nic.getIsolationUri())); |
| String ipv4 = nic.getIPv4Address(); |
| String mac = nic.getMacAddress(); |
| |
| CreateBcfAttachmentCommand cmd = new CreateBcfAttachmentCommand(tenantId, |
| tenantName, networkId, pgName, nicId, vlan, ipv4, mac); |
| |
| _bcfUtils.sendBcfCommandWithNetworkSyncCheck(cmd, network); |
| |
| return true; |
| } |
| |
| @Override |
| public void rollbackMigration(NicProfile nic, Network network, |
| VirtualMachineProfile vm, ReservationContext src, |
| ReservationContext dst) { |
| logger.debug("BCF guru rollback migration"); |
| } |
| |
| @Override |
| public void commitMigration(NicProfile nic, Network network, |
| VirtualMachineProfile vm, ReservationContext src, |
| ReservationContext dst) { |
| logger.debug("BCF guru commit migration"); |
| } |
| |
| private void bcfUtilsInit(){ |
| if (_bcfUtils == null) { |
| _bcfUtils = new BigSwitchBcfUtils(_networkDao, _nicDao, |
| _vmDao, _hostDao, _vpcDao, _bigswitchBcfDao, |
| _agentMgr, _vlanDao, _ipAddressDao, _fwRulesDao, |
| _fwCidrsDao, _aclItemDao, _aclItemCidrsDao, _networkModel); |
| } |
| } |
| } |