You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by hu...@apache.org on 2013/01/14 17:15:57 UTC

[46/50] git commit: Summary: Add initial support for OpenVswitch to the KVM hypervisor

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.

Project: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/commit/c9c40d06
Tree: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/tree/c9c40d06
Diff: http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/diff/c9c40d06

Branch: refs/heads/cloud-agent-with-openvswitch
Commit: c9c40d066ec0039aee3dcb91e6494082efadefe9
Parents: 127867c
Author: Hugo Trippaers <tr...@gmail.com>
Authored: Thu Jan 10 18:30:07 2013 +0100
Committer: Hugo Trippaers <tr...@gmail.com>
Committed: Mon Jan 14 08:38:21 2013 +0100

----------------------------------------------------------------------
 .../hypervisor/kvm/resource/BridgeVifDriver.java   |    3 +
 .../kvm/resource/LibvirtComputingResource.java     |   97 ++++++-
 .../hypervisor/kvm/resource/LibvirtVMDef.java      |   25 ++
 .../hypervisor/kvm/resource/OvsVifDriver.java      |  213 +++++++++++++++
 utils/src/com/cloud/utils/script/Script.java       |   20 +-
 5 files changed, 348 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/c9c40d06/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
index e6f2f7f..031721e 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/BridgeVifDriver.java
@@ -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

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/c9c40d06/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
index b52e2d8..fade750 100755
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -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;

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/c9c40d06/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
index ba6c715..17f6eef 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtVMDef.java
@@ -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();
         }

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/c9c40d06/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java
new file mode 100644
index 0000000..6c3e496
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/OvsVifDriver.java
@@ -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;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-cloudstack/blob/c9c40d06/utils/src/com/cloud/utils/script/Script.java
----------------------------------------------------------------------
diff --git a/utils/src/com/cloud/utils/script/Script.java b/utils/src/com/cloud/utils/script/Script.java
index 1444f83..d82d1d0 100755
--- a/utils/src/com/cloud/utils/script/Script.java
+++ b/utils/src/com/cloud/utils/script/Script.java
@@ -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);
             }