You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by wi...@apache.org on 2013/04/03 12:33:30 UTC

[22/50] [abbrv] CLOUDSTACK-922: LXC Support in Cloudstack.

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/server/src/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java b/server/src/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java
new file mode 100644
index 0000000..75b007c
--- /dev/null
+++ b/server/src/com/cloud/hypervisor/kvm/discoverer/LibvirtServerDiscoverer.java
@@ -0,0 +1,393 @@
+// 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.discoverer;
+
+import java.net.InetAddress;
+import java.net.URI;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+import javax.ejb.Local;
+import javax.inject.Inject;
+import javax.naming.ConfigurationException;
+
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.Listener;
+import com.cloud.agent.api.AgentControlAnswer;
+import com.cloud.agent.api.AgentControlCommand;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.Command;
+import com.cloud.agent.api.ShutdownCommand;
+import com.cloud.agent.api.StartupCommand;
+import com.cloud.agent.api.StartupRoutingCommand;
+import com.cloud.configuration.Config;
+import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.dc.ClusterVO;
+import com.cloud.dc.dao.ClusterDao;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.exception.DiscoveredWithErrorException;
+import com.cloud.exception.DiscoveryException;
+import com.cloud.exception.OperationTimedoutException;
+import com.cloud.host.Host;
+import com.cloud.host.HostVO;
+import com.cloud.host.Status;
+import com.cloud.host.dao.HostDao;
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.hypervisor.Hypervisor.HypervisorType;
+import com.cloud.network.NetworkModel;
+import com.cloud.network.PhysicalNetworkSetupInfo;
+import com.cloud.resource.Discoverer;
+import com.cloud.resource.DiscovererBase;
+import com.cloud.resource.ResourceManager;
+import com.cloud.resource.ResourceStateAdapter;
+import com.cloud.resource.ServerResource;
+import com.cloud.resource.UnableDeleteHostException;
+import com.cloud.utils.ssh.SSHCmdHelper;
+
+public abstract class LibvirtServerDiscoverer extends DiscovererBase implements Discoverer,
+Listener, ResourceStateAdapter {
+    private static final Logger s_logger = Logger.getLogger(LibvirtServerDiscoverer.class);
+    private String _hostIp;
+    private final int _waitTime = 5; /*wait for 5 minutes*/
+    private String _kvmPrivateNic;
+    private String _kvmPublicNic;
+    private String _kvmGuestNic;
+    @Inject HostDao _hostDao = null;
+    @Inject ClusterDao _clusterDao;
+    @Inject ResourceManager _resourceMgr;
+    @Inject AgentManager _agentMgr;
+    @Inject ConfigurationDao _configDao;
+    @Inject NetworkModel _networkMgr;
+
+    public abstract Hypervisor.HypervisorType getHypervisorType();
+
+    @Override
+    public boolean processAnswers(long agentId, long seq, Answer[] answers) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public boolean processCommands(long agentId, long seq, Command[] commands) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public AgentControlAnswer processControlCommand(long agentId,
+            AgentControlCommand cmd) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public void processConnect(HostVO host, StartupCommand cmd, boolean forRebalance) {
+    }
+
+    @Override
+    public boolean processDisconnect(long agentId, Status state) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public boolean isRecurring() {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public int getTimeout() {
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    @Override
+    public boolean processTimeout(long agentId, long seq) {
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    @Override
+    public Map<? extends ServerResource, Map<String, String>> find(long dcId,
+            Long podId, Long clusterId, URI uri, String username,
+            String password, List<String> hostTags) throws DiscoveryException {
+
+        ClusterVO cluster = _clusterDao.findById(clusterId);
+        if(cluster == null || cluster.getHypervisorType() != getHypervisorType()) {
+            if(s_logger.isInfoEnabled())
+                s_logger.info("invalid cluster id or cluster is not for " + getHypervisorType() + " hypervisors"); 
+            return null;
+        }
+
+        Map<KvmDummyResourceBase, Map<String, String>> resources = new HashMap<KvmDummyResourceBase, Map<String, String>>();
+        Map<String, String> details = new HashMap<String, String>();
+        if (!uri.getScheme().equals("http")) {
+            String msg = "urlString is not http so we're not taking care of the discovery for this: " + uri;
+            s_logger.debug(msg);
+            return null;
+        }
+        com.trilead.ssh2.Connection sshConnection = null;
+        String agentIp = null;
+        try {
+
+            String hostname = uri.getHost();
+            InetAddress ia = InetAddress.getByName(hostname);
+            agentIp = ia.getHostAddress();
+            String guid = UUID.nameUUIDFromBytes(agentIp.getBytes()).toString();
+            String guidWithTail = guid + "-LibvirtComputingResource";/*tail added by agent.java*/
+            if (_resourceMgr.findHostByGuid(guidWithTail) != null) {
+                s_logger.debug("Skipping " + agentIp + " because " + guidWithTail + " is already in the database.");
+                return null;
+            }       
+
+            sshConnection = new com.trilead.ssh2.Connection(agentIp, 22);
+
+            sshConnection.connect(null, 60000, 60000);
+            if (!sshConnection.authenticateWithPassword(username, password)) {
+                s_logger.debug("Failed to authenticate");
+                throw new DiscoveredWithErrorException("Authentication error");
+            }
+
+            if (!SSHCmdHelper.sshExecuteCmd(sshConnection, "lsmod|grep kvm", 3)) {
+                s_logger.debug("It's not a KVM enabled machine");
+                return null;
+            }
+
+            List <PhysicalNetworkSetupInfo> netInfos = _networkMgr.getPhysicalNetworkInfo(dcId, getHypervisorType());
+            String kvmPrivateNic = null;
+            String kvmPublicNic = null;
+            String kvmGuestNic = null;
+
+            for (PhysicalNetworkSetupInfo info : netInfos) {
+                if (info.getPrivateNetworkName() != null) {
+                    kvmPrivateNic = info.getPrivateNetworkName();
+                }
+                if (info.getPublicNetworkName() != null) {
+                    kvmPublicNic = info.getPublicNetworkName();
+                }
+                if (info.getGuestNetworkName() != null) {
+                    kvmGuestNic = info.getGuestNetworkName();
+                }
+            }
+
+            if (kvmPrivateNic == null && kvmPublicNic == null && kvmGuestNic == null) {
+                kvmPrivateNic = _kvmPrivateNic;
+                kvmPublicNic = _kvmPublicNic;
+                kvmGuestNic = _kvmGuestNic;
+            } 
+
+            if (kvmPublicNic == null) {
+                kvmPublicNic = (kvmGuestNic != null) ? kvmGuestNic : kvmPrivateNic;
+            }
+
+            if (kvmPrivateNic == null) {
+                kvmPrivateNic = (kvmPublicNic != null) ? kvmPublicNic : kvmGuestNic;
+            }
+
+            if (kvmGuestNic == null) {
+                kvmGuestNic = (kvmPublicNic != null) ? kvmPublicNic : kvmPrivateNic;
+            }
+
+            String parameters = " -m " + _hostIp + " -z " + dcId + " -p " + podId + " -c " + clusterId + " -g " + guid + " -a";
+
+            parameters += " --pubNic=" + kvmPublicNic;
+            parameters += " --prvNic=" + kvmPrivateNic;
+            parameters += " --guestNic=" + kvmGuestNic;
+
+            SSHCmdHelper.sshExecuteCmd(sshConnection, "cloudstack-setup-agent " + parameters, 3);
+
+            KvmDummyResourceBase kvmResource = new KvmDummyResourceBase();
+            Map<String, Object> params = new HashMap<String, Object>();
+
+            params.put("zone", Long.toString(dcId));
+            params.put("pod", Long.toString(podId));
+            params.put("cluster",  Long.toString(clusterId));
+            params.put("guid", guid); 
+            params.put("agentIp", agentIp);
+            kvmResource.configure("kvm agent", params);
+            resources.put(kvmResource, details);
+
+            HostVO connectedHost = waitForHostConnect(dcId, podId, clusterId, guidWithTail);
+            if (connectedHost == null)
+                return null;
+
+            details.put("guid", guidWithTail);
+
+            // place a place holder guid derived from cluster ID
+            if (cluster.getGuid() == null) {
+                cluster.setGuid(UUID.nameUUIDFromBytes(String.valueOf(clusterId).getBytes()).toString());
+                _clusterDao.update(clusterId, cluster);
+            }
+
+            //save user name and password
+            _hostDao.loadDetails(connectedHost);
+            Map<String, String> hostDetails = connectedHost.getDetails();
+            hostDetails.put("password", password);
+            hostDetails.put("username", username);
+            _hostDao.saveDetails(connectedHost);
+            return resources;
+        } catch (DiscoveredWithErrorException e){ 
+            throw e;
+        }catch (Exception e) {
+            String msg = " can't setup agent, due to " + e.toString() + " - " + e.getMessage();
+            s_logger.warn(msg);
+        } finally {
+            if (sshConnection != null)
+                sshConnection.close();
+        }
+
+        return null;
+    }
+
+    private HostVO waitForHostConnect(long dcId, long podId, long clusterId, String guid) {
+        for (int i = 0; i < _waitTime *2; i++) {
+            List<HostVO> hosts = _resourceMgr.listAllUpAndEnabledHosts(Host.Type.Routing, clusterId, podId, dcId);
+            for (HostVO host : hosts) {
+                if (host.getGuid().equalsIgnoreCase(guid)) {
+                    return host;
+                }
+            }
+            try {
+                Thread.sleep(30000);
+            } catch (InterruptedException e) {
+                s_logger.debug("Failed to sleep: " + e.toString());
+            }
+        }
+        s_logger.debug("Timeout, to wait for the host connecting to mgt svr, assuming it is failed");
+        List<HostVO> hosts = _resourceMgr.findHostByGuid(dcId, guid);
+        if (hosts.size() == 1) {
+            return hosts.get(0);
+        } else {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
+//        _setupAgentPath = Script.findScript(getPatchPath(), "setup_agent.sh");
+        _kvmPrivateNic = _configDao.getValue(Config.KvmPrivateNetwork.key());
+        if (_kvmPrivateNic == null) {
+            _kvmPrivateNic = "cloudbr0";
+        }
+
+        _kvmPublicNic = _configDao.getValue(Config.KvmPublicNetwork.key());
+        if (_kvmPublicNic == null) {
+            _kvmPublicNic = _kvmPrivateNic;
+        }
+
+        _kvmGuestNic = _configDao.getValue(Config.KvmGuestNetwork.key());
+        if (_kvmGuestNic == null) {
+            _kvmGuestNic = _kvmPrivateNic;
+        }
+
+        _hostIp = _configDao.getValue("host");
+        if (_hostIp == null) {
+            throw new ConfigurationException("Can't get host IP");
+        }
+        _resourceMgr.registerResourceStateAdapter(this.getClass().getSimpleName(), this);
+        return true;
+    }
+
+    protected String getPatchPath() {
+        return "scripts/vm/hypervisor/kvm/";
+    }
+
+    @Override
+    public void postDiscovery(List<HostVO> hosts, long msId)
+            throws DiscoveryException {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public boolean matchHypervisor(String hypervisor) {
+        // for backwards compatibility, if not supplied, always let to try it
+        if(hypervisor == null)
+            return true;
+
+        return getHypervisorType().toString().equalsIgnoreCase(hypervisor);
+    }
+
+    @Override
+    public HostVO createHostVOForConnectedAgent(HostVO host, StartupCommand[] cmd) {
+        StartupCommand firstCmd = cmd[0];
+        if (!(firstCmd instanceof StartupRoutingCommand)) {
+            return null;
+        }
+
+        StartupRoutingCommand ssCmd = ((StartupRoutingCommand) firstCmd);
+        if (ssCmd.getHypervisorType() != getHypervisorType()) {
+            return null;
+        }
+
+        /* KVM requires host are the same in cluster */
+        ClusterVO clusterVO = _clusterDao.findById(host.getClusterId());
+        List<HostVO> hostsInCluster = _resourceMgr.listAllHostsInCluster(clusterVO.getId());
+        if (!hostsInCluster.isEmpty()) {
+            HostVO oneHost = hostsInCluster.get(0);
+            _hostDao.loadDetails(oneHost);
+            String hostOsInCluster = oneHost.getDetail("Host.OS");
+            String hostOs = ssCmd.getHostDetails().get("Host.OS");
+            if (!hostOsInCluster.equalsIgnoreCase(hostOs)) {
+                throw new IllegalArgumentException("Can't add host: " + firstCmd.getPrivateIpAddress() + " with hostOS: " + hostOs + " into a cluster,"
+                        + "in which there are " + hostOsInCluster + " hosts added");
+            }
+        }
+
+        _hostDao.loadDetails(host);
+
+        return _resourceMgr.fillRoutingHostVO(host, ssCmd, getHypervisorType(), host.getDetails(), null);
+    }
+
+    @Override
+    public HostVO createHostVOForDirectConnectAgent(HostVO host, StartupCommand[] startup, ServerResource resource, Map<String, String> details,
+            List<String> hostTags) {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    @Override
+    public DeleteHostAnswer deleteHost(HostVO host, boolean isForced, boolean isForceDeleteStorage) throws UnableDeleteHostException {
+        if (host.getType() != Host.Type.Routing || 
+            (host.getHypervisorType() != HypervisorType.KVM &&
+             host.getHypervisorType() != HypervisorType.LXC)) {
+            return null;
+        }
+
+        _resourceMgr.deleteRoutingHost(host, isForced, isForceDeleteStorage);
+        try {
+            ShutdownCommand cmd = new ShutdownCommand(ShutdownCommand.DeleteHost, null);
+            _agentMgr.send(host.getId(), cmd);
+        } catch (AgentUnavailableException e) {
+            s_logger.warn("Sending ShutdownCommand failed: ", e);
+        } catch (OperationTimedoutException e) {
+            s_logger.warn("Sending ShutdownCommand failed: ", e);
+        }
+
+        return new DeleteHostAnswer(true);
+    }
+
+    @Override
+    public boolean stop() {
+        _resourceMgr.unregisterResourceStateAdapter(this.getClass().getSimpleName());
+        return super.stop();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/server/src/com/cloud/hypervisor/kvm/discoverer/LxcServerDiscoverer.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/hypervisor/kvm/discoverer/LxcServerDiscoverer.java b/server/src/com/cloud/hypervisor/kvm/discoverer/LxcServerDiscoverer.java
new file mode 100644
index 0000000..0e6a82b
--- /dev/null
+++ b/server/src/com/cloud/hypervisor/kvm/discoverer/LxcServerDiscoverer.java
@@ -0,0 +1,33 @@
+// 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.discoverer;
+
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.resource.Discoverer;
+import org.apache.log4j.Logger;
+
+import javax.ejb.Local;
+
+@Local(value=Discoverer.class)
+public class LxcServerDiscoverer extends LibvirtServerDiscoverer {
+    private static final Logger s_logger = Logger.getLogger(LxcServerDiscoverer.class);
+
+    public Hypervisor.HypervisorType getHypervisorType() {
+        return Hypervisor.HypervisorType.LXC;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/server/src/com/cloud/network/SshKeysDistriMonitor.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/SshKeysDistriMonitor.java b/server/src/com/cloud/network/SshKeysDistriMonitor.java
index fc7c882..82f72de 100755
--- a/server/src/com/cloud/network/SshKeysDistriMonitor.java
+++ b/server/src/com/cloud/network/SshKeysDistriMonitor.java
@@ -72,7 +72,8 @@ public class SshKeysDistriMonitor implements Listener {
 	    public void processConnect(HostVO host, StartupCommand cmd, boolean forRebalance) throws ConnectionException {
 	    	if (cmd instanceof StartupRoutingCommand) {
 	    		if (((StartupRoutingCommand) cmd).getHypervisorType() == HypervisorType.KVM ||
-	    		    ((StartupRoutingCommand) cmd).getHypervisorType() == HypervisorType.XenServer) {
+                    ((StartupRoutingCommand) cmd).getHypervisorType() == HypervisorType.XenServer ||
+                    ((StartupRoutingCommand) cmd).getHypervisorType() == HypervisorType.LXC) {
 	    			/*TODO: Get the private/public keys here*/
 	    			
 	    			String pubKey = _configDao.getValue("ssh.publickey");

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
index 67c94fc..afdbbca 100755
--- a/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
+++ b/server/src/com/cloud/network/router/VirtualNetworkApplianceManagerImpl.java
@@ -1527,9 +1527,11 @@ public class VirtualNetworkApplianceManagerImpl extends ManagerBase implements V
                     DomainRouterVO router = deployRouter(owner, destination, plan, params, isRedundant, vrProvider, offeringId,
                         null, networks, false, null);
 
-                _routerDao.addRouterToGuestNetwork(router, guestNetwork);
-                routers.add(router);
-            }
+                    if (router != null) {
+                        _routerDao.addRouterToGuestNetwork(router, guestNetwork);
+                        routers.add(router);
+                    }
+                }
             }
         } finally {
             if (lock != null) {

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java
index 46ac7af..9a4d6b0 100755
--- a/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java
+++ b/server/src/com/cloud/storage/secondary/SecondaryStorageManagerImpl.java
@@ -747,7 +747,11 @@ public class SecondaryStorageManagerImpl extends ManagerBase implements Secondar
                 }
             } else {
                 if (s_logger.isDebugEnabled()) {
-                    s_logger.debug("Zone host is ready, but secondary storage vm template: " + template.getId() + " is not ready on secondary storage: " + secHost.getId());
+                    if (template == null) {
+                        s_logger.debug("Zone host is ready, but secondary storage vm template does not exist");
+                    } else {
+                        s_logger.debug("Zone host is ready, but secondary storage vm template: " + template.getId() + " is not ready on secondary storage: " + secHost.getId());
+                    }
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/server/src/com/cloud/template/HypervisorTemplateAdapter.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/template/HypervisorTemplateAdapter.java b/server/src/com/cloud/template/HypervisorTemplateAdapter.java
index a0444e5..491900b 100755
--- a/server/src/com/cloud/template/HypervisorTemplateAdapter.java
+++ b/server/src/com/cloud/template/HypervisorTemplateAdapter.java
@@ -141,6 +141,8 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase implements Te
 	        &&(!url.toLowerCase().endsWith("qcow2.bz2"))&&(!url.toLowerCase().endsWith("qcow2.gz"))
 	        &&(!url.toLowerCase().endsWith("ova"))&&(!url.toLowerCase().endsWith("ova.zip"))
 	        &&(!url.toLowerCase().endsWith("ova.bz2"))&&(!url.toLowerCase().endsWith("ova.gz"))
+	        &&(!url.toLowerCase().endsWith("tar"))&&(!url.toLowerCase().endsWith("tar.zip"))
+	        &&(!url.toLowerCase().endsWith("tar.bz2"))&&(!url.toLowerCase().endsWith("tar.gz"))
 	        &&(!url.toLowerCase().endsWith("img"))&&(!url.toLowerCase().endsWith("raw"))){
 	        throw new InvalidParameterValueException("Please specify a valid "+ cmd.getFormat().toLowerCase());
 	    }
@@ -148,6 +150,7 @@ public class HypervisorTemplateAdapter extends TemplateAdapterBase implements Te
 		if ((cmd.getFormat().equalsIgnoreCase("vhd") && (!url.toLowerCase().endsWith("vhd") && !url.toLowerCase().endsWith("vhd.zip") && !url.toLowerCase().endsWith("vhd.bz2") && !url.toLowerCase().endsWith("vhd.gz") ))
 			|| (cmd.getFormat().equalsIgnoreCase("qcow2") && (!url.toLowerCase().endsWith("qcow2") && !url.toLowerCase().endsWith("qcow2.zip") && !url.toLowerCase().endsWith("qcow2.bz2") && !url.toLowerCase().endsWith("qcow2.gz") ))
 			|| (cmd.getFormat().equalsIgnoreCase("ova") && (!url.toLowerCase().endsWith("ova") && !url.toLowerCase().endsWith("ova.zip") && !url.toLowerCase().endsWith("ova.bz2") && !url.toLowerCase().endsWith("ova.gz")))
+			|| (cmd.getFormat().equalsIgnoreCase("tar") && (!url.toLowerCase().endsWith("tar") && !url.toLowerCase().endsWith("tar.zip") && !url.toLowerCase().endsWith("tar.bz2") && !url.toLowerCase().endsWith("tar.gz")))
 			|| (cmd.getFormat().equalsIgnoreCase("raw") && (!url.toLowerCase().endsWith("img") && !url.toLowerCase().endsWith("raw")))) {
 	        throw new InvalidParameterValueException("Please specify a valid URL. URL:" + url + " is an invalid for the format " + cmd.getFormat().toLowerCase());
 		}

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
index 23746ae..af22716 100755
--- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -794,7 +794,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                                 s_logger.info("The guru did not like the answers so stopping " + vm);
                             }
 
-                            StopCommand cmd = new StopCommand(vm.getInstanceName());
+                            StopCommand cmd = new StopCommand(vm);
                             StopAnswer answer = (StopAnswer) _agentMgr.easySend(destHostId, cmd);
                             if (answer == null || !answer.getResult()) {
                                 s_logger.warn("Unable to stop " + vm + " due to " + (answer != null ? answer.getDetails() : "no answers")); 
@@ -876,7 +876,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
 
     protected <T extends VMInstanceVO> boolean sendStop(VirtualMachineGuru<T> guru, VirtualMachineProfile<T> profile, boolean force) {
         VMInstanceVO vm = profile.getVirtualMachine();
-        StopCommand stop = new StopCommand(vm, vm.getInstanceName(), null);
+        StopCommand stop = new StopCommand(vm);
         try {
             Answer answer = _agentMgr.send(vm.getHostId(), stop);
             if (!answer.getResult()) {
@@ -1061,8 +1061,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
         }
 
         vmGuru.prepareStop(profile);
-
-        StopCommand stop = new StopCommand(vm, vm.getInstanceName(), null);
+        
+        StopCommand stop = new StopCommand(vm);
         boolean stopped = false;
         StopAnswer answer = null;
         try {
@@ -1380,7 +1380,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                 if (!checkVmOnHost(vm, dstHostId)) {
                     s_logger.error("Unable to complete migration for " + vm);
                     try {
-                        _agentMgr.send(srcHostId, new Commands(cleanup(vm.getInstanceName())), null);
+                        _agentMgr.send(srcHostId, new Commands(cleanup(vm)), null);
                     } catch (AgentUnavailableException e) {
                         s_logger.error("AgentUnavailableException while cleanup on source host: " + srcHostId);
                     }
@@ -1399,7 +1399,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                 _alertMgr.sendAlert(alertType, fromHost.getDataCenterId(), fromHost.getPodId(), "Unable to migrate vm " + vm.getInstanceName() + " from host " + fromHost.getName() + " in zone "
                         + dest.getDataCenter().getName() + " and pod " + dest.getPod().getName(), "Migrate Command failed.  Please check logs.");
                 try {
-                    _agentMgr.send(dstHostId, new Commands(cleanup(vm.getInstanceName())), null);
+                    _agentMgr.send(dstHostId, new Commands(cleanup(vm)), null);
                 } catch (AgentUnavailableException ae) {
                     s_logger.info("Looks like the destination Host is unavailable for cleanup");
                 }
@@ -1620,6 +1620,10 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
         return guru.findById(vmId);
     }
 
+    public Command cleanup(VirtualMachine vm) {
+        return new StopCommand(vm);
+    }
+
     public Command cleanup(String vmName) {
         return new StopCommand(vmName);
     }
@@ -1989,7 +1993,6 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
      */
     protected Command compareState(long hostId, VMInstanceVO vm, final AgentVmInfo info, final boolean fullSync, boolean trackExternalChange) {
         State agentState = info.state;
-        final String agentName = info.name;
         final State serverState = vm.getState();
         final String serverName = vm.getInstanceName();
 
@@ -2092,7 +2095,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
             }
 
             s_logger.debug("State matches but the agent said stopped so let's send a cleanup command anyways.");
-            return cleanup(agentName);
+            return cleanup(vm);
         }
 
         if (agentState == State.Shutdowned) {
@@ -2110,8 +2113,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                     return null;
                 }
             } else {
-                s_logger.debug("Sending cleanup to a shutdowned vm: " + agentName);
-                command = cleanup(agentName);
+                s_logger.debug("Sending cleanup to a shutdowned vm: " + vm.getInstanceName());
+                command = cleanup(vm);
             }
         } else if (agentState == State.Stopped) {
             // This state means the VM on the agent was detected previously
@@ -2129,7 +2132,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                 s_logger.debug("Ignoring VM in starting mode: " + vm.getInstanceName());
                 _haMgr.scheduleRestart(vm, false);
             }
-            command = cleanup(agentName);
+            command = cleanup(vm);
         } else if (agentState == State.Running) {
             if (serverState == State.Starting) {
                 if (fullSync) {
@@ -2155,7 +2158,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
                 s_logger.debug("server VM state " + serverState + " does not meet expectation of a running VM report from agent");
 
                 // just be careful not to stop VM for things we don't handle
-                // command = cleanup(agentName);
+                // command = cleanup(vm);
             }
         }
         return command;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/setup/db/db/schema-410to420.sql
----------------------------------------------------------------------
diff --git a/setup/db/db/schema-410to420.sql b/setup/db/db/schema-410to420.sql
index 53f3c44..3dee7ae 100644
--- a/setup/db/db/schema-410to420.sql
+++ b/setup/db/db/schema-410to420.sql
@@ -393,3 +393,15 @@ CREATE VIEW `cloud`.`account_view` AS
 
 ALTER TABLE remote_access_vpn ADD COLUMN `id` bigint unsigned NOT NULL UNIQUE AUTO_INCREMENT COMMENT 'id';
 ALTER TABLE remote_access_vpn ADD COLUMN `uuid` varchar(40) UNIQUE;
+
+-- START: support for LXC
+ 
+INSERT IGNORE INTO `cloud`.`hypervisor_capabilities`(hypervisor_type, hypervisor_version, max_guests_limit, security_group_enabled) VALUES ('LXC', 'default', 50, 1);
+ALTER TABLE `cloud`.`physical_network_traffic_types` ADD COLUMN `lxc_network_label` varchar(255) DEFAULT 'cloudbr0' COMMENT 'The network name label of the physical device dedicated to this traffic on a LXC host';
+ 
+UPDATE configuration SET value='KVM,XenServer,VMware,BareMetal,Ovm,LXC' WHERE name='hypervisor.list';
+ 
+INSERT INTO `cloud`.`vm_template` (id, unique_name, name, public, created, type, hvm, bits, account_id, url, checksum, enable_password, display_text, format, guest_os_id, featured, cross_zones, hypervisor_type)
+     VALUES (10, 'routing-10', 'SystemVM Template (LXC)', 0, now(), 'SYSTEM', 0, 64, 1, 'http://download.cloud.com/templates/acton/acton-systemvm-02062012.qcow2.bz2', '2755de1f9ef2ce4d6f2bee2efbb4da92', 0, 'SystemVM Template (LXC)', 'QCOW2', 15, 0, 1, 'LXC');
+
+-- END: support for LXC

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/ui/scripts/system.js
----------------------------------------------------------------------
diff --git a/ui/scripts/system.js b/ui/scripts/system.js
index 9d6c476..98570d2 100644
--- a/ui/scripts/system.js
+++ b/ui/scripts/system.js
@@ -114,28 +114,34 @@
       }
     });
 
-		if(trafficType.xennetworklabel == null || trafficType.xennetworklabel == 0)
-		  trafficType.xennetworklabel = dictionary['label.network.label.display.for.blank.value'];
-		if(trafficType.kvmnetworklabel == null || trafficType.kvmnetworklabel == 0)
-		  trafficType.kvmnetworklabel = dictionary['label.network.label.display.for.blank.value'];
-		if(trafficType.vmwarenetworklabel == null || trafficType.vmwarenetworklabel == 0)
-		  trafficType.vmwarenetworklabel = dictionary['label.network.label.display.for.blank.value'];
-	        if(trafficType.ovmnetworklabel == null || trafficType.ovmnetworklabel == 0)
-                   trafficType.ovmnetworklabel = dictionary['label.network.label.display.for.blank.value'];	
+    if(trafficType.xennetworklabel == null || trafficType.xennetworklabel == 0)
+      trafficType.xennetworklabel = dictionary['label.network.label.display.for.blank.value'];
+    if(trafficType.kvmnetworklabel == null || trafficType.kvmnetworklabel == 0)
+      trafficType.kvmnetworklabel = dictionary['label.network.label.display.for.blank.value'];
+    if(trafficType.vmwarenetworklabel == null || trafficType.vmwarenetworklabel == 0)
+      trafficType.vmwarenetworklabel = dictionary['label.network.label.display.for.blank.value'];
+    if(trafficType.ovmnetworklabel == null || trafficType.ovmnetworklabel == 0)
+      trafficType.ovmnetworklabel = dictionary['label.network.label.display.for.blank.value'];
+    if(trafficType.lxcnetworklabel == null || trafficType.lxcnetworklabel == 0)
+      trafficType.lxcnetworklabel = dictionary['label.network.label.display.for.blank.value'];
+
     return trafficType;
   };
 
   var updateTrafficLabels = function(trafficType, labels, complete) {
     var array1 = [];
-		if(labels.xennetworklabel != dictionary['label.network.label.display.for.blank.value'])
-		  array1.push("&xennetworklabel=" + labels.xennetworklabel);
-		if(labels.kvmnetworklabel != dictionary['label.network.label.display.for.blank.value'])
-		  array1.push("&kvmnetworklabel=" + labels.kvmnetworklabel);
-		if(labels.vmwarenetworklabel != dictionary['label.network.label.display.for.blank.value'])
-		  array1.push("&vmwarenetworklabel=" + labels.vmwarenetworklabel);
-	        if(labels.ovmnetworklabel != dictionary['label.network.label.display.for.blank.value'])
-                  array1.push("&ovmnetworklabel=" + labels.ovmnetworklabel); 		
-		$.ajax({
+    if(labels.xennetworklabel != dictionary['label.network.label.display.for.blank.value'])
+      array1.push("&xennetworklabel=" + labels.xennetworklabel);
+    if(labels.kvmnetworklabel != dictionary['label.network.label.display.for.blank.value'])
+      array1.push("&kvmnetworklabel=" + labels.kvmnetworklabel);
+    if(labels.vmwarenetworklabel != dictionary['label.network.label.display.for.blank.value'])
+      array1.push("&vmwarenetworklabel=" + labels.vmwarenetworklabel);
+    if(labels.ovmnetworklabel != dictionary['label.network.label.display.for.blank.value'])
+      array1.push("&ovmnetworklabel=" + labels.ovmnetworklabel);
+    if(labels.lxcnetworklabel != dictionary['label.network.label.display.for.blank.value'])
+      array1.push("&lxcnetworklabel=" + labels.lxcnetworklabel);
+
+    $.ajax({
       url: createURL('updateTrafficType' + array1.join("")),
       data: {
         id: trafficType.id       
@@ -463,7 +469,8 @@
                     xennetworklabel: { label: 'label.xen.traffic.label', isEditable: true },
                     kvmnetworklabel: { label: 'label.kvm.traffic.label', isEditable: true },
                     vmwarenetworklabel: { label: 'label.vmware.traffic.label', isEditable: true },
-                    ovmnetworklabel: { label: 'OVM traffic label',isEditable: true }
+                    ovmnetworklabel: { label: 'OVM traffic label',isEditable: true },
+                    lxcnetworklabel: { label: 'label.lxc.traffic.label',isEditable: true }
                   }
                 ],
 
@@ -483,6 +490,7 @@
                       selectedPublicNetworkObj.kvmnetworklabel = trafficType.kvmnetworklabel;
                       selectedPublicNetworkObj.vmwarenetworklabel = trafficType.vmwarenetworklabel;
                       selectedPublicNetworkObj.ovmnetworklabel = trafficType.ovmnetworklabel;
+                      selectedPublicNetworkObj.lxcnetworklabel = trafficType.lxcnetworklabel;
 
                       args.response.success({data: selectedPublicNetworkObj});
                     }
@@ -636,7 +644,8 @@
                     xennetworklabel: { label: 'label.xen.traffic.label', isEditable: true },
                     kvmnetworklabel: { label: 'label.kvm.traffic.label', isEditable: true },
                     vmwarenetworklabel: { label: 'label.vmware.traffic.label', isEditable: true },
-                    ovmnetworklabel: { label: 'OVM traffic label', isEditable: true }
+                    ovmnetworklabel: { label: 'OVM traffic label', isEditable: true },
+                    lxcnetworklabel: { label: 'label.lxc.traffic.label', isEditable: true }
                   }
                 ],
 
@@ -795,7 +804,8 @@
                     xennetworklabel: { label: 'label.xen.traffic.label', isEditable: true },
                     kvmnetworklabel: { label: 'label.kvm.traffic.label', isEditable: true },
                     vmwarenetworklabel: { label: 'label.vmware.traffic.label', isEditable: true },
-                    ovmnetworklabel: { label: 'OVM traffic label', isEditable: true } 
+                    ovmnetworklabel: { label: 'OVM traffic label', isEditable: true },
+                    lxcnetworklabel: { label: 'label.lxc.traffic.label', isEditable: true }
                   }
                 ],
                 dataProvider: function(args) {
@@ -811,6 +821,7 @@
                       selectedManagementNetworkObj.kvmnetworklabel = trafficType.kvmnetworklabel;
                       selectedManagementNetworkObj.vmwarenetworklabel = trafficType.vmwarenetworklabel;
                       selectedManagementNetworkObj.ovmnetworklabel = trafficType.ovmnetworklabel;
+                      selectedManagementNetworkObj.lxcnetworklabel = trafficType.lxcnetworklabel;
                       args.response.success({ data: selectedManagementNetworkObj });
                     }
                   });
@@ -928,7 +939,8 @@
                     xennetworklabel: { label: 'label.xen.traffic.label', isEditable: true },
                     kvmnetworklabel: { label: 'label.kvm.traffic.label', isEditable: true },
                     vmwarenetworklabel: { label: 'label.vmware.traffic.label', isEditable: true },
-                    ovmnetworklabel: { label: 'OVM traffic label', isEditable: true }
+                    ovmnetworklabel: { label: 'OVM traffic label', isEditable: true },
+                    lxcnetworklabel: { label: 'label.lxc.traffic.label', isEditable: true }
                   }
                 ],
                 dataProvider: function(args) { //physical network + Guest traffic type       
@@ -964,6 +976,7 @@
 											selectedPhysicalNetworkObj["kvmnetworklabel"] = trafficType.kvmnetworklabel;
 											selectedPhysicalNetworkObj["vmwarenetworklabel"] = trafficType.vmwarenetworklabel;
                                                                                         selectedPhysicalNetworkObj["ovmnetworklabel"] = trafficType.ovmnetworklabel;
+                                                                                        selectedPhysicalNetworkObj["lxcnetworklabel"] = trafficType.lxcnetworklabel;
 
 											args.response.success({
 												actionFilter: function() {
@@ -9491,6 +9504,13 @@
                         items.push({id: "ocfs2", description: "ocfs2"});
                         args.response.success({data: items});
                       }
+                      else if(selectedClusterObj.hypervisortype == "LXC") {
+                        var items = [];
+                        items.push({id: "nfs", description: "nfs"});
+                        items.push({id: "SharedMountPoint", description: "SharedMountPoint"});
+                        items.push({id: "rbd", description: "RBD"});
+                        args.response.success({data: items});
+                      }
                       else {
                         args.response.success({data:[]});
                       }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/ui/scripts/templates.js
----------------------------------------------------------------------
diff --git a/ui/scripts/templates.js b/ui/scripts/templates.js
index 040ce4a..6268f6b 100644
--- a/ui/scripts/templates.js
+++ b/ui/scripts/templates.js
@@ -237,6 +237,10 @@
                         //formatSelect.append("<option value='RAW'>RAW</option>");
                         items.push({id:'RAW', description: 'RAW'});
                       }
+                      else if(args.hypervisor == "LXC") {
+                        //formatSelect.append("<option value='TAR'>TAR</option>");
+                        items.push({id:'TAR', description: 'TAR'});
+                      }
                       args.response.success({data: items});
                     }
                   },

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/aa79ccf9/ui/scripts/zoneWizard.js
----------------------------------------------------------------------
diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js
index a64c86a..76fd5e9 100755
--- a/ui/scripts/zoneWizard.js
+++ b/ui/scripts/zoneWizard.js
@@ -53,6 +53,9 @@
       case 'Ovm':
         hypervisorAttr = 'ovmnetworklabel';
         break;
+      case 'LXC':
+        hypervisorAttr = 'lxcnetworklabel';
+        break;
     }
 
     trafficLabelStr = trafficLabel ? '&' + hypervisorAttr + '=' + trafficLabel : '';
@@ -395,6 +398,7 @@
 										nonSupportedHypervisors["VMware"] = 1;
 										nonSupportedHypervisors["BareMetal"] = 1;
 										nonSupportedHypervisors["Ovm"] = 1;
+										nonSupportedHypervisors["LXC"] = 1;
 									}
 									
 									if(items != null) {
@@ -1256,6 +1260,12 @@
                 items.push({id: "ocfs2", description: "ocfs2"});
                 args.response.success({data: items});
               }
+              else if(selectedClusterObj.hypervisortype == "LXC") {
+                var items = [];
+                items.push({id: "nfs", description: "nfs"});
+                items.push({id: "SharedMountPoint", description: "SharedMountPoint"});
+                args.response.success({data: items});
+              }
               else {
                 args.response.success({data:[]});
               }