bug 6772: use vhd-util to coalesce snapshots into primary storage

status 6772: resolved fixed
This commit is contained in:
anthony 2011-03-07 19:28:25 -08:00
parent b8e03aca4c
commit 0c2a639a2d
7 changed files with 217 additions and 91 deletions

View File

@ -1,5 +1,4 @@
/**
* Copyright (C) 2010 Cloud.com, Inc. All rights reserved.
*
* This software is licensed under the GNU General Public License v3 or later.
*
@ -2069,6 +2068,25 @@ public abstract class CitrixResourceBase implements ServerResource {
}
}
String createTemplateFromSnapshot(Connection conn, String templatePath, String snapshotPath) {
String results = callHostPluginAsync(conn, "vmopspremium", "create_privatetemplate_from_snapshot",
2 * 60 * 60 * 1000, "templatePath", templatePath, "snapshotPath", snapshotPath);
if (results == null || results.isEmpty()) {
String msg = "create_privatetemplate_from_snapshot return null";
s_logger.warn(msg);
throw new CloudRuntimeException(msg);
}
String[] tmp = results.split("#");
String status = tmp[0];
if (status.equals("0")) {
return results;
} else {
s_logger.warn(results);
throw new CloudRuntimeException(results);
}
}
String copy_vhd_from_secondarystorage(Connection conn, String mountpoint, String sruuid) {
String results = callHostPluginAsync(conn, "vmopspremium", "copy_vhd_from_secondarystorage",
2 * 60 * 60 * 1000, "mountpoint", mountpoint, "sruuid", sruuid);
@ -4812,7 +4830,7 @@ public abstract class CitrixResourceBase implements ServerResource {
}
} else {
try {
String volumePath = mountpoint + volumeUUID + ".vhd";
String volumePath = mountpoint + "/" + volumeUUID + ".vhd";
String uuid = copy_vhd_from_secondarystorage(conn, volumePath, srUuid);
return new CopyVolumeAnswer(cmd, true, null, srUuid, uuid);
} finally {
@ -5122,6 +5140,7 @@ public abstract class CitrixResourceBase implements ServerResource {
s_logger.warn(details);
return new CreatePrivateTemplateAnswer(cmd, false, details);
}
VDI volume = getVDIbyUuid(conn, volumeUUID);
// create template SR
URI tmpltURI = new URI(secondaryStoragePoolURL + "/" + installPath);
@ -5134,13 +5153,13 @@ public abstract class CitrixResourceBase implements ServerResource {
tmpltVDI.setNameLabel(conn, userSpecifiedName);
}
String tmpltSrUUID = tmpltSR.getUuid(conn);
String tmpltUUID = tmpltVDI.getUuid(conn);
String tmpltFilename = tmpltUUID + ".vhd";
long virtualSize = tmpltVDI.getVirtualSize(conn);
long physicalSize = tmpltVDI.getPhysicalUtilisation(conn);
// create the template.properties file
result = postCreatePrivateTemplate(conn, tmpltSrUUID, tmpltFilename, tmpltUUID, userSpecifiedName, null, physicalSize, virtualSize, templateId);
String templatePath = secondaryStorageMountPath + "/" + installPath;
result = postCreatePrivateTemplate(conn, templatePath, tmpltFilename, tmpltUUID, userSpecifiedName, null, physicalSize, virtualSize, templateId);
if (!result) {
throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir: " + tmpltURI);
}
@ -5182,34 +5201,23 @@ public abstract class CitrixResourceBase implements ServerResource {
s_logger.warn(details);
return new CreatePrivateTemplateAnswer(cmd, false, details);
}
String templatePath = secondaryStorageMountPath + "/" + installPath;
// create snapshot SR
URI snapshotURI = new URI(secondaryStoragePoolURL + "/snapshots/" + accountId + "/" + volumeId );
snapshotSR = createNfsSRbyURI(conn, snapshotURI, false);
snapshotSR.scan(conn);
VDI snapshotVDI = getVDIbyUuid(conn, backedUpSnapshotUuid);
// create template SR
URI tmpltURI = new URI(secondaryStoragePoolURL + "/" + installPath);
tmpltSR = createNfsSRbyURI(conn, tmpltURI, false);
// copy snapshotVDI to template SR
VDI tmpltVDI = cloudVDIcopy(conn, snapshotVDI, tmpltSR);
String tmpltSrUUID = tmpltSR.getUuid(conn);
String tmpltUUID = tmpltVDI.getUuid(conn);
String tmpltFilename = tmpltUUID + ".vhd";
long virtualSize = tmpltVDI.getVirtualSize(conn);
long physicalSize = tmpltVDI.getPhysicalUtilisation(conn);
String snapshotPath = secondaryStorageMountPath + "/snapshots/" + accountId + "/" + volumeId + "/" + backedUpSnapshotUuid + ".vhd";
String results = createTemplateFromSnapshot(conn, templatePath, snapshotPath);
String[] tmp = results.split("#");
String tmpltUuid = tmp[1];
long physicalSize = Long.parseLong(tmp[2]);
long virtualSize = Long.parseLong(tmp[3]) * 1024 * 1024;
String tmpltFilename = tmpltUuid + ".vhd";
// create the template.properties file
result = postCreatePrivateTemplate(conn, tmpltSrUUID, tmpltFilename, tmpltUUID, userSpecifiedName, null, physicalSize, virtualSize, newTemplateId);
result = postCreatePrivateTemplate(conn, templatePath, tmpltFilename, tmpltUuid, userSpecifiedName, null, physicalSize, virtualSize, newTemplateId);
if (!result) {
throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir: " + tmpltURI);
throw new CloudRuntimeException("Could not create the template.properties file on secondary storage dir: " + templatePath);
}
installPath = installPath + "/" + tmpltFilename;
return new CreatePrivateTemplateAnswer(cmd, true, null, installPath, virtualSize, physicalSize, tmpltUUID, ImageFormat.VHD);
} catch (XenAPIException e) {
details = "Creating template from snapshot " + backedUpSnapshotUuid + " failed due to " + e.getMessage();
s_logger.error(details, e);
return new CreatePrivateTemplateAnswer(cmd, true, null, installPath, virtualSize, physicalSize, tmpltUuid, ImageFormat.VHD);
} catch (Exception e) {
details = "Creating template from snapshot " + backedUpSnapshotUuid + " failed due to " + e.getMessage();
s_logger.error(details, e);
@ -5339,8 +5347,6 @@ public abstract class CitrixResourceBase implements ServerResource {
String secondaryStoragePoolURL = cmd.getSecondaryStoragePoolURL();
String backedUpSnapshotUuid = cmd.getSnapshotUuid();
// By default, assume the command has failed and set the params to be
// passed to CreateVolumeFromSnapshotAnswer appropriately
boolean result = false;
// Generic error message.
String details = null;
@ -5359,18 +5365,10 @@ public abstract class CitrixResourceBase implements ServerResource {
}
// Get the absolute path of the snapshot on the secondary storage.
URI snapshotURI = new URI(secondaryStoragePoolURL + "/snapshots/" + accountId + "/" + volumeId );
snapshotSR = createNfsSRbyURI(conn, snapshotURI, false);
snapshotSR.scan(conn);
VDI snapshotVDI = getVDIbyUuid(conn, backedUpSnapshotUuid);
VDI volumeVDI = cloudVDIcopy(conn, snapshotVDI, primaryStorageSR);
volumeUUID = volumeVDI.getUuid(conn);
String snapshotPath = snapshotURI.getHost() + ":" + snapshotURI.getPath() + "/" + backedUpSnapshotUuid + ".vhd";
String srUuid = primaryStorageSR.getUuid(conn);
volumeUUID = copy_vhd_from_secondarystorage(conn, snapshotPath, srUuid);
result = true;
} catch (XenAPIException e) {
details += " due to " + e.toString();
s_logger.warn(details, e);
@ -5640,6 +5638,7 @@ public abstract class CitrixResourceBase implements ServerResource {
return new ConsoleProxyLoadAnswer(cmd, proxyVmId, proxyVmName, success, result);
}
protected boolean createSecondaryStorageFolder(Connection conn, String remoteMountPath, String newFolder) {
String result = callHostPlugin(conn, "vmopsSnapshot", "create_secondary_storage_folder", "remoteMountPath", remoteMountPath, "newFolder", newFolder);
return (result != null);
@ -5650,7 +5649,7 @@ public abstract class CitrixResourceBase implements ServerResource {
return (result != null);
}
protected boolean postCreatePrivateTemplate(Connection conn, String tmpltSrUUID,String tmpltFilename, String templateName, String templateDescription, String checksum, long size, long virtualSize, long templateId) {
protected boolean postCreatePrivateTemplate(Connection conn, String templatePath, String tmpltFilename, String templateName, String templateDescription, String checksum, long size, long virtualSize, long templateId) {
if (templateDescription == null) {
templateDescription = "";
@ -5660,7 +5659,7 @@ public abstract class CitrixResourceBase implements ServerResource {
checksum = "";
}
String result = callHostPluginWithTimeOut(conn, "vmopsSnapshot", "post_create_private_template", 110*60, "tmpltSrUUID", tmpltSrUUID, "templateFilename", tmpltFilename, "templateName", templateName, "templateDescription", templateDescription,
String result = callHostPluginWithTimeOut(conn, "vmopsSnapshot", "post_create_private_template", 110*60, "templatePath", templatePath, "templateFilename", tmpltFilename, "templateName", templateName, "templateDescription", templateDescription,
"checksum", checksum, "size", String.valueOf(size), "virtualSize", String.valueOf(virtualSize), "templateId", String.valueOf(templateId));
boolean success = false;

View File

@ -0,0 +1,112 @@
#!/bin/bash
#set -x
usage() {
printf "Usage: %s [vhd file in secondary storage] [template directory in secondary storage] \n" $(basename $0)
}
cleanup()
{
if [ ! -z $snapshotdir ]; then
umount $snapshotdir
if [ $? -eq 0 ]; then
rm $snapshotdir -rf
fi
fi
if [ ! -z $templatedir ]; then
umount $templatedir
if [ $? -eq 0 ]; then
rm $templatedir -rf
fi
fi
}
if [ -z $1 ]; then
usage
echo "2#no vhd file path"
exit 0
else
snapshoturl=${1%/*}
vhdfilename=${1##*/}
fi
if [ -z $2 ]; then
usage
echo "3#no template path"
exit 0
else
templateurl=$2
fi
snapshotdir=/var/run/cloud_mount/$(uuidgen -r)
mkdir -p $snapshotdir
if [ $? -ne 0 ]; then
echo "4#cann't make dir $snapshotdir"
exit 0
fi
mount $snapshoturl $snapshotdir
if [ $? -ne 0 ]; then
rm -rf $snapshotdir
echo "5#can not mount $snapshoturl to $snapshotdir"
exit 0
fi
templatedir=/var/run/cloud_mount/$(uuidgen -r)
mkdir -p $templatedir
if [ $? -ne 0 ]; then
templatedir=""
cleanup
echo "6#cann't make dir $templatedir"
exit 0
fi
mount $templateurl $templatedir
if [ $? -ne 0 ]; then
rm -rf $templatedir
templatedir=""
cleanup
echo "7#can not mount $templateurl to $templatedir"
exit 0
fi
VHDUTIL="/opt/xensource/bin/vhd-util"
copyvhd()
{
local desvhd=$1
local srcvhd=$2
local parent=`$VHDUTIL query -p -n $srcvhd`
if [ $? -ne 0 ]; then
echo "30#failed to query $srcvhd"
cleanup
exit 0
fi
if [ "${parent##*vhd has}" = " no parent" ]; then
dd if=$srcvhd of=$desvhd bs=2M
if [ $? -ne 0 ]; then
echo "31#failed to dd $srcvhd to $desvhd"
cleanup
exit 0
fi
else
copyvhd $desvhd $parent
$VHDUTIL coalesce -p $desvhd -n $srcvhd
if [ $? -ne 0 ]; then
echo "32#failed to coalesce $desvhd to $srcvhd"
cleanup
exit 0
fi
fi
}
templateuuid=$(uuidgen -r)
desvhd=$templatedir/$templateuuid.vhd
srcvhd=$snapshotdir/$vhdfilename
copyvhd $desvhd $srcvhd
virtualSize=`$VHDUTIL query -v -n $desvhd`
physicalSize=`ls -l $desvhd | awk '{print $5}'`
cleanup
echo "0#$templateuuid#$physicalSize#$virtualSize"
exit 0

View File

@ -19,6 +19,7 @@ import random
VHD_UTIL = '/usr/sbin/vhd-util'
VHD_PREFIX = 'VHD-'
CLOUD_DIR = '/var/run/cloud_mount'
def echo(fn):
def wrapped(*v, **k):
@ -40,7 +41,7 @@ def create_secondary_storage_folder(session, args):
try:
# Mount the remote resource folder locally
remote_mount_path = args["remoteMountPath"]
local_mount_path = os.path.join(SR.MOUNT_BASE, "mount" + str(int(random.random() * 1000000)))
local_mount_path = os.path.join(CLOUD_DIR, util.gen_uuid())
mount(remote_mount_path, local_mount_path)
# Create the new folder
@ -76,7 +77,7 @@ def delete_secondary_storage_folder(session, args):
try:
# Mount the remote resource folder locally
remote_mount_path = args["remoteMountPath"]
local_mount_path = os.path.join(SR.MOUNT_BASE, "mount" + str(int(random.random() * 1000000)))
local_mount_path = os.path.join(CLOUD_DIR, util.gen_uuid())
mount(remote_mount_path, local_mount_path)
# Delete the specified folder
@ -104,53 +105,55 @@ def delete_secondary_storage_folder(session, args):
def post_create_private_template(session, args):
local_mount_path = None
try:
# get local template folder
sruuid = args["tmpltSrUUID"]
local_mount_path = os.path.join(SR.MOUNT_BASE, sruuid)
# Retrieve args
filename = args["templateFilename"]
name = args["templateName"]
description = args["templateDescription"]
checksum = args["checksum"]
size = args["size"]
virtual_size = args["virtualSize"]
template_id = args["templateId"]
# Determine the template size
template_install_path = local_mount_path + "/" + filename
file_size = os.path.getsize(template_install_path)
util.SMlog("Got template file_size: " + str(file_size))
# Create the template.properties file
template_properties_install_path = local_mount_path + "/template.properties"
f = open(template_properties_install_path, "w")
f.write("filename=" + filename + "\n")
f.write("vhd=true\n")
f.write("id=" + template_id + "\n")
f.write("vhd.filename=" + filename + "\n")
f.write("public=false\n")
f.write("uniquename=" + name + "\n")
f.write("vhd.virtualsize=" + virtual_size + "\n")
f.write("virtualsize=" + virtual_size + "\n")
f.write("checksum=" + checksum + "\n")
f.write("hvm=true\n")
f.write("description=" + description + "\n")
f.write("vhd.size=" + str(file_size) + "\n")
f.write("size=" + str(file_size) + "\n")
f.close()
util.SMlog("Created template.properties file")
try:
# get local template folder
templatePath = args["templatePath"]
local_mount_path = os.path.join(CLOUD_DIR, util.gen_uuid())
mount(templatePath, local_mount_path)
# Retrieve args
filename = args["templateFilename"]
name = args["templateName"]
description = args["templateDescription"]
checksum = args["checksum"]
file_size = args["size"]
virtual_size = args["virtualSize"]
template_id = args["templateId"]
# Set permissions
permissions = stat.S_IREAD | stat.S_IWRITE | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH
os.chmod(template_properties_install_path, permissions)
util.SMlog("Set permissions on template and template.properties")
# Create the template.properties file
template_properties_install_path = local_mount_path + "/template.properties"
f = open(template_properties_install_path, "w")
f.write("filename=" + filename + "\n")
f.write("vhd=true\n")
f.write("id=" + template_id + "\n")
f.write("vhd.filename=" + filename + "\n")
f.write("public=false\n")
f.write("uniquename=" + name + "\n")
f.write("vhd.virtualsize=" + virtual_size + "\n")
f.write("virtualsize=" + virtual_size + "\n")
f.write("checksum=" + checksum + "\n")
f.write("hvm=true\n")
f.write("description=" + description + "\n")
f.write("vhd.size=" + str(file_size) + "\n")
f.write("size=" + str(file_size) + "\n")
f.close()
util.SMlog("Created template.properties file")
# Set permissions
permissions = stat.S_IREAD | stat.S_IWRITE | stat.S_IRGRP | stat.S_IWGRP | stat.S_IROTH | stat.S_IWOTH
os.chmod(template_properties_install_path, permissions)
util.SMlog("Set permissions on template and template.properties")
except:
errMsg = "post_create_private_template failed."
util.SMlog(errMsg)
raise xs_errors.XenError(errMsg)
except:
errMsg = "post_create_private_template failed."
util.SMlog(errMsg)
raise xs_errors.XenError(errMsg)
finally:
if local_mount_path != None:
# Unmount the local folder
umount(local_mount_path)
# Remove the local folder
os.system("rm -rf " + local_mount_path)
return "1"
def isfile(path, isISCSI):
@ -306,7 +309,6 @@ def umount(localDir):
def mountSnapshotsDir(secondaryStorageMountPath, relativeDir, dcId, accountId, instanceId):
# The aim is to mount secondaryStorageMountPath on
# SR.MOUNT_BASE/<dcId>/<relativeDir>
# And create <accountId>/<instanceId> dir on it, if it doesn't exist already.
# Assuming that secondaryStorageMountPath exists remotely
@ -316,7 +318,7 @@ def mountSnapshotsDir(secondaryStorageMountPath, relativeDir, dcId, accountId, i
snapshotsDir = os.path.join(secondaryStorageMountPath, relativeDir)
# Mkdir local mount point dir, if it doesn't exist.
localMountPointPath = os.path.join(SR.MOUNT_BASE, dcId)
localMountPointPath = os.path.join(CLOUD_DIR, dcId)
localMountPointPath = os.path.join(localMountPointPath, relativeDir)
makedirs(localMountPointPath)
@ -339,7 +341,7 @@ def mountSnapshotsDir(secondaryStorageMountPath, relativeDir, dcId, accountId, i
@echo
def unmountSnapshotsDir(session, args):
dcId = args['dcId']
localMountPointPath = os.path.join(SR.MOUNT_BASE, dcId)
localMountPointPath = os.path.join(CLOUD_DIR, dcId)
localMountPointPath = os.path.join(localMountPointPath, "snapshots")
try:
umount(localMountPointPath)

View File

@ -17,6 +17,17 @@ def echo(fn):
return res
return wrapped
@echo
def create_privatetemplate_from_snapshot(session, args):
templatePath = args['templatePath']
snapshotPath = args['snapshotPath']
try:
cmd = ["bash", "/opt/xensource/bin/create_privatetemplate_from_snapshot.sh",snapshotPath, templatePath]
txt = util.pread2(cmd)
except:
txt = '10#failed'
return txt
@echo
def copy_vhd_to_secondarystorage(session, args):
mountpoint = args['mountpoint']
@ -86,5 +97,5 @@ def heartbeat(session, args):
return txt
if __name__ == "__main__":
XenAPIPlugin.dispatch({"copy_vhd_to_secondarystorage":copy_vhd_to_secondarystorage, "copy_vhd_from_secondarystorage":copy_vhd_from_secondarystorage, "setup_heartbeat_sr":setup_heartbeat_sr, "setup_heartbeat_file":setup_heartbeat_file, "check_heartbeat":check_heartbeat, "heartbeat": heartbeat})
XenAPIPlugin.dispatch({"create_privatetemplate_from_snapshot":create_privatetemplate_from_snapshot, "copy_vhd_to_secondarystorage":copy_vhd_to_secondarystorage, "copy_vhd_from_secondarystorage":copy_vhd_from_secondarystorage, "setup_heartbeat_sr":setup_heartbeat_sr, "setup_heartbeat_file":setup_heartbeat_file, "check_heartbeat":check_heartbeat, "heartbeat": heartbeat})

View File

@ -41,3 +41,4 @@ vhd-util=..,0755,/opt/xensource/bin
vmopspremium=..,0755,/etc/xapi.d/plugins
InterfaceReconfigure.py=.,0755,/opt/xensource/bin
fsimage.so=..,0755,/usr/lib/fs/ext2fs-lib
create_privatetemplate_from_snapshot.sh=..,0755,/opt/xensource/bin

View File

@ -41,3 +41,4 @@ xenheartbeat.sh=..,0755,/opt/xensource/bin
launch_hb.sh=..,0755,/opt/xensource/bin
vhd-util=..,0755,/opt/xensource/bin
vmopspremium=..,0755,/etc/xapi.d/plugins
create_privatetemplate_from_snapshot.sh=..,0755,/opt/xensource/bin