From 263216caa5b548099451f669fae918ed63608fd7 Mon Sep 17 00:00:00 2001 From: Edison Su Date: Fri, 10 Aug 2012 13:32:21 -0700 Subject: [PATCH] Adds CLVM as an option for primary storage. It copies most of the RBD bits, in most cases simply adding an 'else if' or similar. In the managesnapshot.sh we add the ability to manipulate snapshots for the CLVM volumes, as well as a few minor fixes like adjusting the usage for the -b option to reflect that it accepts four arguments (note that it already accepted four args, it just wasn't reflected in the usage). RB: https://reviews.apache.org/r/6470/ Send-by: shadowsor@gmail.com --- api/src/com/cloud/storage/Storage.java | 1 + .../resource/LibvirtComputingResource.java | 18 ++- .../kvm/storage/KVMStoragePoolManager.java | 4 + .../kvm/storage/LibvirtStorageAdaptor.java | 16 ++- .../kvm/storage/LibvirtStoragePool.java | 6 +- scripts/storage/qcow2/managesnapshot.sh | 136 +++++++++++++++--- .../com/cloud/storage/StorageManagerImpl.java | 4 +- ui/scripts/system.js | 1 + ui/scripts/zoneWizard.js | 1 + 9 files changed, 154 insertions(+), 33 deletions(-) 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") {