You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ko...@apache.org on 2015/05/19 18:21:01 UTC

[15/50] [abbrv] git commit: updated refs/heads/CLOUDSTACK-8301 to 8ff9000

Refactoring the LibvirtComputingResource
  - Adding LibvirtBackupSnapshotCommandWrapper, LibvirtCreatePrivateTemplateFromVolumeCommandWrapper and LibvirtManageSnapshotCommandWrapper
  - 3 unit tests added
  - KVM hypervisor plugin with 18.3% coverage

Less tests added to those classes because the code is quite complex and way too long.
The tests added are just covering the new flow, to make sure it works fine. I will come back to those classes later.


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

Branch: refs/heads/CLOUDSTACK-8301
Commit: bcf78d3b4304800d9a60db72742fb169c470093b
Parents: 52d9f0c
Author: wilderrodrigues <wr...@schubergphilis.com>
Authored: Tue May 5 11:48:55 2015 +0200
Committer: wilderrodrigues <wr...@schubergphilis.com>
Committed: Wed May 6 19:24:13 2015 +0200

----------------------------------------------------------------------
 .../kvm/resource/LibvirtComputingResource.java  | 402 +------------------
 .../LibvirtBackupSnapshotCommandWrapper.java    | 204 ++++++++++
 ...PrivateTemplateFromVolumeCommandWrapper.java | 176 ++++++++
 .../LibvirtManageSnapshotCommandWrapper.java    | 163 ++++++++
 .../resource/wrapper/LibvirtRequestWrapper.java |   6 +
 .../resource/LibvirtComputingResourceTest.java  | 137 ++++++-
 6 files changed, 699 insertions(+), 389 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bcf78d3b/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 3c40c90..4e8b4cf 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java
@@ -16,26 +16,20 @@
 // under the License.
 package com.cloud.hypervisor.kvm.resource;
 
-import java.io.BufferedOutputStream;
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.FileReader;
 import java.io.IOException;
 import java.io.Reader;
 import java.net.InetAddress;
 import java.net.URI;
 import java.net.URISyntaxException;
-import java.text.DateFormat;
-import java.text.MessageFormat;
-import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Date;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -54,10 +48,7 @@ import javax.naming.ConfigurationException;
 import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
 import org.apache.cloudstack.storage.to.PrimaryDataStoreTO;
 import org.apache.cloudstack.storage.to.VolumeObjectTO;
-import org.apache.cloudstack.utils.qemu.QemuImg;
 import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
-import org.apache.cloudstack.utils.qemu.QemuImgException;
-import org.apache.cloudstack.utils.qemu.QemuImgFile;
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.IOUtils;
 import org.apache.log4j.Logger;
@@ -67,26 +58,14 @@ import org.libvirt.DomainBlockStats;
 import org.libvirt.DomainInfo;
 import org.libvirt.DomainInfo.DomainState;
 import org.libvirt.DomainInterfaceStats;
-import org.libvirt.DomainSnapshot;
 import org.libvirt.LibvirtException;
 import org.libvirt.NodeInfo;
 import org.libvirt.StorageVol;
 
-import com.ceph.rados.IoCTX;
-import com.ceph.rados.Rados;
-import com.ceph.rados.RadosException;
-import com.ceph.rbd.Rbd;
-import com.ceph.rbd.RbdException;
-import com.ceph.rbd.RbdImage;
 import com.cloud.agent.api.Answer;
-import com.cloud.agent.api.BackupSnapshotAnswer;
-import com.cloud.agent.api.BackupSnapshotCommand;
 import com.cloud.agent.api.Command;
 import com.cloud.agent.api.CreatePrivateTemplateFromSnapshotCommand;
-import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
 import com.cloud.agent.api.HostVmStateReportEntry;
-import com.cloud.agent.api.ManageSnapshotAnswer;
-import com.cloud.agent.api.ManageSnapshotCommand;
 import com.cloud.agent.api.PingCommand;
 import com.cloud.agent.api.PingRoutingCommand;
 import com.cloud.agent.api.PingRoutingWithNwGroupsCommand;
@@ -156,7 +135,6 @@ import com.cloud.resource.ServerResource;
 import com.cloud.resource.ServerResourceBase;
 import com.cloud.storage.JavaStorageLayer;
 import com.cloud.storage.Storage;
-import com.cloud.storage.Storage.ImageFormat;
 import com.cloud.storage.Storage.StoragePoolType;
 import com.cloud.storage.StorageLayer;
 import com.cloud.storage.Volume;
@@ -234,7 +212,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
     public static final String SSHPUBKEYPATH = SSHKEYSPATH + File.separator + "id_rsa.pub.cloud";
 
     private String _mountPoint = "/mnt";
-    StorageLayer _storage;
+    private StorageLayer _storage;
     private KVMStoragePoolManager _storagePoolMgr;
 
     private VifDriver _defaultVifDriver;
@@ -276,9 +254,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
     private final Map <String, String> _pifs = new HashMap<String, String>();
     private final Map<String, VmStats> _vmStats = new ConcurrentHashMap<String, VmStats>();
 
-    protected static final MessageFormat SnapshotXML = new MessageFormat("   <domainsnapshot>" + "       <name>{0}</name>" + "          <domain>"
-            + "            <uuid>{1}</uuid>" + "        </domain>" + "    </domainsnapshot>");
-
     protected static final HashMap<DomainState, PowerState> s_powerStatesTable;
     static {
         s_powerStatesTable = new HashMap<DomainState, PowerState>();
@@ -414,6 +389,22 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         return _monitor;
     }
 
+    public StorageLayer getStorage() {
+        return _storage;
+    }
+
+    public String createTmplPath() {
+        return _createTmplPath;
+    }
+
+    public int getCmdsTimeout() {
+        return _cmdsTimeout;
+    }
+
+    public String manageSnapshotPath() {
+        return _manageSnapshotPath;
+    }
+
     private static final class KeyValueInterpreter extends OutputInterpreter {
         private final Map<String, String> map = new HashMap<String, String>();
 
@@ -1261,13 +1252,7 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         }
 
         try {
-            if (cmd instanceof CreatePrivateTemplateFromVolumeCommand) {
-                return execute((CreatePrivateTemplateFromVolumeCommand)cmd);
-            } else if (cmd instanceof ManageSnapshotCommand) {
-                return execute((ManageSnapshotCommand)cmd);
-            } else if (cmd instanceof BackupSnapshotCommand) {
-                return execute((BackupSnapshotCommand)cmd);
-            } else if (cmd instanceof CreatePrivateTemplateFromSnapshotCommand) {
+            if (cmd instanceof CreatePrivateTemplateFromSnapshotCommand) {
                 return execute((CreatePrivateTemplateFromSnapshotCommand)cmd);
             } else if (cmd instanceof StartCommand) {
                 return execute((StartCommand)cmd);
@@ -1899,248 +1884,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         return new ExecutionResult(true, null);
     }
 
-    protected ManageSnapshotAnswer execute(final ManageSnapshotCommand cmd) {
-        final String snapshotName = cmd.getSnapshotName();
-        final String snapshotPath = cmd.getSnapshotPath();
-        final String vmName = cmd.getVmName();
-        try {
-            final Connect conn = LibvirtConnection.getConnectionByVmName(vmName);
-            DomainState state = null;
-            Domain vm = null;
-            if (vmName != null) {
-                try {
-                    vm = getDomain(conn, cmd.getVmName());
-                    state = vm.getInfo().state;
-                } catch (final LibvirtException e) {
-                    s_logger.trace("Ignoring libvirt error.", e);
-                }
-            }
-
-            final KVMStoragePool primaryPool = _storagePoolMgr.getStoragePool(cmd.getPool().getType(), cmd.getPool().getUuid());
-
-            final KVMPhysicalDisk disk = primaryPool.getPhysicalDisk(cmd.getVolumePath());
-            if (state == DomainState.VIR_DOMAIN_RUNNING && !primaryPool.isExternalSnapshot()) {
-                final String vmUuid = vm.getUUIDString();
-                final Object[] args = new Object[] {snapshotName, vmUuid};
-                final String snapshot = SnapshotXML.format(args);
-                s_logger.debug(snapshot);
-                if (cmd.getCommandSwitch().equalsIgnoreCase(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
-                    vm.snapshotCreateXML(snapshot);
-                } else {
-                    final DomainSnapshot snap = vm.snapshotLookupByName(snapshotName);
-                    snap.delete(0);
-                }
-
-                /*
-                 * libvirt on RHEL6 doesn't handle resume event emitted from
-                 * qemu
-                 */
-                vm = getDomain(conn, cmd.getVmName());
-                state = vm.getInfo().state;
-                if (state == DomainState.VIR_DOMAIN_PAUSED) {
-                    vm.resume();
-                }
-            } else {
-                /**
-                 * For RBD we can't use libvirt to do our snapshotting or any Bash scripts.
-                 * libvirt also wants to store the memory contents of the Virtual Machine,
-                 * but that's not possible with RBD since there is no way to store the memory
-                 * contents in RBD.
-                 *
-                 * So we rely on the Java bindings for RBD to create our snapshot
-                 *
-                 * This snapshot might not be 100% consistent due to writes still being in the
-                 * memory of the Virtual Machine, but if the VM runs a kernel which supports
-                 * barriers properly (>2.6.32) this won't be any different then pulling the power
-                 * cord out of a running machine.
-                 */
-                if (primaryPool.getType() == StoragePoolType.RBD) {
-                    try {
-                        final Rados r = new Rados(primaryPool.getAuthUserName());
-                        r.confSet("mon_host", primaryPool.getSourceHost() + ":" + primaryPool.getSourcePort());
-                        r.confSet("key", primaryPool.getAuthSecret());
-                        r.confSet("client_mount_timeout", "30");
-                        r.connect();
-                        s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host"));
-
-                        final IoCTX io = r.ioCtxCreate(primaryPool.getSourceDir());
-                        final Rbd rbd = new Rbd(io);
-                        final RbdImage image = rbd.open(disk.getName());
-
-                        if (cmd.getCommandSwitch().equalsIgnoreCase(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
-                            s_logger.debug("Attempting to create RBD snapshot " + disk.getName() + "@" + snapshotName);
-                            image.snapCreate(snapshotName);
-                        } else {
-                            s_logger.debug("Attempting to remove RBD snapshot " + disk.getName() + "@" + snapshotName);
-                            image.snapRemove(snapshotName);
-                        }
-
-                        rbd.close(image);
-                        r.ioCtxDestroy(io);
-                    } catch (final Exception e) {
-                        s_logger.error("A RBD snapshot operation on " + disk.getName() + " failed. The error was: " + e.getMessage());
-                    }
-                } else {
-                    /* VM is not running, create a snapshot by ourself */
-                    final Script command = new Script(_manageSnapshotPath, _cmdsTimeout, s_logger);
-                    if (cmd.getCommandSwitch().equalsIgnoreCase(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
-                        command.add("-c", disk.getPath());
-                    } else {
-                        command.add("-d", snapshotPath);
-                    }
-
-                    command.add("-n", snapshotName);
-                    final String result = command.execute();
-                    if (result != null) {
-                        s_logger.debug("Failed to manage snapshot: " + result);
-                        return new ManageSnapshotAnswer(cmd, false, "Failed to manage snapshot: " + result);
-                    }
-                }
-            }
-            return new ManageSnapshotAnswer(cmd, cmd.getSnapshotId(), disk.getPath() + File.separator + snapshotName, true, null);
-        } catch (final LibvirtException e) {
-            s_logger.debug("Failed to manage snapshot: " + e.toString());
-            return new ManageSnapshotAnswer(cmd, false, "Failed to manage snapshot: " + e.toString());
-        }
-
-    }
-
-    protected BackupSnapshotAnswer execute(final BackupSnapshotCommand cmd) {
-        final Long dcId = cmd.getDataCenterId();
-        final Long accountId = cmd.getAccountId();
-        final Long volumeId = cmd.getVolumeId();
-        final String secondaryStoragePoolUrl = cmd.getSecondaryStorageUrl();
-        final String snapshotName = cmd.getSnapshotName();
-        String snapshotDestPath = null;
-        String snapshotRelPath = null;
-        final String vmName = cmd.getVmName();
-        KVMStoragePool secondaryStoragePool = null;
-        try {
-            final Connect conn = LibvirtConnection.getConnectionByVmName(vmName);
-
-            secondaryStoragePool = _storagePoolMgr.getStoragePoolByURI(secondaryStoragePoolUrl);
-
-            final String ssPmountPath = secondaryStoragePool.getLocalPath();
-            snapshotRelPath = File.separator + "snapshots" + File.separator + dcId + File.separator + accountId + File.separator + volumeId;
-
-            snapshotDestPath = ssPmountPath + File.separator + "snapshots" + File.separator + dcId + File.separator + accountId + File.separator + volumeId;
-            final KVMStoragePool primaryPool = _storagePoolMgr.getStoragePool(cmd.getPool().getType(), cmd.getPrimaryStoragePoolNameLabel());
-            final KVMPhysicalDisk snapshotDisk = primaryPool.getPhysicalDisk(cmd.getVolumePath());
-
-            /**
-             * RBD snapshots can't be copied using qemu-img, so we have to use
-             * the Java bindings for librbd here.
-             *
-             * These bindings will read the snapshot and write the contents to
-             * the secondary storage directly
-             *
-             * It will stop doing so if the amount of time spend is longer then
-             * cmds.timeout
-             */
-            if (primaryPool.getType() == StoragePoolType.RBD) {
-                try {
-                    final Rados r = new Rados(primaryPool.getAuthUserName());
-                    r.confSet("mon_host", primaryPool.getSourceHost() + ":" + primaryPool.getSourcePort());
-                    r.confSet("key", primaryPool.getAuthSecret());
-                    r.confSet("client_mount_timeout", "30");
-                    r.connect();
-                    s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host"));
-
-                    final IoCTX io = r.ioCtxCreate(primaryPool.getSourceDir());
-                    final Rbd rbd = new Rbd(io);
-                    final RbdImage image = rbd.open(snapshotDisk.getName(), snapshotName);
-                    final File fh = new File(snapshotDestPath);
-                    try(BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fh));) {
-                        final int chunkSize = 4194304;
-                        long offset = 0;
-                        s_logger.debug("Backuping up RBD snapshot " + snapshotName + " to  " + snapshotDestPath);
-                        while (true) {
-                            final byte[] buf = new byte[chunkSize];
-                            final int bytes = image.read(offset, buf, chunkSize);
-                            if (bytes <= 0) {
-                                break;
-                            }
-                            bos.write(buf, 0, bytes);
-                            offset += bytes;
-                        }
-                        s_logger.debug("Completed backing up RBD snapshot " + snapshotName + " to  " + snapshotDestPath + ". Bytes written: " + offset);
-                    }catch(final IOException ex)
-                    {
-                        s_logger.error("BackupSnapshotAnswer:Exception:"+ ex.getMessage());
-                    }
-                    r.ioCtxDestroy(io);
-                } catch (final RadosException e) {
-                    s_logger.error("A RADOS operation failed. The error was: " + e.getMessage());
-                    return new BackupSnapshotAnswer(cmd, false, e.toString(), null, true);
-                } catch (final RbdException e) {
-                    s_logger.error("A RBD operation on " + snapshotDisk.getName() + " failed. The error was: " + e.getMessage());
-                    return new BackupSnapshotAnswer(cmd, false, e.toString(), null, true);
-                }
-            } else {
-                final Script command = new Script(_manageSnapshotPath, _cmdsTimeout, s_logger);
-                command.add("-b", snapshotDisk.getPath());
-                command.add("-n", snapshotName);
-                command.add("-p", snapshotDestPath);
-                command.add("-t", snapshotName);
-                final String result = command.execute();
-                if (result != null) {
-                    s_logger.debug("Failed to backup snaptshot: " + result);
-                    return new BackupSnapshotAnswer(cmd, false, result, null, true);
-                }
-            }
-            /* Delete the snapshot on primary */
-
-            DomainState state = null;
-            Domain vm = null;
-            if (vmName != null) {
-                try {
-                    vm = getDomain(conn, cmd.getVmName());
-                    state = vm.getInfo().state;
-                } catch (final LibvirtException e) {
-                    s_logger.trace("Ignoring libvirt error.", e);
-                }
-            }
-
-            final KVMStoragePool primaryStorage = _storagePoolMgr.getStoragePool(cmd.getPool().getType(), cmd.getPool().getUuid());
-            if (state == DomainState.VIR_DOMAIN_RUNNING && !primaryStorage.isExternalSnapshot()) {
-                final String vmUuid = vm.getUUIDString();
-                final Object[] args = new Object[] {snapshotName, vmUuid};
-                final String snapshot = SnapshotXML.format(args);
-                s_logger.debug(snapshot);
-                final DomainSnapshot snap = vm.snapshotLookupByName(snapshotName);
-                snap.delete(0);
-
-                /*
-                 * libvirt on RHEL6 doesn't handle resume event emitted from
-                 * qemu
-                 */
-                vm = getDomain(conn, cmd.getVmName());
-                state = vm.getInfo().state;
-                if (state == DomainState.VIR_DOMAIN_PAUSED) {
-                    vm.resume();
-                }
-            } else {
-                final Script command = new Script(_manageSnapshotPath, _cmdsTimeout, s_logger);
-                command.add("-d", snapshotDisk.getPath());
-                command.add("-n", snapshotName);
-                final String result = command.execute();
-                if (result != null) {
-                    s_logger.debug("Failed to backup snapshot: " + result);
-                    return new BackupSnapshotAnswer(cmd, false, "Failed to backup snapshot: " + result, null, true);
-                }
-            }
-        } catch (final LibvirtException e) {
-            return new BackupSnapshotAnswer(cmd, false, e.toString(), null, true);
-        } catch (final CloudRuntimeException e) {
-            return new BackupSnapshotAnswer(cmd, false, e.toString(), null, true);
-        } finally {
-            if (secondaryStoragePool != null) {
-                _storagePoolMgr.deleteStoragePool(secondaryStoragePool.getType(), secondaryStoragePool.getUuid());
-            }
-        }
-        return new BackupSnapshotAnswer(cmd, true, null, snapshotRelPath + File.separator + snapshotName, true);
-    }
-
     protected CreatePrivateTemplateAnswer execute(final CreatePrivateTemplateFromSnapshotCommand cmd) {
         final String templateFolder = cmd.getAccountId() + File.separator + cmd.getNewTemplateId();
         final String templateInstallFolder = "template/tmpl/" + templateFolder;
@@ -2198,115 +1941,6 @@ public class LibvirtComputingResource extends ServerResourceBase implements Serv
         }
     }
 
-    protected CreatePrivateTemplateAnswer execute(final CreatePrivateTemplateFromVolumeCommand cmd) {
-        final String secondaryStorageURL = cmd.getSecondaryStorageUrl();
-
-        KVMStoragePool secondaryStorage = null;
-        KVMStoragePool primary = null;
-        try {
-            final String templateFolder = cmd.getAccountId() + File.separator + cmd.getTemplateId() + File.separator;
-            final String templateInstallFolder = "/template/tmpl/" + templateFolder;
-
-            secondaryStorage = _storagePoolMgr.getStoragePoolByURI(secondaryStorageURL);
-
-            try {
-                primary = _storagePoolMgr.getStoragePool(cmd.getPool().getType(), cmd.getPrimaryStoragePoolNameLabel());
-            } catch (final CloudRuntimeException e) {
-                if (e.getMessage().contains("not found")) {
-                    primary =
-                            _storagePoolMgr.createStoragePool(cmd.getPool().getUuid(), cmd.getPool().getHost(), cmd.getPool().getPort(), cmd.getPool().getPath(),
-                                    cmd.getPool().getUserInfo(), cmd.getPool().getType());
-                } else {
-                    return new CreatePrivateTemplateAnswer(cmd, false, e.getMessage());
-                }
-            }
-
-            final KVMPhysicalDisk disk = primary.getPhysicalDisk(cmd.getVolumePath());
-            final String tmpltPath = secondaryStorage.getLocalPath() + File.separator + templateInstallFolder;
-            _storage.mkdirs(tmpltPath);
-
-            if (primary.getType() != StoragePoolType.RBD) {
-                final Script command = new Script(_createTmplPath, _cmdsTimeout, s_logger);
-                command.add("-f", disk.getPath());
-                command.add("-t", tmpltPath);
-                command.add("-n", cmd.getUniqueName() + ".qcow2");
-
-                final String result = command.execute();
-
-                if (result != null) {
-                    s_logger.debug("failed to create template: " + result);
-                    return new CreatePrivateTemplateAnswer(cmd, false, result);
-                }
-            } else {
-                s_logger.debug("Converting RBD disk " + disk.getPath() + " into template " + cmd.getUniqueName());
-
-                final QemuImgFile srcFile =
-                        new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), primary.getSourcePort(), primary.getAuthUserName(),
-                                primary.getAuthSecret(), disk.getPath()));
-                srcFile.setFormat(PhysicalDiskFormat.RAW);
-
-                final QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + cmd.getUniqueName() + ".qcow2");
-                destFile.setFormat(PhysicalDiskFormat.QCOW2);
-
-                final QemuImg q = new QemuImg(0);
-                try {
-                    q.convert(srcFile, destFile);
-                } catch (final QemuImgException e) {
-                    s_logger.error("Failed to create new template while converting " + srcFile.getFileName() + " to " + destFile.getFileName() + " the error was: " +
-                            e.getMessage());
-                }
-
-                final File templateProp = new File(tmpltPath + "/template.properties");
-                if (!templateProp.exists()) {
-                    templateProp.createNewFile();
-                }
-
-                String templateContent = "filename=" + cmd.getUniqueName() + ".qcow2" + System.getProperty("line.separator");
-
-                final DateFormat dateFormat = new SimpleDateFormat("MM_dd_yyyy");
-                final Date date = new Date();
-                templateContent += "snapshot.name=" + dateFormat.format(date) + System.getProperty("line.separator");
-
-                try(FileOutputStream templFo = new FileOutputStream(templateProp);) {
-                    templFo.write(templateContent.getBytes());
-                    templFo.flush();
-                }catch(final IOException ex)
-                {
-                    s_logger.error("CreatePrivateTemplateAnswer:Exception:"+ex.getMessage());
-                }
-
-            }
-
-            final Map<String, Object> params = new HashMap<String, Object>();
-            params.put(StorageLayer.InstanceConfigKey, _storage);
-            final Processor qcow2Processor = new QCOW2Processor();
-
-            qcow2Processor.configure("QCOW2 Processor", params);
-
-            final FormatInfo info = qcow2Processor.process(tmpltPath, null, cmd.getUniqueName());
-
-            final TemplateLocation loc = new TemplateLocation(_storage, tmpltPath);
-            loc.create(1, true, cmd.getUniqueName());
-            loc.addFormat(info);
-            loc.save();
-
-            return new CreatePrivateTemplateAnswer(cmd, true, null, templateInstallFolder + cmd.getUniqueName() + ".qcow2", info.virtualSize, info.size,
-                    cmd.getUniqueName(), ImageFormat.QCOW2);
-        } catch (final InternalErrorException e) {
-            return new CreatePrivateTemplateAnswer(cmd, false, e.toString());
-        } catch (final IOException e) {
-            return new CreatePrivateTemplateAnswer(cmd, false, e.toString());
-        } catch (final ConfigurationException e) {
-            return new CreatePrivateTemplateAnswer(cmd, false, e.toString());
-        } catch (final CloudRuntimeException e) {
-            return new CreatePrivateTemplateAnswer(cmd, false, e.toString());
-        } finally {
-            if (secondaryStorage != null) {
-                _storagePoolMgr.deleteStoragePool(secondaryStorage.getType(), secondaryStorage.getUuid());
-            }
-        }
-    }
-
     protected PowerState convertToPowerState(final DomainState ps) {
         final PowerState state = s_powerStatesTable.get(ps);
         return state == null ? PowerState.PowerUnknown : state;

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bcf78d3b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtBackupSnapshotCommandWrapper.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtBackupSnapshotCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtBackupSnapshotCommandWrapper.java
new file mode 100644
index 0000000..afbbf73
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtBackupSnapshotCommandWrapper.java
@@ -0,0 +1,204 @@
+//
+// 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.wrapper;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.MessageFormat;
+
+import org.apache.log4j.Logger;
+import org.libvirt.Connect;
+import org.libvirt.Domain;
+import org.libvirt.DomainInfo.DomainState;
+import org.libvirt.DomainSnapshot;
+import org.libvirt.LibvirtException;
+
+import com.ceph.rados.IoCTX;
+import com.ceph.rados.Rados;
+import com.ceph.rados.RadosException;
+import com.ceph.rbd.Rbd;
+import com.ceph.rbd.RbdException;
+import com.ceph.rbd.RbdImage;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.BackupSnapshotAnswer;
+import com.cloud.agent.api.BackupSnapshotCommand;
+import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.script.Script;
+
+public final class LibvirtBackupSnapshotCommandWrapper extends CommandWrapper<BackupSnapshotCommand, Answer, LibvirtComputingResource> {
+
+    private static final Logger s_logger = Logger.getLogger(LibvirtBackupSnapshotCommandWrapper.class);
+
+    @Override
+    public Answer execute(final BackupSnapshotCommand command, final LibvirtComputingResource libvirtComputingResource) {
+        final Long dcId = command.getDataCenterId();
+        final Long accountId = command.getAccountId();
+        final Long volumeId = command.getVolumeId();
+        final String secondaryStoragePoolUrl = command.getSecondaryStorageUrl();
+        final String snapshotName = command.getSnapshotName();
+        String snapshotDestPath = null;
+        String snapshotRelPath = null;
+        final String vmName = command.getVmName();
+        KVMStoragePool secondaryStoragePool = null;
+        final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();
+
+        try {
+            final LibvirtConnectionWrapper libvirtConnectionWrapper = libvirtComputingResource.getLibvirtConnectionWrapper();
+            final Connect conn = libvirtConnectionWrapper.getConnectionByVmName(vmName);
+
+            secondaryStoragePool = storagePoolMgr.getStoragePoolByURI(secondaryStoragePoolUrl);
+
+            final String ssPmountPath = secondaryStoragePool.getLocalPath();
+            snapshotRelPath = File.separator + "snapshots" + File.separator + dcId + File.separator + accountId + File.separator + volumeId;
+
+            snapshotDestPath = ssPmountPath + File.separator + "snapshots" + File.separator + dcId + File.separator + accountId + File.separator + volumeId;
+            final KVMStoragePool primaryPool = storagePoolMgr.getStoragePool(command.getPool().getType(), command.getPrimaryStoragePoolNameLabel());
+            final KVMPhysicalDisk snapshotDisk = primaryPool.getPhysicalDisk(command.getVolumePath());
+
+            final String manageSnapshotPath = libvirtComputingResource.manageSnapshotPath();
+            final int cmdsTimeout = libvirtComputingResource.getCmdsTimeout();
+
+            /**
+             * RBD snapshots can't be copied using qemu-img, so we have to use
+             * the Java bindings for librbd here.
+             *
+             * These bindings will read the snapshot and write the contents to
+             * the secondary storage directly
+             *
+             * It will stop doing so if the amount of time spend is longer then
+             * cmds.timeout
+             */
+            if (primaryPool.getType() == StoragePoolType.RBD) {
+                try {
+                    final Rados r = new Rados(primaryPool.getAuthUserName());
+                    r.confSet("mon_host", primaryPool.getSourceHost() + ":" + primaryPool.getSourcePort());
+                    r.confSet("key", primaryPool.getAuthSecret());
+                    r.confSet("client_mount_timeout", "30");
+                    r.connect();
+                    s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host"));
+
+                    final IoCTX io = r.ioCtxCreate(primaryPool.getSourceDir());
+                    final Rbd rbd = new Rbd(io);
+                    final RbdImage image = rbd.open(snapshotDisk.getName(), snapshotName);
+                    final File fh = new File(snapshotDestPath);
+                    try(BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fh));) {
+                        final int chunkSize = 4194304;
+                        long offset = 0;
+                        s_logger.debug("Backuping up RBD snapshot " + snapshotName + " to  " + snapshotDestPath);
+                        while (true) {
+                            final byte[] buf = new byte[chunkSize];
+                            final int bytes = image.read(offset, buf, chunkSize);
+                            if (bytes <= 0) {
+                                break;
+                            }
+                            bos.write(buf, 0, bytes);
+                            offset += bytes;
+                        }
+                        s_logger.debug("Completed backing up RBD snapshot " + snapshotName + " to  " + snapshotDestPath + ". Bytes written: " + offset);
+                    }catch(final IOException ex)
+                    {
+                        s_logger.error("BackupSnapshotAnswer:Exception:"+ ex.getMessage());
+                    }
+                    r.ioCtxDestroy(io);
+                } catch (final RadosException e) {
+                    s_logger.error("A RADOS operation failed. The error was: " + e.getMessage());
+                    return new BackupSnapshotAnswer(command, false, e.toString(), null, true);
+                } catch (final RbdException e) {
+                    s_logger.error("A RBD operation on " + snapshotDisk.getName() + " failed. The error was: " + e.getMessage());
+                    return new BackupSnapshotAnswer(command, false, e.toString(), null, true);
+                }
+            } else {
+                final Script scriptCommand = new Script(manageSnapshotPath, cmdsTimeout, s_logger);
+                scriptCommand.add("-b", snapshotDisk.getPath());
+                scriptCommand.add("-n", snapshotName);
+                scriptCommand.add("-p", snapshotDestPath);
+                scriptCommand.add("-t", snapshotName);
+                final String result = scriptCommand.execute();
+
+                if (result != null) {
+                    s_logger.debug("Failed to backup snaptshot: " + result);
+                    return new BackupSnapshotAnswer(command, false, result, null, true);
+                }
+            }
+            /* Delete the snapshot on primary */
+
+            DomainState state = null;
+            Domain vm = null;
+            if (vmName != null) {
+                try {
+                    vm = libvirtComputingResource.getDomain(conn, command.getVmName());
+                    state = vm.getInfo().state;
+                } catch (final LibvirtException e) {
+                    s_logger.trace("Ignoring libvirt error.", e);
+                }
+            }
+
+            final KVMStoragePool primaryStorage = storagePoolMgr.getStoragePool(command.getPool().getType(), command.getPool().getUuid());
+
+            if (state == DomainState.VIR_DOMAIN_RUNNING && !primaryStorage.isExternalSnapshot()) {
+                final MessageFormat snapshotXML = new MessageFormat("   <domainsnapshot>" + "       <name>{0}</name>" + "          <domain>"
+                        + "            <uuid>{1}</uuid>" + "        </domain>" + "    </domainsnapshot>");
+
+                final String vmUuid = vm.getUUIDString();
+                final Object[] args = new Object[] {snapshotName, vmUuid};
+                final String snapshot = snapshotXML.format(args);
+                s_logger.debug(snapshot);
+                final DomainSnapshot snap = vm.snapshotLookupByName(snapshotName);
+                snap.delete(0);
+
+                /*
+                 * libvirt on RHEL6 doesn't handle resume event emitted from
+                 * qemu
+                 */
+                vm = libvirtComputingResource.getDomain(conn, command.getVmName());
+                state = vm.getInfo().state;
+                if (state == DomainState.VIR_DOMAIN_PAUSED) {
+                    vm.resume();
+                }
+            } else {
+                final Script scriptCommand = new Script(manageSnapshotPath, cmdsTimeout, s_logger);
+                scriptCommand.add("-d", snapshotDisk.getPath());
+                scriptCommand.add("-n", snapshotName);
+                final String result = scriptCommand.execute();
+                if (result != null) {
+                    s_logger.debug("Failed to backup snapshot: " + result);
+                    return new BackupSnapshotAnswer(command, false, "Failed to backup snapshot: " + result, null, true);
+                }
+            }
+        } catch (final LibvirtException e) {
+            return new BackupSnapshotAnswer(command, false, e.toString(), null, true);
+        } catch (final CloudRuntimeException e) {
+            return new BackupSnapshotAnswer(command, false, e.toString(), null, true);
+        } finally {
+            if (secondaryStoragePool != null) {
+                storagePoolMgr.deleteStoragePool(secondaryStoragePool.getType(), secondaryStoragePool.getUuid());
+            }
+        }
+        return new BackupSnapshotAnswer(command, true, null, snapshotRelPath + File.separator + snapshotName, true);
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bcf78d3b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreatePrivateTemplateFromVolumeCommandWrapper.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreatePrivateTemplateFromVolumeCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreatePrivateTemplateFromVolumeCommandWrapper.java
new file mode 100644
index 0000000..f3bfe1f
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtCreatePrivateTemplateFromVolumeCommandWrapper.java
@@ -0,0 +1,176 @@
+//
+// 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.wrapper;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.naming.ConfigurationException;
+
+import org.apache.cloudstack.utils.qemu.QemuImg;
+import org.apache.cloudstack.utils.qemu.QemuImg.PhysicalDiskFormat;
+import org.apache.cloudstack.utils.qemu.QemuImgException;
+import org.apache.cloudstack.utils.qemu.QemuImgFile;
+import org.apache.log4j.Logger;
+
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
+import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer;
+import com.cloud.exception.InternalErrorException;
+import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePoolManager;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.storage.Storage.ImageFormat;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.storage.StorageLayer;
+import com.cloud.storage.template.Processor;
+import com.cloud.storage.template.Processor.FormatInfo;
+import com.cloud.storage.template.QCOW2Processor;
+import com.cloud.storage.template.TemplateLocation;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.utils.script.Script;
+
+public final class LibvirtCreatePrivateTemplateFromVolumeCommandWrapper extends CommandWrapper<CreatePrivateTemplateFromVolumeCommand, Answer, LibvirtComputingResource> {
+
+    private static final Logger s_logger = Logger.getLogger(LibvirtCreatePrivateTemplateFromVolumeCommandWrapper.class);
+
+    @Override
+    public Answer execute(final CreatePrivateTemplateFromVolumeCommand command, final LibvirtComputingResource libvirtComputingResource) {
+        final String secondaryStorageURL = command.getSecondaryStorageUrl();
+
+        KVMStoragePool secondaryStorage = null;
+        KVMStoragePool primary = null;
+        final KVMStoragePoolManager storagePoolMgr = libvirtComputingResource.getStoragePoolMgr();
+        try {
+            final String templateFolder = command.getAccountId() + File.separator + command.getTemplateId() + File.separator;
+            final String templateInstallFolder = "/template/tmpl/" + templateFolder;
+
+            secondaryStorage = storagePoolMgr.getStoragePoolByURI(secondaryStorageURL);
+
+            try {
+                primary = storagePoolMgr.getStoragePool(command.getPool().getType(), command.getPrimaryStoragePoolNameLabel());
+            } catch (final CloudRuntimeException e) {
+                if (e.getMessage().contains("not found")) {
+                    primary =
+                            storagePoolMgr.createStoragePool(command.getPool().getUuid(), command.getPool().getHost(), command.getPool().getPort(), command.getPool().getPath(),
+                                    command.getPool().getUserInfo(), command.getPool().getType());
+                } else {
+                    return new CreatePrivateTemplateAnswer(command, false, e.getMessage());
+                }
+            }
+
+            final KVMPhysicalDisk disk = primary.getPhysicalDisk(command.getVolumePath());
+            final String tmpltPath = secondaryStorage.getLocalPath() + File.separator + templateInstallFolder;
+            final StorageLayer storage = libvirtComputingResource.getStorage();
+            storage.mkdirs(tmpltPath);
+
+            if (primary.getType() != StoragePoolType.RBD) {
+                final String createTmplPath = libvirtComputingResource.createTmplPath();
+                final int cmdsTimeout = libvirtComputingResource.getCmdsTimeout();
+
+                final Script scriptCommand = new Script(createTmplPath, cmdsTimeout, s_logger);
+                scriptCommand.add("-f", disk.getPath());
+                scriptCommand.add("-t", tmpltPath);
+                scriptCommand.add("-n", command.getUniqueName() + ".qcow2");
+
+                final String result = scriptCommand.execute();
+
+                if (result != null) {
+                    s_logger.debug("failed to create template: " + result);
+                    return new CreatePrivateTemplateAnswer(command, false, result);
+                }
+            } else {
+                s_logger.debug("Converting RBD disk " + disk.getPath() + " into template " + command.getUniqueName());
+
+                final QemuImgFile srcFile =
+                        new QemuImgFile(KVMPhysicalDisk.RBDStringBuilder(primary.getSourceHost(), primary.getSourcePort(), primary.getAuthUserName(),
+                                primary.getAuthSecret(), disk.getPath()));
+                srcFile.setFormat(PhysicalDiskFormat.RAW);
+
+                final QemuImgFile destFile = new QemuImgFile(tmpltPath + "/" + command.getUniqueName() + ".qcow2");
+                destFile.setFormat(PhysicalDiskFormat.QCOW2);
+
+                final QemuImg q = new QemuImg(0);
+                try {
+                    q.convert(srcFile, destFile);
+                } catch (final QemuImgException e) {
+                    s_logger.error("Failed to create new template while converting " + srcFile.getFileName() + " to " + destFile.getFileName() + " the error was: " +
+                            e.getMessage());
+                }
+
+                final File templateProp = new File(tmpltPath + "/template.properties");
+                if (!templateProp.exists()) {
+                    templateProp.createNewFile();
+                }
+
+                String templateContent = "filename=" + command.getUniqueName() + ".qcow2" + System.getProperty("line.separator");
+
+                final DateFormat dateFormat = new SimpleDateFormat("MM_dd_yyyy");
+                final Date date = new Date();
+                templateContent += "snapshot.name=" + dateFormat.format(date) + System.getProperty("line.separator");
+
+                try(FileOutputStream templFo = new FileOutputStream(templateProp);) {
+                    templFo.write(templateContent.getBytes());
+                    templFo.flush();
+                }catch(final IOException ex)
+                {
+                    s_logger.error("CreatePrivateTemplateAnswer:Exception:"+ex.getMessage());
+                }
+
+            }
+
+            final Map<String, Object> params = new HashMap<String, Object>();
+            params.put(StorageLayer.InstanceConfigKey, storage);
+            final Processor qcow2Processor = new QCOW2Processor();
+
+            qcow2Processor.configure("QCOW2 Processor", params);
+
+            final FormatInfo info = qcow2Processor.process(tmpltPath, null, command.getUniqueName());
+
+            final TemplateLocation loc = new TemplateLocation(storage, tmpltPath);
+            loc.create(1, true, command.getUniqueName());
+            loc.addFormat(info);
+            loc.save();
+
+            return new CreatePrivateTemplateAnswer(command, true, null, templateInstallFolder + command.getUniqueName() + ".qcow2", info.virtualSize, info.size,
+                    command.getUniqueName(), ImageFormat.QCOW2);
+        } catch (final InternalErrorException e) {
+            return new CreatePrivateTemplateAnswer(command, false, e.toString());
+        } catch (final IOException e) {
+            return new CreatePrivateTemplateAnswer(command, false, e.toString());
+        } catch (final ConfigurationException e) {
+            return new CreatePrivateTemplateAnswer(command, false, e.toString());
+        } catch (final CloudRuntimeException e) {
+            return new CreatePrivateTemplateAnswer(command, false, e.toString());
+        } finally {
+            if (secondaryStorage != null) {
+                storagePoolMgr.deleteStoragePool(secondaryStorage.getType(), secondaryStorage.getUuid());
+            }
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bcf78d3b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtManageSnapshotCommandWrapper.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtManageSnapshotCommandWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtManageSnapshotCommandWrapper.java
new file mode 100644
index 0000000..9a991a1
--- /dev/null
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtManageSnapshotCommandWrapper.java
@@ -0,0 +1,163 @@
+//
+// 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.wrapper;
+
+import java.io.File;
+import java.text.MessageFormat;
+
+import org.apache.log4j.Logger;
+import org.libvirt.Connect;
+import org.libvirt.Domain;
+import org.libvirt.DomainInfo.DomainState;
+import org.libvirt.DomainSnapshot;
+import org.libvirt.LibvirtException;
+
+import com.ceph.rados.IoCTX;
+import com.ceph.rados.Rados;
+import com.ceph.rbd.Rbd;
+import com.ceph.rbd.RbdImage;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.ManageSnapshotAnswer;
+import com.cloud.agent.api.ManageSnapshotCommand;
+import com.cloud.hypervisor.kvm.resource.LibvirtComputingResource;
+import com.cloud.hypervisor.kvm.storage.KVMPhysicalDisk;
+import com.cloud.hypervisor.kvm.storage.KVMStoragePool;
+import com.cloud.resource.CommandWrapper;
+import com.cloud.storage.Storage.StoragePoolType;
+import com.cloud.utils.script.Script;
+
+public final class LibvirtManageSnapshotCommandWrapper extends CommandWrapper<ManageSnapshotCommand, Answer, LibvirtComputingResource> {
+
+    private static final Logger s_logger = Logger.getLogger(LibvirtManageSnapshotCommandWrapper.class);
+
+    @Override
+    public Answer execute(final ManageSnapshotCommand command, final LibvirtComputingResource libvirtComputingResource) {
+        final String snapshotName = command.getSnapshotName();
+        final String snapshotPath = command.getSnapshotPath();
+        final String vmName = command.getVmName();
+        try {
+            final LibvirtConnectionWrapper libvirtConnectionWrapper = libvirtComputingResource.getLibvirtConnectionWrapper();
+            final Connect conn = libvirtConnectionWrapper.getConnectionByVmName(vmName);
+            DomainState state = null;
+            Domain vm = null;
+            if (vmName != null) {
+                try {
+                    vm = libvirtComputingResource.getDomain(conn, command.getVmName());
+                    state = vm.getInfo().state;
+                } catch (final LibvirtException e) {
+                    s_logger.trace("Ignoring libvirt error.", e);
+                }
+            }
+
+            final KVMStoragePool primaryPool = libvirtComputingResource.getStoragePoolMgr().getStoragePool(command.getPool().getType(), command.getPool().getUuid());
+
+            final KVMPhysicalDisk disk = primaryPool.getPhysicalDisk(command.getVolumePath());
+            if (state == DomainState.VIR_DOMAIN_RUNNING && !primaryPool.isExternalSnapshot()) {
+
+                final MessageFormat snapshotXML = new MessageFormat("   <domainsnapshot>" + "       <name>{0}</name>" + "          <domain>"
+                        + "            <uuid>{1}</uuid>" + "        </domain>" + "    </domainsnapshot>");
+
+                final String vmUuid = vm.getUUIDString();
+                final Object[] args = new Object[] {snapshotName, vmUuid};
+                final String snapshot = snapshotXML.format(args);
+                s_logger.debug(snapshot);
+                if (command.getCommandSwitch().equalsIgnoreCase(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
+                    vm.snapshotCreateXML(snapshot);
+                } else {
+                    final DomainSnapshot snap = vm.snapshotLookupByName(snapshotName);
+                    snap.delete(0);
+                }
+
+                /*
+                 * libvirt on RHEL6 doesn't handle resume event emitted from
+                 * qemu
+                 */
+                vm = libvirtComputingResource.getDomain(conn, command.getVmName());
+                state = vm.getInfo().state;
+                if (state == DomainState.VIR_DOMAIN_PAUSED) {
+                    vm.resume();
+                }
+            } else {
+                /**
+                 * For RBD we can't use libvirt to do our snapshotting or any Bash scripts.
+                 * libvirt also wants to store the memory contents of the Virtual Machine,
+                 * but that's not possible with RBD since there is no way to store the memory
+                 * contents in RBD.
+                 *
+                 * So we rely on the Java bindings for RBD to create our snapshot
+                 *
+                 * This snapshot might not be 100% consistent due to writes still being in the
+                 * memory of the Virtual Machine, but if the VM runs a kernel which supports
+                 * barriers properly (>2.6.32) this won't be any different then pulling the power
+                 * cord out of a running machine.
+                 */
+                if (primaryPool.getType() == StoragePoolType.RBD) {
+                    try {
+                        final Rados r = new Rados(primaryPool.getAuthUserName());
+                        r.confSet("mon_host", primaryPool.getSourceHost() + ":" + primaryPool.getSourcePort());
+                        r.confSet("key", primaryPool.getAuthSecret());
+                        r.confSet("client_mount_timeout", "30");
+                        r.connect();
+                        s_logger.debug("Succesfully connected to Ceph cluster at " + r.confGet("mon_host"));
+
+                        final IoCTX io = r.ioCtxCreate(primaryPool.getSourceDir());
+                        final Rbd rbd = new Rbd(io);
+                        final RbdImage image = rbd.open(disk.getName());
+
+                        if (command.getCommandSwitch().equalsIgnoreCase(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
+                            s_logger.debug("Attempting to create RBD snapshot " + disk.getName() + "@" + snapshotName);
+                            image.snapCreate(snapshotName);
+                        } else {
+                            s_logger.debug("Attempting to remove RBD snapshot " + disk.getName() + "@" + snapshotName);
+                            image.snapRemove(snapshotName);
+                        }
+
+                        rbd.close(image);
+                        r.ioCtxDestroy(io);
+                    } catch (final Exception e) {
+                        s_logger.error("A RBD snapshot operation on " + disk.getName() + " failed. The error was: " + e.getMessage());
+                    }
+                } else {
+                    /* VM is not running, create a snapshot by ourself */
+                    final int cmdsTimeout = libvirtComputingResource.getCmdsTimeout();
+                    final String manageSnapshotPath = libvirtComputingResource.manageSnapshotPath();
+
+                    final Script scriptCommand = new Script(manageSnapshotPath, cmdsTimeout, s_logger);
+                    if (command.getCommandSwitch().equalsIgnoreCase(ManageSnapshotCommand.CREATE_SNAPSHOT)) {
+                        scriptCommand.add("-c", disk.getPath());
+                    } else {
+                        scriptCommand.add("-d", snapshotPath);
+                    }
+
+                    scriptCommand.add("-n", snapshotName);
+                    final String result = scriptCommand.execute();
+                    if (result != null) {
+                        s_logger.debug("Failed to manage snapshot: " + result);
+                        return new ManageSnapshotAnswer(command, false, "Failed to manage snapshot: " + result);
+                    }
+                }
+            }
+            return new ManageSnapshotAnswer(command, command.getSnapshotId(), disk.getPath() + File.separator + snapshotName, true, null);
+        } catch (final LibvirtException e) {
+            s_logger.debug("Failed to manage snapshot: " + e.toString());
+            return new ManageSnapshotAnswer(command, false, "Failed to manage snapshot: " + e.toString());
+        }
+    }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bcf78d3b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRequestWrapper.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRequestWrapper.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRequestWrapper.java
index 148075b..2656037 100644
--- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRequestWrapper.java
+++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRequestWrapper.java
@@ -23,12 +23,14 @@ import java.util.Hashtable;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.AttachIsoCommand;
 import com.cloud.agent.api.AttachVolumeCommand;
+import com.cloud.agent.api.BackupSnapshotCommand;
 import com.cloud.agent.api.CheckHealthCommand;
 import com.cloud.agent.api.CheckNetworkCommand;
 import com.cloud.agent.api.CheckOnHostCommand;
 import com.cloud.agent.api.CheckVirtualMachineCommand;
 import com.cloud.agent.api.CleanupNetworkRulesCmd;
 import com.cloud.agent.api.Command;
+import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
 import com.cloud.agent.api.CreateStoragePoolCommand;
 import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
 import com.cloud.agent.api.DeleteStoragePoolCommand;
@@ -39,6 +41,7 @@ import com.cloud.agent.api.GetVmDiskStatsCommand;
 import com.cloud.agent.api.GetVmStatsCommand;
 import com.cloud.agent.api.GetVncPortCommand;
 import com.cloud.agent.api.MaintainCommand;
+import com.cloud.agent.api.ManageSnapshotCommand;
 import com.cloud.agent.api.MigrateCommand;
 import com.cloud.agent.api.ModifySshKeysCommand;
 import com.cloud.agent.api.ModifyStoragePoolCommand;
@@ -136,6 +139,9 @@ public class LibvirtRequestWrapper extends RequestWrapper {
         linbvirtCommands.put(PlugNicCommand.class, new LibvirtPlugNicCommandWrapper());
         linbvirtCommands.put(UnPlugNicCommand.class, new LibvirtUnPlugNicCommandWrapper());
         linbvirtCommands.put(NetworkUsageCommand.class, new LibvirtNetworkUsageCommandWrapper());
+        linbvirtCommands.put(CreatePrivateTemplateFromVolumeCommand.class, new LibvirtCreatePrivateTemplateFromVolumeCommandWrapper());
+        linbvirtCommands.put(ManageSnapshotCommand.class, new LibvirtManageSnapshotCommandWrapper());
+        linbvirtCommands.put(BackupSnapshotCommand.class, new LibvirtBackupSnapshotCommandWrapper());
 
         resources.put(LibvirtComputingResource.class, linbvirtCommands);
     }

http://git-wip-us.apache.org/repos/asf/cloudstack/blob/bcf78d3b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
index 9b0be08..b0c2734 100644
--- a/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
+++ b/plugins/hypervisors/kvm/test/com/cloud/hypervisor/kvm/resource/LibvirtComputingResourceTest.java
@@ -66,11 +66,13 @@ import org.xml.sax.SAXException;
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.AttachIsoCommand;
 import com.cloud.agent.api.AttachVolumeCommand;
+import com.cloud.agent.api.BackupSnapshotCommand;
 import com.cloud.agent.api.CheckHealthCommand;
 import com.cloud.agent.api.CheckNetworkCommand;
 import com.cloud.agent.api.CheckOnHostCommand;
 import com.cloud.agent.api.CheckVirtualMachineCommand;
 import com.cloud.agent.api.CleanupNetworkRulesCmd;
+import com.cloud.agent.api.CreatePrivateTemplateFromVolumeCommand;
 import com.cloud.agent.api.CreateStoragePoolCommand;
 import com.cloud.agent.api.CreateVolumeFromSnapshotCommand;
 import com.cloud.agent.api.DeleteStoragePoolCommand;
@@ -81,6 +83,7 @@ import com.cloud.agent.api.GetVmDiskStatsCommand;
 import com.cloud.agent.api.GetVmStatsCommand;
 import com.cloud.agent.api.GetVncPortCommand;
 import com.cloud.agent.api.MaintainCommand;
+import com.cloud.agent.api.ManageSnapshotCommand;
 import com.cloud.agent.api.MigrateCommand;
 import com.cloud.agent.api.ModifySshKeysCommand;
 import com.cloud.agent.api.ModifyStoragePoolCommand;
@@ -3313,7 +3316,6 @@ public class LibvirtComputingResourceTest {
         final Answer answer = wrapper.execute(command, libvirtComputingResource);
         assertTrue(answer.getResult());
 
-        //Being called twice, although I did not find the second place yet.
         verify(libvirtComputingResource, times(1)).networkUsage(command.getPrivateIP(), "create", null);
     }
 
@@ -3337,7 +3339,6 @@ public class LibvirtComputingResourceTest {
         final Answer answer = wrapper.execute(command, libvirtComputingResource);
         assertTrue(answer.getResult());
 
-        //Being called twice, although I did not find the second place yet.
         verify(libvirtComputingResource, times(1)).configureVPCNetworkUsage(command.getPrivateIP(), command.getGatewayIP(), "create", command.getVpcCIDR());
     }
 
@@ -3360,7 +3361,6 @@ public class LibvirtComputingResourceTest {
         final Answer answer = wrapper.execute(command, libvirtComputingResource);
         assertTrue(answer.getResult());
 
-        //Being called twice, although I did not find the second place yet.
         verify(libvirtComputingResource, times(1)).getVPCNetworkStats(command.getPrivateIP(), command.getGatewayIP(), command.getOption());
     }
 
@@ -3383,7 +3383,6 @@ public class LibvirtComputingResourceTest {
         final Answer answer = wrapper.execute(command, libvirtComputingResource);
         assertTrue(answer.getResult());
 
-        //Being called twice, although I did not find the second place yet.
         verify(libvirtComputingResource, times(1)).getVPCNetworkStats(command.getPrivateIP(), command.getGatewayIP(), command.getOption());
     }
 
@@ -3406,7 +3405,135 @@ public class LibvirtComputingResourceTest {
         final Answer answer = wrapper.execute(command, libvirtComputingResource);
         assertTrue(answer.getResult());
 
-        //Being called twice, although I did not find the second place yet.
         verify(libvirtComputingResource, times(1)).configureVPCNetworkUsage(command.getPrivateIP(), command.getGatewayIP(), command.getOption(), command.getVpcCIDR());
     }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testCreatePrivateTemplateFromVolumeCommand() {
+        //Simple test used to make sure the flow (LibvirtComputingResource => Request => CommandWrapper) is working.
+        //The code is way to big and complex. Will finish the refactor and come back to this to add more cases.
+
+        final StoragePool pool = Mockito.mock(StoragePool.class);;
+        final String secondaryStorageUrl = "nfs:/192.168.2.2/storage/secondary";
+        final long templateId = 1l;
+        final long accountId = 1l;
+        final String userSpecifiedName = "User";
+        final String uniqueName = "Unique";
+        final String volumePath = "/123/vol";
+        final String vmName = "Test";
+        final int wait = 0;
+
+        final CreatePrivateTemplateFromVolumeCommand command = new CreatePrivateTemplateFromVolumeCommand(pool, secondaryStorageUrl, templateId, accountId, userSpecifiedName, uniqueName, volumePath, vmName, wait);
+
+        final KVMStoragePoolManager storagePoolMgr = Mockito.mock(KVMStoragePoolManager.class);
+        final KVMStoragePool secondaryStorage = Mockito.mock(KVMStoragePool.class);
+        //final KVMStoragePool primary = Mockito.mock(KVMStoragePool.class);
+
+        when(libvirtComputingResource.getStoragePoolMgr()).thenReturn(storagePoolMgr);
+        when(storagePoolMgr.getStoragePoolByURI(secondaryStorageUrl)).thenReturn(secondaryStorage);
+        when(storagePoolMgr.getStoragePool(command.getPool().getType(), command.getPrimaryStoragePoolNameLabel())).thenThrow(new CloudRuntimeException("error"));
+
+        final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Answer answer = wrapper.execute(command, libvirtComputingResource);
+        assertFalse(answer.getResult());
+
+        verify(libvirtComputingResource, times(1)).getStoragePoolMgr();
+        verify(storagePoolMgr, times(1)).getStoragePoolByURI(secondaryStorageUrl);
+        verify(storagePoolMgr, times(1)).getStoragePool(command.getPool().getType(), command.getPrimaryStoragePoolNameLabel());
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testManageSnapshotCommandLibvirtException() {
+        //Simple test used to make sure the flow (LibvirtComputingResource => Request => CommandWrapper) is working.
+        //The code is way to big and complex. Will finish the refactor and come back to this to add more cases.
+
+        final StoragePool pool = Mockito.mock(StoragePool.class);;
+        final String volumePath = "/123/vol";
+        final String vmName = "Test";
+
+        final long snapshotId = 1l;
+        final String preSnapshotPath = "/snapshot/path";
+        final String snapshotName = "snap";
+
+        final ManageSnapshotCommand command = new ManageSnapshotCommand(snapshotId, volumePath, pool, preSnapshotPath, snapshotName, vmName);
+
+        final LibvirtConnectionWrapper libvirtConnectionWrapper = Mockito.mock(LibvirtConnectionWrapper.class);
+        //final Connect conn = Mockito.mock(Connect.class);
+
+        when(libvirtComputingResource.getLibvirtConnectionWrapper()).thenReturn(libvirtConnectionWrapper);
+
+        try {
+            when(libvirtConnectionWrapper.getConnectionByVmName(command.getVmName())).thenThrow(LibvirtException.class);
+        } catch (final LibvirtException e) {
+            fail(e.getMessage());
+        }
+
+        final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Answer answer = wrapper.execute(command, libvirtComputingResource);
+        assertFalse(answer.getResult());
+
+        verify(libvirtComputingResource, times(1)).getLibvirtConnectionWrapper();
+        try {
+            verify(libvirtConnectionWrapper, times(1)).getConnectionByVmName(command.getVmName());
+        } catch (final LibvirtException e) {
+            fail(e.getMessage());
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testBackupSnapshotCommandLibvirtException() {
+        //Simple test used to make sure the flow (LibvirtComputingResource => Request => CommandWrapper) is working.
+        //The code is way to big and complex. Will finish the refactor and come back to this to add more cases.
+
+        final StoragePool pool = Mockito.mock(StoragePool.class);;
+        final String secondaryStorageUrl = "nfs:/192.168.2.2/storage/secondary";
+        final long accountId = 1l;
+        final String volumePath = "/123/vol";
+        final String vmName = "Test";
+        final int wait = 0;
+
+        final long snapshotId = 1l;
+        final String snapshotName = "snap";
+
+        final Long dcId = 1l;
+        final Long volumeId = 1l;
+        final Long secHostId = 1l;
+        final String snapshotUuid = "9a0afe7c-26a7-4585-bf87-abf82ae106d9";
+        final String prevBackupUuid = "003a0cc2-2e04-417a-bee0-534ef1724561";
+        final boolean isVolumeInactive = false;
+        final String prevSnapshotUuid = "1791efae-f22d-474b-87c6-92547d6c5877";
+
+        final BackupSnapshotCommand command = new BackupSnapshotCommand(secondaryStorageUrl, dcId, accountId, volumeId, snapshotId, secHostId, volumePath, pool, snapshotUuid, snapshotName, prevSnapshotUuid, prevBackupUuid, isVolumeInactive, vmName, wait);
+
+        final LibvirtConnectionWrapper libvirtConnectionWrapper = Mockito.mock(LibvirtConnectionWrapper.class);
+        //final Connect conn = Mockito.mock(Connect.class);
+
+        when(libvirtComputingResource.getLibvirtConnectionWrapper()).thenReturn(libvirtConnectionWrapper);
+
+        try {
+            when(libvirtConnectionWrapper.getConnectionByVmName(command.getVmName())).thenThrow(LibvirtException.class);
+        } catch (final LibvirtException e) {
+            fail(e.getMessage());
+        }
+
+        final LibvirtRequestWrapper wrapper = LibvirtRequestWrapper.getInstance();
+        assertNotNull(wrapper);
+
+        final Answer answer = wrapper.execute(command, libvirtComputingResource);
+        assertFalse(answer.getResult());
+
+        verify(libvirtComputingResource, times(1)).getLibvirtConnectionWrapper();
+        try {
+            verify(libvirtConnectionWrapper, times(1)).getConnectionByVmName(command.getVmName());
+        } catch (final LibvirtException e) {
+            fail(e.getMessage());
+        }
+    }
 }
\ No newline at end of file