blob: da409df98359512fa209f02acfd24e268ff48e47 [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.bigswitch;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.net.util.SubnetUtils;
import org.apache.log4j.Logger;
import org.bouncycastle.util.IPAddress;
import com.cloud.agent.AgentManager;
import com.cloud.agent.api.BcfAnswer;
import com.cloud.agent.api.BcfCommand;
import com.cloud.agent.api.CacheBcfTopologyCommand;
import com.cloud.agent.api.GetControllerDataAnswer;
import com.cloud.agent.api.GetControllerDataCommand;
import com.cloud.agent.api.SyncBcfTopologyCommand;
import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.VlanDao;
import com.cloud.host.HostVO;
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.NetworkModel;
import com.cloud.network.Networks.BroadcastDomainType;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.bigswitch.TopologyData.Port;
import com.cloud.network.dao.BigSwitchBcfDao;
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.Purpose;
import com.cloud.network.rules.FirewallRuleVO;
import com.cloud.network.vpc.NetworkACLItem;
import com.cloud.network.vpc.NetworkACLItemCidrsDao;
import com.cloud.network.vpc.NetworkACLItemCidrsVO;
import com.cloud.network.vpc.NetworkACLItemDao;
import com.cloud.network.vpc.NetworkACLItemVO;
import com.cloud.network.vpc.Vpc;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.utils.db.DB;
import com.cloud.utils.db.SearchCriteria;
import com.cloud.utils.db.Transaction;
import com.cloud.utils.db.TransactionCallback;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.vm.NicVO;
import com.cloud.vm.VMInstanceVO;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.VMInstanceDao;
public class BigSwitchBcfUtils {
private static final Logger s_logger = Logger.getLogger(BigSwitchBcfUtils.class);
private final NetworkDao _networkDao;
private final NicDao _nicDao;
private final VMInstanceDao _vmDao;
private final HostDao _hostDao;
private final VpcDao _vpcDao;
private final BigSwitchBcfDao _bigswitchBcfDao;
private final AgentManager _agentMgr;
private final VlanDao _vlanDao;
private final IPAddressDao _ipAddressDao;
private final FirewallRulesDao _fwRulesDao;
private final FirewallRulesCidrsDao _fwCidrsDao;
private final NetworkACLItemDao _aclItemDao;
private final NetworkACLItemCidrsDao _aclItemCidrsDao;
private final NetworkModel _networkModel;
public BigSwitchBcfUtils(NetworkDao networkDao,
NicDao nicDao, VMInstanceDao vmDao, HostDao hostDao,
VpcDao vpcDao, BigSwitchBcfDao bigswitchBcfDao,
AgentManager agentMgr, VlanDao vlanDao, IPAddressDao ipAddressDao,
FirewallRulesDao fwRulesDao, FirewallRulesCidrsDao fwCidrsDao,
NetworkACLItemDao aclItemDao, NetworkACLItemCidrsDao aclItemCidrsDao,
NetworkModel networkModel){
_networkDao = networkDao;
_nicDao = nicDao;
_vmDao = vmDao;
_hostDao = hostDao;
_vpcDao = vpcDao;
_bigswitchBcfDao = bigswitchBcfDao;
_agentMgr = agentMgr;
_vlanDao = vlanDao;
_ipAddressDao = ipAddressDao;
_fwRulesDao = fwRulesDao;
_fwCidrsDao = fwCidrsDao;
_aclItemDao = aclItemDao;
_aclItemCidrsDao = aclItemCidrsDao;
_networkModel = networkModel;
}
public ControlClusterData getControlClusterData(long physicalNetworkId){
ControlClusterData cluster = new ControlClusterData();
// reusable command to query all devices
GetControllerDataCommand cmd = new GetControllerDataCommand();
// retrieve all registered BCF devices
List<BigSwitchBcfDeviceVO> devices = _bigswitchBcfDao.listByPhysicalNetwork(physicalNetworkId);
for (BigSwitchBcfDeviceVO d: devices){
HostVO bigswitchBcfHost = _hostDao.findById(d.getHostId());
if (bigswitchBcfHost == null){
continue;
}
_hostDao.loadDetails(bigswitchBcfHost);
GetControllerDataAnswer answer = (GetControllerDataAnswer) _agentMgr.easySend(bigswitchBcfHost.getId(), cmd);
if (answer != null){
if (answer.isMaster()) {
cluster.setMaster(bigswitchBcfHost);
} else {
cluster.setSlave(bigswitchBcfHost);
}
}
}
return cluster;
}
public TopologyData getTopology(){
long physicalNetworkId;
List<BigSwitchBcfDeviceVO> devices = _bigswitchBcfDao.listAll();
if(!devices.isEmpty()){
physicalNetworkId = devices.get(0).getPhysicalNetworkId();
return getTopology(physicalNetworkId);
} else {
return null;
}
}
public NetworkVO getPublicNetwork(long physicalNetworkId){
List<NetworkVO> pubNets = _networkDao.listByPhysicalNetworkTrafficType(physicalNetworkId, TrafficType.Public);
return pubNets.get(0);
}
public TopologyData getTopology(long physicalNetworkId){
List<NetworkVO> networks;
List<NicVO> nics;
networks = _networkDao.listByPhysicalNetworkTrafficType(physicalNetworkId, TrafficType.Guest);
TopologyData topo = new TopologyData();
// networks:
// - all user created networks (VPC or non-VPC)
// - one external network
// routers:
// - per VPC (handle in network loop)
// - per stand-alone network (handle in network loop)
// - one external router
// handle external network first, only if NAT service is enabled
if(networks != null) {
if(!(networks.isEmpty()) && isNatEnabled()){
// get public net info - needed to set up source nat gateway
NetworkVO pubNet = getPublicNetwork(physicalNetworkId);
// locate subnet info
SearchCriteria<VlanVO> sc = _vlanDao.createSearchCriteria();
sc.setParameters("network_id", pubNet.getId());
VlanVO vlanVO = _vlanDao.findOneBy(sc);
// add tenant external network external
TopologyData.Network network = topo.new Network();
network.setId("external");
network.setName("external");
network.setTenantId("external");
network.setTenantName("external");
String pubVlan = null;
try {
pubVlan = BroadcastDomainType.getValue(vlanVO.getVlanTag());
if(StringUtils.isNumeric(pubVlan)){
network.setVlan(Integer.valueOf(pubVlan));
} else {
// untagged
pubVlan = "0";
}
} catch (URISyntaxException e) {
e.printStackTrace();
}
topo.addNetwork(network);
}
}
// routerMap used internally for multiple updates to same tenant's router
// add back to topo.routers after loop
HashMap<String, RouterData> routerMap = new HashMap<String, RouterData>();
for (NetworkVO netVO: networks){
TopologyData.Network network = topo.new Network();
network.setId(netVO.getUuid());
network.setName(netVO.getName());
Integer vlan = null;
if (netVO.getBroadcastUri() != null) {
String vlanStr = BroadcastDomainType.getValue(netVO.getBroadcastUri());
if(StringUtils.isNumeric(vlanStr)){
vlan = Integer.valueOf(vlanStr);
} else {
// untagged
vlan = 0;
}
}
network.setVlan(vlan);
network.setState(netVO.getState().name());
nics = _nicDao.listByNetworkId(netVO.getId());
List<Port> ports = new ArrayList<Port>();
String tenantId = null;
String tenantName = null;
// if VPC network, assign BCF tenant id with vpc uuid
Vpc vpc = null;
if(netVO.getVpcId()!=null){
vpc = _vpcDao.acquireInLockTable(netVO.getVpcId());
}
if (vpc != null) {
tenantId = vpc.getUuid();
tenantName = vpc.getName();
} else {
tenantId = netVO.getUuid();
tenantName = netVO.getName();
}
for(NicVO nic: nics){
NetworkData netData = new NetworkData();
TopologyData.Port p = topo.new Port();
p.setAttachmentInfo(netData.new AttachmentInfo(nic.getUuid(),nic.getMacAddress()));
VMInstanceVO vm = _vmDao.findById(nic.getInstanceId());
HostVO host = _hostDao.findById(vm.getHostId());
// if host not found, ignore this nic
if (host == null) {
continue;
}
String hostname = host.getName();
long zoneId = netVO.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 (host.getHypervisorType() == HypervisorType.KVM){
pgName = hostname;
} else if (host.getHypervisorType() == HypervisorType.VMware){
pgName = hostname + "-" + vswitchName;
}
p.setHostId(pgName);
p.setSegmentInfo(netData.new SegmentInfo(BroadcastDomainType.Vlan.name(), vlan));
p.setOwner(BigSwitchBcfApi.getCloudstackInstanceId());
List<AttachmentData.Attachment.IpAddress> ipList = new ArrayList<AttachmentData.Attachment.IpAddress>();
ipList.add(new AttachmentData().getAttachment().new IpAddress(nic.getIPv4Address()));
p.setIpAddresses(ipList);
p.setId(nic.getUuid());
p.setMac(nic.getMacAddress());
netData.getNetwork().setId(network.getId());
netData.getNetwork().setName(network.getName());
netData.getNetwork().setTenantId(tenantId);
netData.getNetwork().setTenantName(tenantName);
netData.getNetwork().setState(netVO.getState().name());
p.setNetwork(netData.getNetwork());
ports.add(p);
}
network.setTenantId(tenantId);
network.setTenantName(tenantName);
network.setPorts(ports);
topo.addNetwork(network);
// add router for network
RouterData routerData;
if(tenantId != null){
if(!routerMap.containsKey(tenantId)){
routerData = new RouterData(tenantId);
routerMap.put(tenantId, routerData);
} else {
routerData = routerMap.get(tenantId);
}
routerData.getRouter().getAcls().addAll(listACLbyNetwork(netVO));
if(vpc != null){
routerData.getRouter().addExternalGateway(getPublicIpByVpc(vpc));
} else {
routerData.getRouter().addExternalGateway(getPublicIpByNetwork(netVO));
}
RouterInterfaceData intf = new RouterInterfaceData(tenantId, netVO.getGateway(), netVO.getCidr(),
netVO.getUuid(), netVO.getName());
routerData.getRouter().addInterface(intf);
}
}
for(RouterData rd: routerMap.values()) {
topo.addRouter(rd.getRouter());
}
return topo;
}
public String getPublicIpByNetwork(Network network){
List<IPAddressVO> allocatedIps = _ipAddressDao.listByAssociatedNetwork(network.getId(), true);
for (IPAddressVO ip: allocatedIps){
if(ip.isSourceNat()){
return ip.getAddress().addr();
}
}
return null;
}
public String getPublicIpByVpc(Vpc vpc){
List<IPAddressVO> allocatedIps = _ipAddressDao.listByAssociatedVpc(vpc.getId(), true);
for (IPAddressVO ip: allocatedIps){
if(ip.isSourceNat()){
return ip.getAddress().addr();
}
}
return null;
}
public List<AclData> listACLbyNetwork(Network network){
List<AclData> aclList = new ArrayList<AclData>();
List<FirewallRuleVO> fwRules;
fwRules = _fwRulesDao.listByNetworkAndPurposeAndNotRevoked(network.getId(), Purpose.Firewall);
List<FirewallRulesCidrsVO> fwCidrList = null;
SubnetUtils utils;
for(FirewallRuleVO rule: fwRules){
AclData acl = new AclData();
acl.setId(rule.getUuid());
acl.setPriority((int)rule.getId()); // CloudStack Firewall interface does not have priority
acl.setIpProto(rule.getProtocol());
String cidr = null;
Integer port = rule.getSourcePortStart();
fwCidrList = _fwCidrsDao.listByFirewallRuleId(rule.getId());
if(fwCidrList != null){
if(fwCidrList.size()>1 || !rule.getSourcePortEnd().equals(port)){
continue;
} else {
cidr = fwCidrList.get(0).getCidr();
}
}
if (cidr == null || cidr.equalsIgnoreCase("0.0.0.0/0")) {
cidr = "";
} else {
utils = new SubnetUtils(cidr);
if(!utils.getInfo().getNetworkAddress().equals(utils.getInfo().getAddress())){
continue;
}
}
acl.setSource(acl.new AclNetwork(cidr, port));
acl.setAction("permit");
aclList.add(acl);
}
List<NetworkACLItemVO> aclItems;
List<NetworkACLItemCidrsVO> aclCidrList;
if (network.getNetworkACLId() != null){
aclItems = _aclItemDao.listByACL(network.getNetworkACLId());
for(NetworkACLItem item: aclItems){
AclData acl = new AclData();
acl.setId(item.getUuid());
acl.setPriority(item.getNumber());
acl.setIpProto(item.getProtocol());
String cidr = null; // currently BCF supports single cidr policy
Integer port = item.getSourcePortStart(); // currently BCF supports single port policy
aclCidrList = _aclItemCidrsDao.listByNetworkACLItemId(item.getId());
if(aclCidrList != null){
if(aclCidrList.size()>1 || !item.getSourcePortEnd().equals(port)){
continue;
} else {
cidr = aclCidrList.get(0).getCidr();
}
}
if (cidr == null || cidr.equalsIgnoreCase("0.0.0.0/0")) {
cidr = "";
} else {
utils = new SubnetUtils(cidr);
if(!utils.getInfo().getNetworkAddress().equals(utils.getInfo().getAddress())){
continue;
}
}
acl.setSource(acl.new AclNetwork(cidr, port));
acl.setAction(item.getAction().name());
aclList.add(acl);
}
}
return aclList;
}
public String syncTopologyToBcfHost(HostVO bigswitchBcfHost){
SyncBcfTopologyCommand syncCmd;
if(isNatEnabled()){
syncCmd = new SyncBcfTopologyCommand(true, true);
} else {
syncCmd = new SyncBcfTopologyCommand(true, false);
}
BcfAnswer syncAnswer = (BcfAnswer) _agentMgr.easySend(bigswitchBcfHost.getId(), syncCmd);
if (syncAnswer == null || !syncAnswer.getResult()) {
s_logger.error("SyncBcfTopologyCommand failed");
return null;
}
return syncAnswer.getHash();
}
public String syncTopologyToBcfHost(HostVO bigswitchBcfHost, boolean natEnabled){
SyncBcfTopologyCommand syncCmd;
if(natEnabled){
syncCmd = new SyncBcfTopologyCommand(true, true);
} else {
syncCmd = new SyncBcfTopologyCommand(true, false);
}
BcfAnswer syncAnswer = (BcfAnswer) _agentMgr.easySend(bigswitchBcfHost.getId(), syncCmd);
if (syncAnswer == null || !syncAnswer.getResult()) {
s_logger.error("SyncBcfTopologyCommand failed");
return null;
}
return syncAnswer.getHash();
}
public BcfAnswer sendBcfCommandWithNetworkSyncCheck(BcfCommand cmd, Network network)throws IllegalArgumentException{
// get registered Big Switch controller
ControlClusterData cluster = getControlClusterData(network.getPhysicalNetworkId());
if(cluster.getMaster()==null){
return new BcfAnswer(cmd, new CloudRuntimeException("Big Switch Network controller temporarily unavailable"));
}
TopologyData topo = getTopology(network.getPhysicalNetworkId());
cmd.setTopology(topo);
BcfAnswer answer = (BcfAnswer) _agentMgr.easySend(cluster.getMaster().getId(), cmd);
if (answer == null || !answer.getResult()) {
s_logger.error ("BCF API Command failed");
throw new IllegalArgumentException("Failed API call to Big Switch Network plugin");
}
String newHash = answer.getHash();
if (cmd.isTopologySyncRequested()) {
newHash = syncTopologyToBcfHost(cluster.getMaster());
}
if(newHash != null){
commitTopologyHash(network.getPhysicalNetworkId(), newHash);
}
HostVO slave = cluster.getSlave();
if(slave != null){
TopologyData newTopo = getTopology(network.getPhysicalNetworkId());
CacheBcfTopologyCommand cacheCmd = new CacheBcfTopologyCommand(newTopo);
_agentMgr.easySend(cluster.getSlave().getId(), cacheCmd);
}
return answer;
}
@DB
public Boolean commitTopologyHash(long physicalNetworkId, final String hash) {
final List<BigSwitchBcfDeviceVO> devices = _bigswitchBcfDao.listByPhysicalNetwork(physicalNetworkId);
return Transaction.execute(new TransactionCallback<Boolean>() {
@Override
public Boolean doInTransaction(TransactionStatus status) {
for (BigSwitchBcfDeviceVO d: devices){
d.setHash(hash);
_bigswitchBcfDao.update(d.getId(), d);
}
return true;
}
});
}
public Boolean isNatEnabled(){
List<BigSwitchBcfDeviceVO> devices = _bigswitchBcfDao.listAll();
if(devices != null && !devices.isEmpty()){
return devices.get(0).getNat();
} else {
return false;
}
}
public Integer getSubnetMaskLength(String maskString){
if(!IPAddress.isValidIPv4(maskString)){
return null;
}
String[] octets = maskString.split("\\.");
Integer bits = 0;
for (String o: octets){
switch(o){
case "255":
bits += 8;
continue;
case "254":
bits += 7;
return bits;
case "252":
bits += 6;
return bits;
case "248":
bits += 5;
return bits;
case "240":
bits += 4;
return bits;
case "224":
bits += 3;
return bits;
case "192":
bits += 2;
return bits;
case "128":
bits += 1;
return bits;
case "0":
return bits;
default:
throw new NumberFormatException("non-contiguous subnet mask not supported");
}
}
return bits;
}
}