http://git-wip-us.apache.org/repos/asf/cloudstack/blob/f84fc12d/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot
----------------------------------------------------------------------
diff --git a/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot
b/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot
new file mode 100644
index 0000000..53f31a9
--- /dev/null
+++ b/scripts/vm/hypervisor/xenserver/xcposs/vmopsSnapshot
@@ -0,0 +1,601 @@
+#!/usr/bin/python
+# 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.
+
+# Version @VERSION@
+#
+# A plugin for executing script needed by vmops cloud
+
+import os, sys, time
+import XenAPIPlugin
+sys.path.append("/usr/lib/xcp/sm/")
+import SR, VDI, SRCommand, util, lvutil
+from util import CommandException
+import vhdutil
+import shutil
+import lvhdutil
+import errno
+import subprocess
+import xs_errors
+import cleanup
+import stat
+import random
+
+VHD_UTIL = 'vhd-util'
+VHD_PREFIX = 'VHD-'
+CLOUD_DIR = '/run/cloud_mount'
+
+def echo(fn):
+ def wrapped(*v, **k):
+ name = fn.__name__
+ util.SMlog("#### VMOPS enter %s ####" % name )
+ res = fn(*v, **k)
+ util.SMlog("#### VMOPS exit %s ####" % name )
+ return res
+ return wrapped
+
+
+@echo
+def create_secondary_storage_folder(session, args):
+ local_mount_path = None
+
+ util.SMlog("create_secondary_storage_folder, args: " + str(args))
+
+ try:
+ try:
+ # Mount the remote resource folder locally
+ remote_mount_path = args["remoteMountPath"]
+ local_mount_path = os.path.join(CLOUD_DIR, util.gen_uuid())
+ mount(remote_mount_path, local_mount_path)
+
+ # Create the new folder
+ new_folder = local_mount_path + "/" + args["newFolder"]
+ if not os.path.isdir(new_folder):
+ current_umask = os.umask(0)
+ os.makedirs(new_folder)
+ os.umask(current_umask)
+ except OSError, (errno, strerror):
+ errMsg = "create_secondary_storage_folder failed: errno: " +
str(errno) + ", strerr: " + strerror
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ except:
+ errMsg = "create_secondary_storage_folder 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("rmdir " + local_mount_path)
+
+ return "1"
+
+@echo
+def delete_secondary_storage_folder(session, args):
+ local_mount_path = None
+
+ util.SMlog("delete_secondary_storage_folder, args: " + str(args))
+
+ try:
+ try:
+ # Mount the remote resource folder locally
+ remote_mount_path = args["remoteMountPath"]
+ local_mount_path = os.path.join(CLOUD_DIR, util.gen_uuid())
+ mount(remote_mount_path, local_mount_path)
+
+ # Delete the specified folder
+ folder = local_mount_path + "/" + args["folder"]
+ if os.path.isdir(folder):
+ os.system("rm -f " + folder + "/*")
+ os.system("rmdir " + folder)
+ except OSError, (errno, strerror):
+ errMsg = "delete_secondary_storage_folder failed: errno: " +
str(errno) + ", strerr: " + strerror
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ except:
+ errMsg = "delete_secondary_storage_folder 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("rmdir " + local_mount_path)
+
+ return "1"
+
+@echo
+def post_create_private_template(session, args):
+ local_mount_path = None
+ try:
+ 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"]
+
+ # 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)
+
+ finally:
+ if local_mount_path != None:
+ # Unmount the local folder
+ umount(local_mount_path)
+ # Remove the local folder
+ os.system("rmdir " + local_mount_path)
+ return "1"
+
+def isfile(path, isISCSI):
+ errMsg = ''
+ exists = True
+ if isISCSI:
+ exists = checkVolumeAvailablility(path)
+ else:
+ exists = os.path.isfile(path)
+
+ if not exists:
+ errMsg = "File " + path + " does not exist."
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ return errMsg
+
+def copyfile(fromFile, toFile, isISCSI):
+ util.SMlog("Starting to copy " + fromFile + " to " + toFile)
+ errMsg = ''
+ try:
+ cmd = ['dd', 'if=' + fromFile, 'of=' + toFile, 'bs=4M']
+ txt = util.pread2(cmd)
+ except:
+ try:
+ os.system("rm -f " + toFile)
+ except:
+ txt = ''
+ txt = ''
+ errMsg = "Error while copying " + fromFile + " to " + toFile + " in
secondary storage"
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+
+ util.SMlog("Successfully copied " + fromFile + " to " + toFile)
+ return errMsg
+
+def chdir(path):
+ try:
+ os.chdir(path)
+ except OSError, (errno, strerror):
+ errMsg = "Unable to chdir to " + path + " because of OSError with
errno: " + str(errno) + " and strerr: " + strerror
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ util.SMlog("Chdired to " + path)
+ return
+
+def scanParent(path):
+ # Do a scan for the parent for ISCSI volumes
+ # Note that the parent need not be visible on the XenServer
+ parentUUID = ''
+ try:
+ lvName = os.path.basename(path)
+ dirname = os.path.dirname(path)
+ vgName = os.path.basename(dirname)
+ vhdInfo = vhdutil.getVHDInfoLVM(lvName, lvhdutil.extractUuid, vgName)
+ parentUUID = vhdInfo.parentUuid
+ except:
+ errMsg = "Could not get vhd parent of " + path
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ return parentUUID
+
+def getParent(path, isISCSI):
+ parentUUID = ''
+ try :
+ if isISCSI:
+ parentUUID = vhdutil.getParent(path, lvhdutil.extractUuid)
+ else:
+ parentUUID = vhdutil.getParent(path, cleanup.FileVDI.extractUuid)
+ except:
+ errMsg = "Could not get vhd parent of " + path
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ return parentUUID
+
+def getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI):
+ snapshotVHD = getVHD(snapshotUuid, isISCSI)
+ snapshotPath = os.path.join(primarySRPath, snapshotVHD)
+
+ baseCopyUuid = ''
+ if isISCSI:
+ checkVolumeAvailablility(snapshotPath)
+ baseCopyUuid = scanParent(snapshotPath)
+ else:
+ baseCopyUuid = getParent(snapshotPath, isISCSI)
+
+ util.SMlog("Base copy of snapshotUuid: " + snapshotUuid + " is " +
baseCopyUuid)
+ return baseCopyUuid
+
+def setParent(parent, child):
+ try:
+ cmd = [VHD_UTIL, "modify", "-p", parent, "-n", child]
+ txt = util.pread2(cmd)
+ except:
+ errMsg = "Unexpected error while trying to set parent of " + child + "
to " + parent
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ util.SMlog("Successfully set parent of " + child + " to " + parent)
+ return
+
+def rename(originalVHD, newVHD):
+ try:
+ os.rename(originalVHD, newVHD)
+ except OSError, (errno, strerror):
+ errMsg = "OSError while renaming " + origiinalVHD + " to " + newVHD +
"with errno: " + str(errno) + " and strerr: " + strerror
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ return
+
+def makedirs(path):
+ if not os.path.isdir(path):
+ try:
+ os.makedirs(path)
+ except OSError, (errno, strerror):
+ umount(path)
+ if os.path.isdir(path):
+ return
+ errMsg = "OSError while creating " + path + " with errno: " +
str(errno) + " and strerr: " + strerror
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ return
+
+def mount(remoteDir, localDir):
+ makedirs(localDir)
+ options = "soft,tcp,timeo=133,retrans=1"
+ try:
+ cmd = ['mount', '-o', options, remoteDir, localDir]
+ txt = util.pread2(cmd)
+ except:
+ txt = ''
+ errMsg = "Unexpected error while trying to mount " + remoteDir + " to
" + localDir
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ util.SMlog("Successfully mounted " + remoteDir + " to " + localDir)
+
+ return
+
+def umount(localDir):
+ try:
+ cmd = ['umount', localDir]
+ util.pread2(cmd)
+ except CommandException:
+ errMsg = "CommandException raised while trying to umount " + localDir
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+
+ util.SMlog("Successfully unmounted " + localDir)
+ return
+
+def mountSnapshotsDir(secondaryStorageMountPath, localMountPointPath, path):
+ # The aim is to mount secondaryStorageMountPath on
+ # And create <accountId>/<instanceId> dir on it, if it doesn't exist
already.
+ # Assuming that secondaryStorageMountPath exists remotely
+
+ # Just mount secondaryStorageMountPath/<relativeDir>/SecondaryStorageHost/
everytime
+ # Never unmount.
+ # path is like "snapshots/account/volumeId", we mount
secondary_storage:/snapshots
+ relativeDir = path.split("/")[0]
+ restDir = "/".join(path.split("/")[1:])
+ snapshotsDir = os.path.join(secondaryStorageMountPath, relativeDir)
+
+ makedirs(localMountPointPath)
+ # if something is not mounted already on localMountPointPath,
+ # mount secondaryStorageMountPath on localMountPath
+ if os.path.ismount(localMountPointPath):
+ # There is more than one secondary storage per zone.
+ # And we are mounting each sec storage under a zone-specific directory
+ # So two secondary storage snapshot dirs will never get mounted on the
same point on the same XenServer.
+ util.SMlog("The remote snapshots directory has already been mounted on
" + localMountPointPath)
+ else:
+ mount(snapshotsDir, localMountPointPath)
+
+ # Create accountId/instanceId dir on localMountPointPath, if it doesn't
exist
+ backupsDir = os.path.join(localMountPointPath, restDir)
+ makedirs(backupsDir)
+ return backupsDir
+
+def unmountAll(path):
+ try:
+ for dir in os.listdir(path):
+ if dir.isdigit():
+ util.SMlog("Unmounting Sub-Directory: " + dir)
+ localMountPointPath = os.path.join(path, dir)
+ umount(localMountPointPath)
+ except:
+ util.SMlog("Ignoring the error while trying to unmount the snapshots
dir")
+
+@echo
+def unmountSnapshotsDir(session, args):
+ dcId = args['dcId']
+ localMountPointPath = os.path.join(CLOUD_DIR, dcId)
+ localMountPointPath = os.path.join(localMountPointPath, "snapshots")
+ unmountAll(localMountPointPath)
+ try:
+ umount(localMountPointPath)
+ except:
+ util.SMlog("Ignoring the error while trying to unmount the snapshots
dir.")
+
+ return "1"
+
+def getPrimarySRPath(session, primaryStorageSRUuid, isISCSI):
+ sr = session.xenapi.SR.get_by_uuid(primaryStorageSRUuid)
+ srrec = session.xenapi.SR.get_record(sr)
+ srtype = srrec["type"]
+ if srtype == "file":
+ pbd = session.xenapi.SR.get_PBDs(sr)[0]
+ pbdrec = session.xenapi.PBD.get_record(pbd)
+ primarySRPath = pbdrec["device_config"]["location"]
+ return primarySRPath
+ elif isISCSI:
+ primarySRDir = lvhdutil.VG_PREFIX + primaryStorageSRUuid
+ return os.path.join(lvhdutil.VG_LOCATION, primarySRDir)
+ else:
+ return os.path.join(SR.MOUNT_BASE, primaryStorageSRUuid)
+
+def getBackupVHD(UUID):
+ return UUID + '.' + SR.DEFAULT_TAP
+
+def getVHD(UUID, isISCSI):
+ if isISCSI:
+ return VHD_PREFIX + UUID
+ else:
+ return UUID + '.' + SR.DEFAULT_TAP
+
+def getIsTrueString(stringValue):
+ booleanValue = False
+ if (stringValue and stringValue == 'true'):
+ booleanValue = True
+ return booleanValue
+
+def makeUnavailable(uuid, primarySRPath, isISCSI):
+ if not isISCSI:
+ return
+ VHD = getVHD(uuid, isISCSI)
+ path = os.path.join(primarySRPath, VHD)
+ manageAvailability(path, '-an')
+ return
+
+def manageAvailability(path, value):
+ if path.__contains__("/var/run/sr-mount"):
+ return
+ util.SMlog("Setting availability of " + path + " to " + value)
+ try:
+ cmd = ['/usr/sbin/lvchange', value, path]
+ util.pread2(cmd)
+ except: #CommandException, (rc, cmdListStr, stderr):
+ #errMsg = "CommandException thrown while executing: " + cmdListStr + "
with return code: " + str(rc) + " and stderr: " + stderr
+ errMsg = "Unexpected exception thrown by lvchange"
+ util.SMlog(errMsg)
+ if value == "-ay":
+ # Raise an error only if we are trying to make it available.
+ # Just warn if we are trying to make it unavailable after the
+ # snapshot operation is done.
+ raise xs_errors.XenError(errMsg)
+ return
+
+
+def checkVolumeAvailablility(path):
+ try:
+ if not isVolumeAvailable(path):
+ # The VHD file is not available on XenSever. The volume is probably
+ # inactive or detached.
+ # Do lvchange -ay to make it available on XenServer
+ manageAvailability(path, '-ay')
+ except:
+ errMsg = "Could not determine status of ISCSI path: " + path
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+
+ success = False
+ i = 0
+ while i < 6:
+ i = i + 1
+ # Check if the vhd is actually visible by checking for the link
+ # set isISCSI to true
+ success = isVolumeAvailable(path)
+ if success:
+ util.SMlog("Made vhd: " + path + " available and confirmed that it
is visible")
+ break
+
+ # Sleep for 10 seconds before checking again.
+ time.sleep(10)
+
+ # If not visible within 1 min fail
+ if not success:
+ util.SMlog("Could not make vhd: " + path + " available despite
waiting for 1 minute. Does it exist?")
+
+ return success
+
+def isVolumeAvailable(path):
+ # Check if iscsi volume is available on this XenServer.
+ status = "0"
+ try:
+ p = subprocess.Popen(["/bin/bash", "-c", "if [ -L " + path + " ]; then
echo 1; else echo 0;fi"], stdout=subprocess.PIPE)
+ status = p.communicate()[0].strip("\n")
+ except:
+ errMsg = "Could not determine status of ISCSI path: " + path
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+
+ return (status == "1")
+
+def getVhdParent(session, args):
+ util.SMlog("getParent with " + str(args))
+ primaryStorageSRUuid = args['primaryStorageSRUuid']
+ snapshotUuid = args['snapshotUuid']
+ isISCSI = getIsTrueString(args['isISCSI'])
+
+ primarySRPath = getPrimarySRPath(session, primaryStorageSRUuid, isISCSI)
+ util.SMlog("primarySRPath: " + primarySRPath)
+
+ baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI)
+
+ return baseCopyUuid
+
+
+def backupSnapshot(session, args):
+ util.SMlog("Called backupSnapshot with " + str(args))
+ primaryStorageSRUuid = args['primaryStorageSRUuid']
+ secondaryStorageMountPath = args['secondaryStorageMountPath']
+ snapshotUuid = args['snapshotUuid']
+ prevBackupUuid = args['prevBackupUuid']
+ backupUuid = args['backupUuid']
+ isISCSI = getIsTrueString(args['isISCSI'])
+ path = args['path']
+ localMountPoint = args['localMountPoint']
+ primarySRPath = getPrimarySRPath(session, primaryStorageSRUuid, isISCSI)
+ util.SMlog("primarySRPath: " + primarySRPath)
+
+ baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI)
+ baseCopyVHD = getVHD(baseCopyUuid, isISCSI)
+ baseCopyPath = os.path.join(primarySRPath, baseCopyVHD)
+ util.SMlog("Base copy path: " + baseCopyPath)
+
+
+ # Mount secondary storage mount path on XenServer along the path
+ # /var/run/sr-mount/<dcId>/snapshots/ and create <accountId>/<volumeId> dir
+ # on it.
+ backupsDir = mountSnapshotsDir(secondaryStorageMountPath, localMountPoint,
path)
+ util.SMlog("Backups dir " + backupsDir)
+ prevBackupUuid = prevBackupUuid.split("/")[-1]
+ # Check existence of snapshot on primary storage
+ isfile(baseCopyPath, isISCSI)
+ if prevBackupUuid:
+ # Check existence of prevBackupFile
+ prevBackupVHD = getBackupVHD(prevBackupUuid)
+ prevBackupFile = os.path.join(backupsDir, prevBackupVHD)
+ isfile(prevBackupFile, False)
+
+ # copy baseCopyPath to backupsDir with new uuid
+ backupVHD = getBackupVHD(backupUuid)
+ backupFile = os.path.join(backupsDir, backupVHD)
+ util.SMlog("Back up " + baseCopyUuid + " to Secondary Storage as " +
backupUuid)
+ copyfile(baseCopyPath, backupFile, isISCSI)
+ vhdutil.setHidden(backupFile, False)
+
+ # Because the primary storage is always scanned, the parent of this base
copy is always the first base copy.
+ # We don't want that, we want a chain of VHDs each of which is a delta
from the previous.
+ # So set the parent of the current baseCopyVHD to prevBackupVHD
+ if prevBackupUuid:
+ # If there was a previous snapshot
+ setParent(prevBackupFile, backupFile)
+
+ txt = "1#" + backupUuid
+ return txt
+
+@echo
+def deleteSnapshotBackup(session, args):
+ util.SMlog("Calling deleteSnapshotBackup with " + str(args))
+ secondaryStorageMountPath = args['secondaryStorageMountPath']
+ backupUUID = args['backupUUID']
+ path = args['path']
+ localMountPoint = args['localMountPoint']
+
+ backupsDir = mountSnapshotsDir(secondaryStorageMountPath, localMountPoint,
path)
+ # chdir to the backupsDir for convenience
+ chdir(backupsDir)
+
+ backupVHD = getBackupVHD(backupUUID)
+ util.SMlog("checking existence of " + backupVHD)
+
+ # The backupVHD is on secondary which is NFS and not ISCSI.
+ if not os.path.isfile(backupVHD):
+ util.SMlog("backupVHD " + backupVHD + "does not exist. Not trying to
delete it")
+ return "1"
+ util.SMlog("backupVHD " + backupVHD + " exists.")
+
+ # Just delete the backupVHD
+ try:
+ os.remove(backupVHD)
+ except OSError, (errno, strerror):
+ errMsg = "OSError while removing " + backupVHD + " with errno: " +
str(errno) + " and strerr: " + strerror
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+
+ return "1"
+
+@echo
+def revert_memory_snapshot(session, args):
+ util.SMlog("Calling revert_memory_snapshot with " + str(args))
+ vmName = args['vmName']
+ snapshotUUID = args['snapshotUUID']
+ oldVmUuid = args['oldVmUuid']
+ snapshotMemory = args['snapshotMemory']
+ hostUUID = args['hostUUID']
+ try:
+ cmd = '''xe vbd-list vm-uuid=%s | grep 'vdi-uuid' | grep -v 'not in
database' | sed -e 's/vdi-uuid ( RO)://g' ''' % oldVmUuid
+ vdiUuids = os.popen(cmd).read().split()
+ cmd2 = '''xe vm-param-get param-name=power-state uuid=''' + oldVmUuid
+ if os.popen(cmd2).read().split()[0] != 'halted':
+ os.system("xe vm-shutdown force=true vm=" + vmName)
+ os.system("xe vm-destroy uuid=" + oldVmUuid)
+ os.system("xe snapshot-revert snapshot-uuid=" + snapshotUUID)
+ if snapshotMemory == 'true':
+ os.system("xe vm-resume vm=" + vmName + " on=" + hostUUID)
+ for vdiUuid in vdiUuids:
+ os.system("xe vdi-destroy uuid=" + vdiUuid)
+ except OSError, (errno, strerror):
+ errMsg = "OSError while reverting vm " + vmName + " to snapshot " +
snapshotUUID + " with errno: " + str(errno) + " and strerr: " + strerror
+ util.SMlog(errMsg)
+ raise xs_errors.XenError(errMsg)
+ return "0"
+
+if __name__ == "__main__":
+ XenAPIPlugin.dispatch({"getVhdParent":getVhdParent,
"create_secondary_storage_folder":create_secondary_storage_folder,
"delete_secondary_storage_folder":delete_secondary_storage_folder,
"post_create_private_template":post_create_private_template, "backupSnapshot":
backupSnapshot, "deleteSnapshotBackup": deleteSnapshotBackup,
"unmountSnapshotsDir": unmountSnapshotsDir,
"revert_memory_snapshot":revert_memory_snapshot})
+
+