blob: 7cb50edb4efc90e2a6b69a9d4aab0a265e444f2b [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 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 org.apache.log4j.Logger;
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 {
private static final Logger s_logger = Logger.getLogger(BigSwitchBcfGuestNetworkGuru.class);
@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 {
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) {
// 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")) {
s_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()) {
s_logger.error("No BigSwitch Controller on physical network " + physnet.getName());
return null;
}
for (BigSwitchBcfDeviceVO d: devices){
s_logger.debug("BigSwitch Controller " + d.getUuid()
+ " found on physical network " + physnet.getId());
}
s_logger.debug("Physical isolation type is BCF_SEGMENT, asking GuestNetworkGuru to design this network");
NetworkVO networkObject = (NetworkVO)super.design(offering, plan, userSpecified, 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) {
s_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());
s_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) {
s_logger.debug("BCF guru rollback migration");
}
@Override
public void commitMigration(NicProfile nic, Network network,
VirtualMachineProfile vm, ReservationContext src,
ReservationContext dst) {
s_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);
}
}
}