| #!/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 |
| if os.path.exists("/opt/xensource/sm"): |
| sys.path.extend(["/opt/xensource/sm/", "/usr/local/sbin/", "/sbin/"]) |
| if os.path.exists("/usr/lib/xcp/sm"): |
| sys.path.extend(["/usr/lib/xcp/sm/", "/usr/local/sbin/", "/sbin/"]) |
| |
| 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 |
| import cloudstack_pluginlib as lib |
| import logging |
| |
| lib.setup_logging("/var/log/cloud/cloud.log") |
| |
| VHDUTIL = "vhd-util" |
| VHD_PREFIX = 'VHD-' |
| CLOUD_DIR = '/var/run/cloud_mount' |
| |
| def echo(fn): |
| def wrapped(*v, **k): |
| name = fn.__name__ |
| logging.debug("#### CLOUD enter %s ####" % name ) |
| res = fn(*v, **k) |
| logging.debug("#### CLOUD exit %s ####" % name ) |
| return res |
| return wrapped |
| |
| def getPrimarySRPath(primaryStorageSRUuid, isISCSI): |
| if 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 |
| logging.debug("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" |
| logging.debug(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 |
| logging.debug(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: |
| logging.debug("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: |
| logging.debug("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 |
| logging.debug(errMsg) |
| raise xs_errors.XenError(errMsg) |
| |
| return (status == "1") |
| |
| 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 |
| logging.debug(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) |
| |
| logging.debug("Base copy of snapshotUuid: " + snapshotUuid + " is " + baseCopyUuid) |
| return baseCopyUuid |
| |
| 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 |
| logging.debug(errMsg) |
| raise xs_errors.XenError(errMsg) |
| return parentUUID |
| |
| def getVhdParent(session, args): |
| logging.debug("getParent with " + str(args)) |
| try: |
| primaryStorageSRUuid = args['primaryStorageSRUuid'] |
| snapshotUuid = args['snapshotUuid'] |
| isISCSI = getIsTrueString(args['isISCSI']) |
| |
| primarySRPath = getPrimarySRPath(primaryStorageSRUuid, isISCSI) |
| logging.debug("primarySRPath: " + primarySRPath) |
| |
| baseCopyUuid = getParentOfSnapshot(snapshotUuid, primarySRPath, isISCSI) |
| |
| return baseCopyUuid |
| except: |
| logging.debug('getVhdParent', exc_info=True) |
| raise xs_errors.XenError("Failed to getVhdParent") |
| 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 |
| logging.debug(errMsg) |
| raise xs_errors.XenError(errMsg) |
| return |
| |
| def umount(localDir): |
| try: |
| cmd = ['umount', localDir] |
| util.pread2(cmd) |
| except CommandException: |
| errMsg = "CommandException raised while trying to umount " + localDir |
| logging.debug(errMsg) |
| raise xs_errors.XenError(errMsg) |
| |
| logging.debug("Successfully unmounted " + localDir) |
| return |
| |
| @echo |
| def mountNfsSecondaryStorage(session, args): |
| remoteDir = args['remoteDir'] |
| localDir = args['localDir'] |
| nfsVersion = args['nfsVersion'] |
| logging.debug("mountNfsSecondaryStorage with params: " + str(args)) |
| mounted = False |
| f = open("/proc/mounts", 'r') |
| for line in f: |
| tokens = line.split(" ") |
| if len(tokens) > 2 and tokens[0] == remoteDir and tokens[1] == localDir: |
| mounted = True |
| |
| if mounted: |
| return "true" |
| |
| makedirs(localDir) |
| options = "soft,tcp,timeo=133,retrans=1" |
| if nfsVersion: |
| options += ",vers=" + nfsVersion |
| try: |
| cmd = ['mount', '-o', options, remoteDir, localDir] |
| txt = util.pread2(cmd) |
| except: |
| txt = '' |
| errMsg = "Unexpected error while trying to mount " + remoteDir + " to " + localDir |
| logging.debug(errMsg) |
| raise xs_errors.XenError(errMsg) |
| logging.debug("Successfully mounted " + remoteDir + " to " + localDir) |
| |
| return "true" |
| |
| @echo |
| def umountNfsSecondaryStorage(session, args): |
| localDir = args['localDir'] |
| try: |
| cmd = ['umount', localDir] |
| util.pread2(cmd) |
| except CommandException: |
| errMsg = "CommandException raised while trying to umount " + localDir |
| logging.debug(errMsg) |
| raise xs_errors.XenError(errMsg) |
| try: |
| os.system("rmdir " + localDir) |
| except: |
| pass |
| logging.debug("Successfully unmounted " + localDir) |
| return "true" |
| |
| @echo |
| def makeDirectory(session, args): |
| path = args['path'] |
| if not os.path.isdir(path): |
| try: |
| os.makedirs(path) |
| except OSError, (errno, strerror): |
| if os.path.isdir(path): |
| return "true" |
| errMsg = "OSError while creating " + path + " with errno: " + str(errno) + " and strerr: " + strerror |
| logging.debug(errMsg) |
| raise xs_errors.XenError(errMsg) |
| return "true" |
| |
| if __name__ == "__main__": |
| XenAPIPlugin.dispatch({"getVhdParent":getVhdParent, "mountNfsSecondaryStorage":mountNfsSecondaryStorage, |
| "umountNfsSecondaryStorage":umountNfsSecondaryStorage, |
| "makeDirectory":makeDirectory}) |
| |
| |