diff --git a/api/src/com/cloud/storage/Storage.java b/api/src/com/cloud/storage/Storage.java index d6bd04ea61e..fba12b62d3d 100755 --- a/api/src/com/cloud/storage/Storage.java +++ b/api/src/com/cloud/storage/Storage.java @@ -96,6 +96,7 @@ public class Storage { Iscsi(true), // for e.g., ZFS Comstar ISO(false), // for iso image LVM(false), // XenServer local LVM SR + CLVM(true), RBD(true), SharedMountPoint(true), VMFS(true), // VMware VMFS storage 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 4a22e73bf6b..f2fe65a619b 100755 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/resource/LibvirtComputingResource.java @@ -2640,13 +2640,19 @@ public class LibvirtComputingResource extends ServerResourceBase implements disk.defNetworkBasedDisk(physicalDisk.getPath().replace("rbd:", ""), pool.getSourceHost(), pool.getSourcePort(), pool.getAuthUserName(), pool.getUuid(), devId, diskBusType, diskProtocol.RBD); - } else if (volume.getType() == Volume.Type.DATADISK) { - disk.defFileBasedDisk(physicalDisk.getPath(), devId, - DiskDef.diskBus.VIRTIO, - DiskDef.diskFmtType.QCOW2); + } else if (pool.getType() == StoragePoolType.CLVM) { + disk.defBlockBasedDisk(physicalDisk.getPath(), devId, + diskBusType); } else { - disk.defFileBasedDisk(physicalDisk.getPath(), devId, - diskBusType, DiskDef.diskFmtType.QCOW2); + if (volume.getType() == Volume.Type.DATADISK) { + disk.defFileBasedDisk(physicalDisk.getPath(), devId, + DiskDef.diskBus.VIRTIO, + DiskDef.diskFmtType.QCOW2); + } else { + disk.defFileBasedDisk(physicalDisk.getPath(), devId, + diskBusType, DiskDef.diskFmtType.QCOW2); + } + } } diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java index 491f7724ea3..751da837cf4 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/KVMStoragePoolManager.java @@ -78,6 +78,10 @@ public class KVMStoragePoolManager { if (destPool.getType() == StoragePoolType.RBD) { return this._storageAdaptor.createDiskFromTemplate(template, name, KVMPhysicalDisk.PhysicalDiskFormat.RAW, template.getSize(), destPool); + } else if (destPool.getType() == StoragePoolType.CLVM) { + return this._storageAdaptor.createDiskFromTemplate(template, name, + KVMPhysicalDisk.PhysicalDiskFormat.RAW, template.getSize(), + destPool); } else { return this._storageAdaptor.createDiskFromTemplate(template, name, KVMPhysicalDisk.PhysicalDiskFormat.QCOW2, diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java index 85c64ba06ac..9f62ee8514d 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStorageAdaptor.java @@ -462,6 +462,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { type = StoragePoolType.Filesystem; } else if (spd.getPoolType() == LibvirtStoragePoolDef.poolType.RBD) { type = StoragePoolType.RBD; + } else if (spd.getPoolType() == LibvirtStoragePoolDef.poolType.LOGICAL) { + type = StoragePoolType.CLVM; } LibvirtStoragePool pool = new LibvirtStoragePool(uuid, storage.getName(), @@ -551,6 +553,8 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { sp = CreateSharedStoragePool(conn, name, host, path); } else if (type == StoragePoolType.RBD) { sp = createRBDStoragePool(conn, name, host, port, userInfo, path); + } else if (type == StoragePoolType.CLVM) { + sp = createCLVMStoragePool(conn, name, host, path); } } @@ -685,9 +689,15 @@ public class LibvirtStorageAdaptor implements StorageAdaptor { if (destPool.getType() != StoragePoolType.RBD) { disk = destPool.createPhysicalDisk(newUuid, format, template.getVirtualSize()); - Script.runSimpleBashScript("qemu-img create -f " - + template.getFormat() + " -b " + template.getPath() + " " - + disk.getPath()); + if (format == PhysicalDiskFormat.QCOW2) { + Script.runSimpleBashScript("qemu-img create -f " + + template.getFormat() + " -b " + template.getPath() + " " + + disk.getPath()); + } else if (format == PhysicalDiskFormat.RAW) { + Script.runSimpleBashScript("qemu-img convert -f " + + template.getFormat() + " -O raw " + template.getPath() + + " " + disk.getPath()); + } } else { disk = new KVMPhysicalDisk(destPool.getSourceDir() + "/" + newUuid, newUuid, destPool); disk.setFormat(format); diff --git a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java index bc428e1273c..32f8ce99d9c 100644 --- a/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java +++ b/plugins/hypervisors/kvm/src/com/cloud/hypervisor/kvm/storage/LibvirtStoragePool.java @@ -88,7 +88,11 @@ public class LibvirtStoragePool implements KVMStoragePool { @Override public PhysicalDiskFormat getDefaultFormat() { - return PhysicalDiskFormat.QCOW2; + if (getStoragePoolType() == StoragePoolType.CLVM) { + return PhysicalDiskFormat.RAW; + } else { + return PhysicalDiskFormat.QCOW2; + } } @Override diff --git a/scripts/storage/qcow2/managesnapshot.sh b/scripts/storage/qcow2/managesnapshot.sh index a305ba83a98..29b7081290d 100755 --- a/scripts/storage/qcow2/managesnapshot.sh +++ b/scripts/storage/qcow2/managesnapshot.sh @@ -6,9 +6,9 @@ # 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 @@ -16,16 +16,16 @@ # specific language governing permissions and limitations # under the License. - + # $Id: managesnapshot.sh 11601 2010-08-11 17:26:15Z kris $ $HeadURL: svn://svn.lab.vmops.com/repos/branches/2.1.refactor/java/scripts/storage/qcow2/managesnapshot.sh $ -# managesnapshot.sh -- manage snapshots for a single disk (create, destroy, rollback) +# managesnapshot.sh -- manage snapshots for a single disk (create, destroy, rollback, backup) usage() { printf "Usage: %s: -c -n \n" $(basename $0) >&2 printf "Usage: %s: -d -n \n" $(basename $0) >&2 printf "Usage: %s: -r -n \n" $(basename $0) >&2 - printf "Usage: %s: -b -n -p \n" $(basename $0) >&2 + printf "Usage: %s: -b -n -p -t \n" $(basename $0) >&2 exit 2 } @@ -40,21 +40,69 @@ then fi fi +is_lv() { + # Must be a block device + if [ -b "${1}" ]; then + # But not a volume group or physical volume + lvm vgs "${1}" > /dev/null 2>&1 && return 1 + # And a logical volume + lvm lvs "${1}" > /dev/null 2>&1 && return 0 + fi + return 1 +} + +get_vg() { + lvm lvs --noheadings --unbuffered --separator=/ "${1}" | cut -d '/' -f 2 +} + +get_lv() { + lvm lvs --noheadings --unbuffered --separator=/ "${1}" | cut -d '/' -f 1 +} + +double_hyphens() { + echo ${1} | sed -e "s/-/--/g" +} + create_snapshot() { local disk=$1 local snapshotname="$2" local failed=0 - if [ -f "${disk}" ]; then + if [ ${dmsnapshot} = "yes" ] && is_lv ${disk}; then + local lv=`get_lv ${disk}` + local vg=`get_vg ${disk}` + local lv_dm=`double_hyphens ${lv}` + local vg_dm=`double_hyphens ${vg}` + local lvdevice=/dev/mapper/${vg_dm}-${lv_dm} + local lv_bytes=`blockdev --getsize64 ${lvdevice}` + local lv_sectors=`blockdev --getsz ${lvdevice}` + + lvm lvcreate --size ${lv_bytes}b --name "${snapshotname}-cow" ${vg} >&2 || return 2 + dmsetup suspend ${vg_dm}-${lv_dm} >&2 + if dmsetup info -c --noheadings -o name ${vg_dm}-${lv_dm}-real > /dev/null 2>&1; then + echo "0 ${lv_sectors} snapshot ${lvdevice}-real /dev/mapper/${vg_dm}-${snapshotname}--cow p 64" | \ + dmsetup create "${vg_dm}-${snapshotname}" >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) + dmsetup resume "${vg_dm}-${snapshotname}" >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) + else + dmsetup table ${vg_dm}-${lv_dm} | dmsetup create ${vg_dm}-${lv_dm}-real >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) + dmsetup resume ${vg_dm}-${lv_dm}-real >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) + echo "0 ${lv_sectors} snapshot ${lvdevice}-real /dev/mapper/${vg_dm}-${snapshotname}--cow p 64" | \ + dmsetup create "${vg_dm}-${snapshotname}" >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) + echo "0 ${lv_sectors} snapshot-origin ${lvdevice}-real" | \ + dmsetup load ${vg_dm}-${lv_dm} >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) + dmsetup resume "${vg_dm}-${snapshotname}" >&2 || ( destroy_snapshot ${disk} "${snapshotname}"; return 2 ) + fi + dmsetup resume "${vg_dm}-${lv_dm}" >&2 + elif [ -f "${disk}" ]; then $qemu_img snapshot -c "$snapshotname" $disk - + if [ $? -gt 0 ] then failed=2 printf "***Failed to create snapshot $snapshotname for path $disk\n" >&2 $qemu_img snapshot -d "$snapshotname" $disk - + if [ $? -gt 0 ] then printf "***Failed to delete snapshot $snapshotname for path $disk\n" >&2 @@ -65,26 +113,46 @@ create_snapshot() { printf "***Failed to create snapshot $snapshotname, undefined type $disk\n" >&2 fi - return $failed + return $failed } destroy_snapshot() { local disk=$1 - local snapshotname=$2 + local snapshotname="$2" local failed=0 - if [ -f $disk ]; then + if is_lv ${disk}; then + local lv=`get_lv ${disk}` + local vg=`get_vg ${disk}` + local lv_dm=`double_hyphens ${lv}` + local vg_dm=`double_hyphens ${vg}` + if [ -e /dev/mapper/${vg_dm}-${lv_dm}-real ]; then + local dm_refcount=`dmsetup info -c --noheadings -o open ${vg_dm}-${lv_dm}-real` + if [ ${dm_refcount} -le 2 ]; then + dmsetup suspend ${vg_dm}-${lv_dm} >&2 + dmsetup table ${vg_dm}-${lv_dm}-real | dmsetup load ${vg_dm}-${lv_dm} >&2 + dmsetup resume ${vg_dm}-${lv_dm} + dmsetup remove "${vg_dm}-${snapshotname}" + dmsetup remove ${vg_dm}-${lv_dm}-real + else + dmsetup remove "${vg_dm}-${snapshotname}" + fi + else + dmsetup remove "${vg_dm}-${snapshotname}" + fi + lvm lvremove -f "${vg}/${snapshotname}-cow" + elif [ -f $disk ]; then $qemu_img snapshot -d "$snapshotname" $disk if [ $? -gt 0 ] then failed=2 printf "Failed to delete snapshot $snapshotname for path $disk\n" >&2 - fi + fi else failed=3 printf "***Failed to delete snapshot $snapshotname, undefined type $disk\n" >&2 fi - return $failed + return $failed } rollback_snapshot() { @@ -93,18 +161,19 @@ rollback_snapshot() { local failed=0 $qemu_img snapshot -a $snapshotname $disk - + if [ $? -gt 0 ] then printf "***Failed to apply snapshot $snapshotname for path $disk\n" >&2 failed=1 fi - - return $failed + + return $failed } + backup_snapshot() { local disk=$1 - local snapshotname=$2 + local snapshotname="$2" local destPath=$3 local destName=$4 @@ -113,24 +182,37 @@ backup_snapshot() { mkdir -p $destPath >& /dev/null if [ $? -gt 0 ] then - printf "Failed to create $destPath" >&2 + printf "Failed to create $destPath\n" >&2 return 3 fi fi - if [ -f ${disk} ]; then - # Does the snapshot exist? + if [ ${dmsnapshot} = "yes" ] && is_lv ${disk}; then + local vg=`get_vg ${disk}` + local vg_dm=`double_hyphens ${vg}` + local scriptdir=`dirname ${0}` + + if ! dmsetup info -c --noheadings -o name ${vg_dm}-${snapshotname} > /dev/null 2>&1; then + printf "Disk ${disk} has no snapshot called ${snapshotname}.\n" >&2 + return 1 + fi + + ${qemu_img} convert -f raw -O qcow2 "/dev/mapper/${vg_dm}-${snapshotname}" "${destPath}/${destName}" || \ + ( printf "${qemu_img} failed to create backup of snapshot ${snapshotname} for disk ${disk} to ${destPath}.\n" >&2; return 2 ) + + elif [ -f ${disk} ]; then + # Does the snapshot exist? $qemu_img snapshot -l $disk|grep -w "$snapshotname" >& /dev/null if [ $? -gt 0 ] then - printf "there is no $snapshotname on disk $disk" >&2 + printf "there is no $snapshotname on disk $disk\n" >&2 return 1 fi $qemu_img convert -f qcow2 -O qcow2 -s $snapshotname $disk $destPath/$destName >& /dev/null if [ $? -gt 0 ] then - printf "Failed to backup $snapshotname for disk $disk to $destPath" >&2 + printf "Failed to backup $snapshotname for disk $disk to $destPath\n" >&2 return 2 fi else @@ -150,6 +232,8 @@ pathval= snapshot= tmplName= deleteDir= +dmsnapshot=no +dmrollback=no while getopts 'c:d:r:n:b:p:t:f' OPTION do @@ -180,6 +264,13 @@ do esac done +if modprobe dm-snapshot; then + dmsnapshot=yes + dmsetup targets | grep -q "^snapshot-merge" && dmrollback=yes +fi + +[ -z "${snapshot}" ] && usage + [ -b "$pathval" ] && snapshot=`echo "${snapshot}" | md5sum -t | awk '{ print $1 }'` if [ "$cflag" == "1" ] @@ -192,6 +283,7 @@ then exit $? elif [ "$bflag" == "1" ] then + [ -z "${destPath}" -o -z "${tmplName}" ] && usage backup_snapshot $pathval $snapshot $destPath $tmplName exit $? elif [ "$rflag" == "1" ] diff --git a/server/src/com/cloud/storage/StorageManagerImpl.java b/server/src/com/cloud/storage/StorageManagerImpl.java index c7dda00b71d..c17379f48bb 100755 --- a/server/src/com/cloud/storage/StorageManagerImpl.java +++ b/server/src/com/cloud/storage/StorageManagerImpl.java @@ -1325,6 +1325,8 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag pool = new StoragePoolVO(StoragePoolType.Filesystem, "localhost", 0, hostPath); } else if (scheme.equalsIgnoreCase("sharedMountPoint")) { pool = new StoragePoolVO(StoragePoolType.SharedMountPoint, storageHost, 0, hostPath); + } else if (scheme.equalsIgnoreCase("clvm")) { + pool = new StoragePoolVO(StoragePoolType.CLVM, storageHost, 0, hostPath.replaceFirst("/", "")); } else if (scheme.equalsIgnoreCase("rbd")) { if (port == -1) { port = 6789; @@ -1628,7 +1630,7 @@ public class StorageManagerImpl implements StorageManager, Manager, ClusterManag s_logger.debug("creating pool " + pool.getName() + " on host " + hostId); if (pool.getPoolType() != StoragePoolType.NetworkFilesystem && pool.getPoolType() != StoragePoolType.Filesystem && pool.getPoolType() != StoragePoolType.IscsiLUN && pool.getPoolType() != StoragePoolType.Iscsi && pool.getPoolType() != StoragePoolType.VMFS && pool.getPoolType() != StoragePoolType.SharedMountPoint - && pool.getPoolType() != StoragePoolType.PreSetup && pool.getPoolType() != StoragePoolType.OCFS2 && pool.getPoolType() != StoragePoolType.RBD) { + && pool.getPoolType() != StoragePoolType.PreSetup && pool.getPoolType() != StoragePoolType.OCFS2 && pool.getPoolType() != StoragePoolType.RBD && pool.getPoolType() != StoragePoolType.CLVM) { s_logger.warn(" Doesn't support storage pool type " + pool.getPoolType()); return false; } diff --git a/ui/scripts/system.js b/ui/scripts/system.js index 9d9936c752e..3db9289bc24 100644 --- a/ui/scripts/system.js +++ b/ui/scripts/system.js @@ -7510,6 +7510,7 @@ items.push({id: "nfs", description: "nfs"}); items.push({id: "SharedMountPoint", description: "SharedMountPoint"}); items.push({id: "rbd", description: "RBD"}); + items.push({id: "clvm", description: "CLVM"}); args.response.success({data: items}); } else if(selectedClusterObj.hypervisortype == "XenServer") { diff --git a/ui/scripts/zoneWizard.js b/ui/scripts/zoneWizard.js index 261668fdaac..f598a2bed94 100644 --- a/ui/scripts/zoneWizard.js +++ b/ui/scripts/zoneWizard.js @@ -966,6 +966,7 @@ var items = []; items.push({id: "nfs", description: "nfs"}); items.push({id: "SharedMountPoint", description: "SharedMountPoint"}); + items.push({id: "clvm", description: "CLVM"}); args.response.success({data: items}); } else if(selectedClusterObj.hypervisortype == "XenServer") {