mirror of
https://github.com/apache/cloudstack.git
synced 2025-10-26 08:42:29 +01:00
Summary: Add initial support for OpenVswitch to the KVM hypervisor
Create OvsVifDriver to deal with openvswitch specifics for plugging intefaces Create a parameter to set the bridge type to use in LibvirtComputingResource. Create several functions to get bridge information from openvswitch Add a check to detect the libvirt version and throw an exception when the version is to low ( < 0.9.11 ) Fix classpath loading in Script.findScript to deal with missing path separators at the end. Add notification to the BridgeVifDriver that lswitch broadcast type is not supported.
This commit is contained in:
parent
0412cb8d92
commit
a0ade283b7
@ -85,6 +85,9 @@ public class BridgeVifDriver extends VifDriverBase {
|
||||
URI broadcastUri = nic.getBroadcastUri();
|
||||
vlanId = broadcastUri.getHost();
|
||||
}
|
||||
else if (nic.getBroadcastType() == Networks.BroadcastDomainType.Lswitch) {
|
||||
throw new InternalErrorException("Nicira NVP Logicalswitches are not supported by the BridgeVifDriver");
|
||||
}
|
||||
String trafficLabel = nic.getName();
|
||||
if (nic.getType() == Networks.TrafficType.Guest) {
|
||||
if (nic.getBroadcastType() == Networks.BroadcastDomainType.Vlan
|
||||
|
||||
@ -362,10 +362,16 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
||||
private String _pingTestPath;
|
||||
|
||||
private int _dom0MinMem;
|
||||
|
||||
protected enum BridgeType {
|
||||
NATIVE, OPENVSWITCH
|
||||
}
|
||||
|
||||
protected enum defineOps {
|
||||
UNDEFINE_VM, DEFINE_VM
|
||||
}
|
||||
|
||||
protected BridgeType _bridgeType;
|
||||
|
||||
private String getEndIpFromStartIp(String startIp, int numIps) {
|
||||
String[] tokens = startIp.split("[.]");
|
||||
@ -474,6 +480,14 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
||||
if (storageScriptsDir == null) {
|
||||
storageScriptsDir = getDefaultStorageScriptsDir();
|
||||
}
|
||||
|
||||
String bridgeType = (String) params.get("network.bridge.type");
|
||||
if (bridgeType == null) {
|
||||
_bridgeType = BridgeType.NATIVE;
|
||||
}
|
||||
else {
|
||||
_bridgeType = BridgeType.valueOf(bridgeType.toUpperCase());
|
||||
}
|
||||
|
||||
params.put("domr.scripts.dir", domrScriptsDir);
|
||||
|
||||
@ -650,11 +664,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
||||
|
||||
LibvirtConnection.initialize(_hypervisorURI);
|
||||
Connect conn = null;
|
||||
try {
|
||||
conn = LibvirtConnection.getConnection();
|
||||
} catch (LibvirtException e) {
|
||||
throw new CloudRuntimeException(e.getMessage());
|
||||
}
|
||||
try {
|
||||
conn = LibvirtConnection.getConnection();
|
||||
|
||||
if (_bridgeType == BridgeType.OPENVSWITCH) {
|
||||
if (conn.getLibVirVersion() < (9 * 1000 + 11)) {
|
||||
throw new ConfigurationException(
|
||||
"LibVirt version 0.9.11 required for openvswitch support, but version "
|
||||
+ conn.getLibVirVersion() + " detected");
|
||||
}
|
||||
}
|
||||
} catch (LibvirtException e) {
|
||||
throw new CloudRuntimeException(e.getMessage());
|
||||
}
|
||||
|
||||
/* Does node support HVM guest? If not, exit */
|
||||
if (!IsHVMEnabled(conn)) {
|
||||
@ -697,7 +719,15 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
||||
}
|
||||
}
|
||||
|
||||
getPifs();
|
||||
switch (_bridgeType) {
|
||||
case NATIVE:
|
||||
getPifs();
|
||||
break;
|
||||
case OPENVSWITCH:
|
||||
getOvsPifs();
|
||||
break;
|
||||
}
|
||||
|
||||
if (_pifs.get("private") == null) {
|
||||
s_logger.debug("Failed to get private nic name");
|
||||
throw new ConfigurationException("Failed to get private nic name");
|
||||
@ -796,6 +826,26 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
||||
}
|
||||
s_logger.debug("done looking for pifs, no more bridges");
|
||||
}
|
||||
|
||||
private void getOvsPifs() {
|
||||
String cmdout = Script.runSimpleBashScript("ovs-vsctl list-br | sed '{:q;N;s/\\n/%/g;t q}'");
|
||||
s_logger.debug("cmdout was " + cmdout);
|
||||
List<String> bridges = Arrays.asList(cmdout.split("%"));
|
||||
for (String bridge : bridges) {
|
||||
s_logger.debug("looking for pif for bridge " + bridge);
|
||||
//String pif = getOvsPif(bridge);
|
||||
// Not really interested in the pif name at this point for ovs bridges
|
||||
String pif = bridge;
|
||||
if(_publicBridgeName != null && bridge.equals(_publicBridgeName)){
|
||||
_pifs.put("public", pif);
|
||||
}
|
||||
if (_guestBridgeName != null && bridge.equals(_guestBridgeName)) {
|
||||
_pifs.put("private", pif);
|
||||
}
|
||||
_pifs.put(bridge, pif);
|
||||
}
|
||||
s_logger.debug("done looking for pifs, no more bridges");
|
||||
}
|
||||
|
||||
private String getPif(String bridge) {
|
||||
String pif = Script.runSimpleBashScript("brctl show | grep " + bridge + " | awk '{print $4}'");
|
||||
@ -808,11 +858,29 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
||||
return pif;
|
||||
}
|
||||
|
||||
private String getOvsPif(String bridge) {
|
||||
String pif = Script.runSimpleBashScript("ovs-vsctl list-ports " + bridge);
|
||||
return pif;
|
||||
}
|
||||
|
||||
private boolean checkNetwork(String networkName) {
|
||||
if (networkName == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_bridgeType == BridgeType.OPENVSWITCH) {
|
||||
return checkOvsNetwork(networkName);
|
||||
}
|
||||
else {
|
||||
return checkBridgeNetwork(networkName);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkBridgeNetwork(String networkName) {
|
||||
if (networkName == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String name = Script.runSimpleBashScript("brctl show | grep "
|
||||
+ networkName + " | awk '{print $4}'");
|
||||
if (name == null) {
|
||||
@ -821,6 +889,23 @@ public class LibvirtComputingResource extends ServerResourceBase implements
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkOvsNetwork(String networkName) {
|
||||
s_logger.debug("Checking if network " + networkName + " exists as openvswitch bridge");
|
||||
if (networkName == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Script command = new Script("/bin/sh", _timeout);
|
||||
command.add("-c");
|
||||
command.add("ovs-vsctl br-exists " + networkName);
|
||||
String result = command.execute(null);
|
||||
if ("Ok".equals(result)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private String getVnetId(String vnetId) {
|
||||
return vnetId;
|
||||
|
||||
@ -646,6 +646,8 @@ public class LibvirtVMDef {
|
||||
private String _ipAddr;
|
||||
private String _scriptPath;
|
||||
private nicModel _model;
|
||||
private String _virtualPortType;
|
||||
private String _virtualPortInterfaceId;
|
||||
|
||||
public void defBridgeNet(String brName, String targetBrName,
|
||||
String macAddr, nicModel model) {
|
||||
@ -695,6 +697,22 @@ public class LibvirtVMDef {
|
||||
public String getMacAddress() {
|
||||
return _macAddr;
|
||||
}
|
||||
|
||||
public void setVirtualPortType(String virtualPortType) {
|
||||
_virtualPortType = virtualPortType;
|
||||
}
|
||||
|
||||
public String getVirtualPortType() {
|
||||
return _virtualPortType;
|
||||
}
|
||||
|
||||
public void setVirtualPortInterfaceId(String virtualPortInterfaceId) {
|
||||
_virtualPortInterfaceId = virtualPortInterfaceId;
|
||||
}
|
||||
|
||||
public String getVirtualPortInterfaceId() {
|
||||
return _virtualPortInterfaceId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
@ -714,6 +732,13 @@ public class LibvirtVMDef {
|
||||
if (_model != null) {
|
||||
netBuilder.append("<model type='" + _model + "'/>\n");
|
||||
}
|
||||
if (_virtualPortType != null) {
|
||||
netBuilder.append("<virtualport type='" + _virtualPortType + "'>\n");
|
||||
if (_virtualPortInterfaceId != null) {
|
||||
netBuilder.append("<parameters interfaceid='" + _virtualPortInterfaceId + "'/>\n");
|
||||
}
|
||||
netBuilder.append("</virtualport>\n");
|
||||
}
|
||||
netBuilder.append("</interface>\n");
|
||||
return netBuilder.toString();
|
||||
}
|
||||
|
||||
@ -0,0 +1,213 @@
|
||||
/*
|
||||
* 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.hypervisor.kvm.resource;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.naming.ConfigurationException;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
import org.libvirt.LibvirtException;
|
||||
|
||||
import com.cloud.agent.api.to.NicTO;
|
||||
import com.cloud.exception.InternalErrorException;
|
||||
import com.cloud.hypervisor.kvm.resource.LibvirtVMDef.InterfaceDef;
|
||||
import com.cloud.network.Networks;
|
||||
import com.cloud.utils.NumbersUtil;
|
||||
import com.cloud.utils.net.NetUtils;
|
||||
import com.cloud.utils.script.OutputInterpreter;
|
||||
import com.cloud.utils.script.Script;
|
||||
|
||||
public class OvsVifDriver extends VifDriverBase {
|
||||
private static final Logger s_logger = Logger
|
||||
.getLogger(BridgeVifDriver.class);
|
||||
private int _timeout;
|
||||
private String _modifyVlanPath;
|
||||
|
||||
@Override
|
||||
public void configure(Map<String, Object> params) throws ConfigurationException {
|
||||
super.configure(params);
|
||||
|
||||
String networkScriptsDir = (String) params.get("network.scripts.dir");
|
||||
if (networkScriptsDir == null) {
|
||||
networkScriptsDir = "scripts/vm/network/vnet";
|
||||
}
|
||||
|
||||
String value = (String) params.get("scripts.timeout");
|
||||
_timeout = NumbersUtil.parseInt(value, 30 * 60) * 1000;
|
||||
|
||||
_modifyVlanPath = Script.findScript(networkScriptsDir, "modifyvlan.sh");
|
||||
if (_modifyVlanPath == null) {
|
||||
throw new ConfigurationException("Unable to find modifyvlan.sh");
|
||||
}
|
||||
|
||||
createControlNetwork(_bridges.get("linklocal"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public InterfaceDef plug(NicTO nic, String guestOsType)
|
||||
throws InternalErrorException, LibvirtException {
|
||||
s_logger.debug("plugging nic=" + nic);
|
||||
|
||||
LibvirtVMDef.InterfaceDef intf = new LibvirtVMDef.InterfaceDef();
|
||||
intf.setVirtualPortType("openvswitch");
|
||||
|
||||
String vlanId = null;
|
||||
String logicalSwitchUuid = null;
|
||||
if (nic.getBroadcastType() == Networks.BroadcastDomainType.Vlan) {
|
||||
URI broadcastUri = nic.getBroadcastUri();
|
||||
vlanId = broadcastUri.getHost();
|
||||
}
|
||||
else if (nic.getBroadcastType() == Networks.BroadcastDomainType.Lswitch) {
|
||||
logicalSwitchUuid = nic.getBroadcastUri().getSchemeSpecificPart();
|
||||
}
|
||||
String trafficLabel = nic.getName();
|
||||
if (nic.getType() == Networks.TrafficType.Guest) {
|
||||
if (nic.getBroadcastType() == Networks.BroadcastDomainType.Vlan
|
||||
&& !vlanId.equalsIgnoreCase("untagged")) {
|
||||
if(trafficLabel != null && !trafficLabel.isEmpty()) {
|
||||
s_logger.debug("creating a vlan dev and bridge for guest traffic per traffic label " + trafficLabel);
|
||||
String brName = createVlanBr(vlanId, _pifs.get(trafficLabel));
|
||||
intf.defBridgeNet(brName, null, nic.getMac(), getGuestNicModel(guestOsType));
|
||||
} else {
|
||||
String brName = createVlanBr(vlanId, _pifs.get("private"));
|
||||
intf.defBridgeNet(brName, null, nic.getMac(), getGuestNicModel(guestOsType));
|
||||
}
|
||||
} else if (nic.getBroadcastType() == Networks.BroadcastDomainType.Lswitch) {
|
||||
s_logger.debug("nic " + nic + " needs to be connected to LogicalSwitch " + logicalSwitchUuid);
|
||||
intf.setVirtualPortInterfaceId(nic.getUuid());
|
||||
String brName = (trafficLabel != null && !trafficLabel.isEmpty()) ? _pifs.get(trafficLabel) : _pifs.get("private");
|
||||
intf.defBridgeNet(brName, null, nic.getMac(), getGuestNicModel(guestOsType));
|
||||
}
|
||||
else {
|
||||
intf.defBridgeNet(_bridges.get("guest"), null, nic.getMac(), getGuestNicModel(guestOsType));
|
||||
}
|
||||
} else if (nic.getType() == Networks.TrafficType.Control) {
|
||||
/* Make sure the network is still there */
|
||||
createControlNetwork(_bridges.get("linklocal"));
|
||||
intf.defBridgeNet(_bridges.get("linklocal"), null, nic.getMac(), getGuestNicModel(guestOsType));
|
||||
} else if (nic.getType() == Networks.TrafficType.Public) {
|
||||
if (nic.getBroadcastType() == Networks.BroadcastDomainType.Vlan
|
||||
&& !vlanId.equalsIgnoreCase("untagged")) {
|
||||
if(trafficLabel != null && !trafficLabel.isEmpty()){
|
||||
s_logger.debug("creating a vlan dev and bridge for public traffic per traffic label " + trafficLabel);
|
||||
String brName = createVlanBr(vlanId, _pifs.get(trafficLabel));
|
||||
intf.defBridgeNet(brName, null, nic.getMac(), getGuestNicModel(guestOsType));
|
||||
} else {
|
||||
String brName = createVlanBr(vlanId, _pifs.get("public"));
|
||||
intf.defBridgeNet(brName, null, nic.getMac(), getGuestNicModel(guestOsType));
|
||||
}
|
||||
} else {
|
||||
intf.defBridgeNet(_bridges.get("public"), null, nic.getMac(), getGuestNicModel(guestOsType));
|
||||
}
|
||||
} else if (nic.getType() == Networks.TrafficType.Management) {
|
||||
intf.defBridgeNet(_bridges.get("private"), null, nic.getMac(), getGuestNicModel(guestOsType));
|
||||
} else if (nic.getType() == Networks.TrafficType.Storage) {
|
||||
String storageBrName = nic.getName() == null ? _bridges.get("private")
|
||||
: nic.getName();
|
||||
intf.defBridgeNet(storageBrName, null, nic.getMac(), getGuestNicModel(guestOsType));
|
||||
}
|
||||
return intf;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unplug(InterfaceDef iface) {
|
||||
// Libvirt apparently takes care of this, see BridgeVifDriver unplug
|
||||
}
|
||||
|
||||
private String setVnetBrName(String pifName, String vnetId) {
|
||||
String brName = "br" + pifName + "-"+ vnetId;
|
||||
String oldStyleBrName = "cloudVirBr" + vnetId;
|
||||
|
||||
if (isBridgeExists(oldStyleBrName)) {
|
||||
s_logger.info("Using old style bridge name for vlan " + vnetId + " because existing bridge " + oldStyleBrName + " was found");
|
||||
brName = oldStyleBrName;
|
||||
}
|
||||
|
||||
return brName;
|
||||
}
|
||||
|
||||
private String createVlanBr(String vlanId, String nic)
|
||||
throws InternalErrorException {
|
||||
String brName = setVnetBrName(nic, vlanId);
|
||||
createVnet(vlanId, nic, brName);
|
||||
return brName;
|
||||
}
|
||||
|
||||
private void createVnet(String vnetId, String pif, String brName)
|
||||
throws InternalErrorException {
|
||||
final Script command = new Script(_modifyVlanPath, _timeout, s_logger);
|
||||
command.add("-v", vnetId);
|
||||
command.add("-p", pif);
|
||||
command.add("-b", brName);
|
||||
command.add("-o", "add");
|
||||
|
||||
final String result = command.execute();
|
||||
if (result != null) {
|
||||
throw new InternalErrorException("Failed to create vnet " + vnetId
|
||||
+ ": " + result);
|
||||
}
|
||||
}
|
||||
|
||||
private void deleteExitingLinkLocalRoutTable(String linkLocalBr) {
|
||||
Script command = new Script("/bin/bash", _timeout);
|
||||
command.add("-c");
|
||||
command.add("ip route | grep " + NetUtils.getLinkLocalCIDR());
|
||||
OutputInterpreter.AllLinesParser parser = new OutputInterpreter.AllLinesParser();
|
||||
String result = command.execute(parser);
|
||||
boolean foundLinkLocalBr = false;
|
||||
if (result == null && parser.getLines() != null) {
|
||||
String[] lines = parser.getLines().split("\\n");
|
||||
for (String line : lines) {
|
||||
String[] tokens = line.split(" ");
|
||||
if (!tokens[2].equalsIgnoreCase(linkLocalBr)) {
|
||||
Script.runSimpleBashScript("ip route del " + NetUtils.getLinkLocalCIDR());
|
||||
} else {
|
||||
foundLinkLocalBr = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!foundLinkLocalBr) {
|
||||
Script.runSimpleBashScript("ifconfig " + linkLocalBr + " 169.254.0.1;" + "ip route add " +
|
||||
NetUtils.getLinkLocalCIDR() + " dev " + linkLocalBr + " src " + NetUtils.getLinkLocalGateway());
|
||||
}
|
||||
}
|
||||
|
||||
private void createControlNetwork(String privBrName) {
|
||||
deleteExitingLinkLocalRoutTable(privBrName);
|
||||
if (!isBridgeExists(privBrName)) {
|
||||
Script.runSimpleBashScript("ovs-vsctl add-br " + privBrName + "; ifconfig " + privBrName + " up; ifconfig " +
|
||||
privBrName + " 169.254.0.1", _timeout);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean isBridgeExists(String bridgeName) {
|
||||
Script command = new Script("/bin/sh", _timeout);
|
||||
command.add("-c");
|
||||
command.add("ovs-vsctl br-exists " + bridgeName);
|
||||
String result = command.execute(null);
|
||||
if ("Ok".equals(result)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -195,7 +195,7 @@ public class Script implements Callable<String> {
|
||||
}
|
||||
|
||||
Task task = null;
|
||||
if (interpreter.drain()) {
|
||||
if (interpreter != null && interpreter.drain()) {
|
||||
task = new Task(interpreter, ir);
|
||||
s_executors.execute(task);
|
||||
}
|
||||
@ -204,8 +204,13 @@ public class Script implements Callable<String> {
|
||||
try {
|
||||
if (_process.waitFor() == 0) {
|
||||
_logger.debug("Execution is successful.");
|
||||
|
||||
return interpreter.drain() ? task.getResult() : interpreter.interpret(ir);
|
||||
if (interpreter != null) {
|
||||
return interpreter.drain() ? task.getResult() : interpreter.interpret(ir);
|
||||
}
|
||||
else {
|
||||
// null return is ok apparently
|
||||
return (_process.exitValue() == 0) ? "Ok" : "Failed, exit code " + _process.exitValue();
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@ -239,7 +244,14 @@ public class Script implements Callable<String> {
|
||||
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(_process.getInputStream()), 128);
|
||||
|
||||
String error = interpreter.processError(reader);
|
||||
String error;
|
||||
if (interpreter != null) {
|
||||
error = interpreter.processError(reader);
|
||||
}
|
||||
else {
|
||||
error = "Non zero exit code : " + _process.exitValue();
|
||||
}
|
||||
|
||||
if (_logger.isDebugEnabled()) {
|
||||
_logger.debug(error);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user