# Copyright 2012 Citrix Systems, Inc. Licensed under the # Apache License, Version 2.0 (the "License"); you may not use this # file except in compliance with the License. Citrix Systems, Inc. # reserves all rights not expressly granted by 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. # # Automatically generated by addcopyright.py at 04/03/2012 from OvmCommonModule import * from OVSSiteSR import sp_create, sr_create, sr_do from OVSParser import parse_ocfs2_cluster_conf from OVSXCluster import clusterm_set_ocfs2_cluster_conf, clusterm_start_o2cb_service from OVSSiteRMServer import get_master_ip from OvmOCFS2Module import OvmOCFS2 import re class OvmStoragePoolDecoder(json.JSONDecoder): def decode(self, jStr): dct = asciiLoads(jStr) pool = OvmStoragePool() setAttrFromDict(pool, 'uuid', dct) setAttrFromDict(pool, 'type', dct) setAttrFromDict(pool, 'path', dct) return pool class OvmStoragePoolEncoder(json.JSONEncoder): def default(self, obj): if not isinstance(obj, OvmStoragePool): raise Exception("%s is not instance of OvmStoragePool"%type(obj)) dct = {} safeDictSet(obj, dct, 'uuid') safeDictSet(obj, dct, 'type') safeDictSet(obj, dct, 'path') safeDictSet(obj, dct, 'mountPoint') safeDictSet(obj, dct, 'totalSpace') safeDictSet(obj, dct, 'freeSpace') safeDictSet(obj, dct, 'usedSpace') return dct def fromOvmStoragePool(pool): return normalizeToGson(json.dumps(pool, cls=OvmStoragePoolEncoder)) def toOvmStoragePool(jStr): return json.loads(jStr, cls=OvmStoragePoolDecoder) logger = OvmLogger('OvmStoragePool') class OvmStoragePool(OvmObject): uuid = '' type = '' path = '' mountPoint = '' totalSpace = 0 freeSpace = 0 usedSpace = 0 def _getSrByNameLable(self, poolUuid): d = db_dump('sr') for uuid, sr in d.items(): if sr.name_label == poolUuid: return sr raise Exception("No SR matching to %s" % poolUuid) def _getSpaceinfoOfDir(self, dir): stat = os.statvfs(dir) freeSpace = stat.f_frsize * stat.f_bavail; totalSpace = stat.f_blocks * stat.f_frsize; return (totalSpace, freeSpace) def _checkDirSizeForImage(self, dir, image): (x, free_storage_size) = OvmStoragePool()._getSpaceinfoOfDir(dir) image_size = os.path.getsize(image) if image_size > (free_storage_size + 1024 * 1024 * 1024): raise Exception("No space on dir %s (free storage:%s, vm size:%s)"%(dir, free_storage_size, image_size)) def _getAllMountPoints(self): mps = [] d = db_dump('sr') for uuid, sr in d.items(): mps.append(sr.mountpoint) return mps def _isMounted(self, path): res = doCmd(['mount']) return (path in res) def _mount(self, target, mountpoint, readonly=False): if not exists(mountpoint): os.makedirs(mountpoint) if not OvmStoragePool()._isMounted(mountpoint): if readonly: doCmd(['mount', target, mountpoint, '-r']) else: doCmd(['mount', target, mountpoint]) def _umount(self, mountpoint): umountCmd = ['umount', '-f', mountpoint] doCmd(umountCmd) ls = os.listdir(mountpoint) if len(ls) == 0: rmDirCmd = ['rm', '-r', mountpoint] doCmd(rmDirCmd) else: logger.warning(OvmStoragePool._umount, "Something wrong when umount %s, there are still files in directory:%s", mountpoint, " ".join(ls)) @staticmethod def create(jStr): try: pool = toOvmStoragePool(jStr) logger.debug(OvmStoragePool.create, fromOvmStoragePool(pool)) spUuid = jsonSuccessToMap(sp_create(pool.type, pool.path))['uuid'] srUuid = jsonSuccessToMap(sr_create(spUuid, name_label=pool.uuid))['uuid'] sr_do(srUuid, "initialize") rs = SUCC() return rs except Exception, e: errmsg = fmt_err_msg(e) logger.error(OvmStoragePool.create, errmsg) raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.create), errmsg) @staticmethod def getDetailsByUuid(uuid): try: sr = OvmStoragePool()._getSrByNameLable(uuid) pool = OvmStoragePool() safeSetAttr(pool, 'uuid', uuid) #Note: the sr.sp.fs_type is not mapped to its class name which we use in mgmt server safeSetAttr(pool, 'type', sr.sp.__class__.__name__) safeSetAttr(pool, 'path', sr.sp.get_fs_spec()) safeSetAttr(pool, 'mountPoint', sr.mountpoint) (totalSpace, freeSpace) = OvmStoragePool()._getSpaceinfoOfDir(sr.mountpoint) safeSetAttr(pool, 'totalSpace', totalSpace) safeSetAttr(pool, 'freeSpace', freeSpace) safeSetAttr(pool, 'usedSpace', totalSpace - freeSpace) res = fromOvmStoragePool(pool) logger.debug(OvmStoragePool.getDetailsByUuid, res) return res except Exception, e: errmsg = fmt_err_msg(e) logger.error(OvmStoragePool.getDetailsByUuid, errmsg) raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.getDetailsByUuid), errmsg) @staticmethod def downloadTemplate(uuid, secPath): secMountPoint = None try: logger.debug(OvmStoragePool.downloadTemplate, "download %s to pool %s"%(secPath, uuid)) try: tmpUuid = get_uuid() secMountPoint = join("/var/cloud/", tmpUuid) if not exists(secMountPoint): os.makedirs(secMountPoint) templateFile = None if secPath.endswith("raw"): secPathDir = os.path.dirname(secPath) templateFile = os.path.basename(secPath) else: secPathDir = secPath # mount as read-only mountCmd = ['mount.nfs', secPathDir, secMountPoint, '-r'] doCmd(mountCmd) if not templateFile: for f in os.listdir(secMountPoint): if isfile(join(secMountPoint, f)) and f.endswith('raw'): templateFile = f break if not templateFile: raise Exception("Can not find raw template in secondary storage") templateSecPath = join(secMountPoint, templateFile) sr = OvmStoragePool()._getSrByNameLable(uuid) priStorageMountPoint = sr.mountpoint # Although mgmt server will check the size, we check again for safety OvmStoragePool()._checkDirSizeForImage(priStorageMountPoint, templateSecPath) seedDir = join(priStorageMountPoint, 'seed_pool', tmpUuid) if exists(seedDir): raise Exception("%s already here, cannot override existing template" % seedDir) os.makedirs(seedDir) tgt = join(seedDir, templateFile) cpTemplateCmd = ['cp', templateSecPath, tgt] logger.info(OvmStoragePool.downloadTemplate, " ".join(cpTemplateCmd)) doCmd(cpTemplateCmd) templateSize = os.path.getsize(tgt) logger.info(OvmStoragePool.downloadTemplate, "primary_storage_download success:installPath:%s, templateSize:%s"%(tgt,templateSize)) rs = toGson({"installPath":tgt, "templateSize":templateSize}) return rs except Exception, e: errmsg = fmt_err_msg(e) logger.error(OvmStoragePool.downloadTemplate, errmsg) raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.downloadTemplate), errmsg) finally: if exists(secMountPoint): try: OvmStoragePool()._umount(secMountPoint) except Exception, e: errmsg = fmt_err_msg(e) logger.error(OvmStoragePool.downloadTemplate, 'unmount secondary storage at %s failed, %s'%(secMountPoint, errmsg)) @staticmethod def prepareOCFS2Nodes(clusterName, nodeString): def configureEtcHosts(nodes): if not exists(ETC_HOSTS): orignalConf = "" else: fd = open(ETC_HOSTS, "r") orignalConf = fd.read() fd.close() pattern = r"(.*%s.*)|(.*%s.*)" newlines = [] for n in nodes: p = pattern % (n["ip_address"], n["name"]) orignalConf = re.sub(p, "", orignalConf) newlines.append("%s\t%s\n"%(n["ip_address"], n["name"])) orignalConf = orignalConf + "".join(newlines) # remove extra empty lines orignalConf = re.sub(r"\n\s*\n*", "\n", orignalConf) logger.debug(OvmStoragePool.prepareOCFS2Nodes, "Configure /etc/hosts:%s\n"%orignalConf) fd = open(ETC_HOSTS, "w") fd.write(orignalConf) fd.close() def configureHostName(nodes): myIp = successToMap(get_master_ip())['ip'] nodeName = None for n in nodes: if myIp == n["ip_address"]: nodeName = n["name"] break if nodeName == None: raise Exception("Cannot find node equals to my ip address:%s"%myIp) if not exists(HOSTNAME_FILE): originalConf = "" else: fd = open(HOSTNAME_FILE, "r") originalConf = fd.read() fd.close() pattern = r"HOSTNAME=(.*)" # remove any old hostname originalConf = re.sub(pattern, "", originalConf) # remove extra empty lines originalConf = re.sub(r"\n\s*\n*", "\n", originalConf) + "\n" + "HOSTNAME=%s"%nodeName logger.debug(OvmStoragePool.prepareOCFS2Nodes, "Configure %s:%s\n"%(HOSTNAME_FILE,originalConf)) fd = open(HOSTNAME_FILE, "w") fd.write(originalConf) fd.close() doCmd(['hostname', nodeName]) def addNodes(nodes, clusterName): ocfs2 = OvmOCFS2() ocfs2._load() isOnline = ocfs2._isClusterOnline(clusterName) if not isOnline: ocfs2._prepareConf(clusterName) for n in nodes: ocfs2._addNode(n['name'], n['number'], n['ip_address'], 7777, clusterName, isOnline) def checkStaleCluster(clusterName): if exists('/sys/kernel/config/cluster/'): dirs = os.listdir('/sys/kernel/config/cluster/') for dir in dirs: if dir != clusterName: errMsg = '''CloudStack detected there is a stale cluster(%s) on host %s. Please manually clean up it first then add again by 1) remove the host from cloudstack 2) umount all OCFS2 device on host 3) /etc/init.d/o2cb offline %s 4) /etc/init.d/o2cb restart if this doesn't resolve the problem, please check oracle manual to see how to offline a cluster ''' % (dir, successToMap(get_master_ip())['ip'], dir) raise Exception(errMsg) try: checkStaleCluster(clusterName) nodeString = nodeString.strip(";") nodes = [] for n in nodeString.split(";"): params = n.split(":") if len(params) != 3: raise Exception("Wrong parameter(%s) in node string(%s)"%(n, nodeString)) dict = {"number":params[0], "ip_address":params[1], "name":params[2]} nodes.append(dict) if len(nodes) > 255: raise Exception("%s nodes beyond maximum 255 allowed by OCFS2"%len(nodes)) configureHostName(nodes) configureEtcHosts(nodes) addNodes(nodes, clusterName) OvmOCFS2()._start(clusterName) fd = open(OCFS2_CONF, 'r') conf = fd.readlines() fd.close() logger.debug(OvmStoragePool.prepareOCFS2Nodes, "Configure cluster.conf to:\n%s"%' '.join(conf)) rs = SUCC() return rs except Exception, e: errmsg = fmt_err_msg(e) logger.error(OvmStoragePool.prepareOCFS2Nodes, errmsg) raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.prepareOCFS2Nodes), errmsg) @staticmethod def createTemplateFromVolume(secStorageMountPath, installPath, volumePath): try: secMountPoint = "" if not isfile(volumePath): raise Exception("Cannot find %s"%volumePath) vmCfg = join(dirname(volumePath), 'vm.cfg') vmName = getVmNameFromConfigureFile(vmCfg) if vmName in doCmd(['xm', 'list']): raise Exception("%s is still running, please stop it first then create template again"%vmName) tmpUuid = get_uuid() secMountPoint = join("/var/cloud/", tmpUuid) OvmStoragePool()._mount(secStorageMountPath, secMountPoint) installPath = installPath.lstrip('/') destPath = join(secMountPoint, installPath) #This prevent us deleting whole secondary in case we got a wrong installPath if destPath == secMountPoint: raise Exception("Install path equals to root of secondary storage(%s)"%destPath) if exists(destPath): logger.warning(OvmStoragePool.createTemplateFromVolume, "%s is already here, delete it since it is most likely stale"%destPath) doCmd(['rm', '-rf', destPath]) OvmStoragePool()._checkDirSizeForImage(secMountPoint, volumePath) os.makedirs(destPath) newName = get_uuid() + ".raw" destName = join(destPath, newName) doCmd(['cp', volumePath, destName]) size = os.path.getsize(destName) resInstallPath = join(installPath, newName) OvmStoragePool()._umount(secMountPoint) rs = toGson({"installPath":resInstallPath, "templateFileName":newName, "virtualSize":size, "physicalSize":size}) return rs except Exception, e: try: if exists(secMountPoint): OvmStoragePool()._umount(secMountPoint) except Exception, e: logger.warning(OvmStoragePool.createTemplateFromVolume, "umount %s failed"%secMountPoint) errmsg = fmt_err_msg(e) logger.error(OvmStoragePool.createTemplateFromVolume, errmsg) raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.createTemplateFromVolume), errmsg) @staticmethod def delete(uuid): try: sr = OvmStoragePool()._getSrByNameLable(uuid) primaryStoragePath = sr.mountpoint OvmStoragePool()._umount(primaryStoragePath) rs = SUCC() return rs except Exception, e: errmsg = fmt_err_msg(e) logger.error(OvmStoragePool.delete, errmsg) raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.delete), errmsg) @staticmethod def copyVolume(secStorageMountPath, volumeFolderOnSecStorage, volumePath, storagePoolUuid, toSec): def copyToSecStorage(secMountPoint, volumeFolderOnSecStorage, volumePath): if not isfile(volumePath): raise Exception("Cannot find volume at %s"%volumePath) OvmStoragePool()._checkDirSizeForImage(secMountPoint, volumePath) volumeFolderOnSecStorage = volumeFolderOnSecStorage.lstrip("/") destPath = join(secMountPoint, volumeFolderOnSecStorage) #This prevent us deleting whole secondary in case we got a wrong volumeFolderOnSecStorage if destPath == secMountPoint: raise Exception("volume path equals to root of secondary storage(%s)"%destPath) if exists(destPath): logger.warning(OvmStoragePool.copyVolume, "%s already exists, delete it first"%destPath) doCmd(['rm', '-rf', destPath]) os.makedirs(destPath) newName = get_uuid() + ".raw" destName = join(destPath, newName) doCmd(['cp', volumePath, destName]) return destName def copyToPrimary(secMountPoint, volumeFolderOnSecStorage, volumePath, primaryMountPath): srcPath = join(secMountPoint, volumeFolderOnSecStorage.lstrip("/"), volumePath.lstrip("/")) if not srcPath.endswith(".raw"): srcPath = srcPath + ".raw" if not isfile(srcPath): raise Exception("Cannot find volume at %s"%srcPath) if not exists(primaryMountPath): raise Exception("Primary storage(%s) seems to have gone"%primaryMountPath) OvmStoragePool()._checkDirSizeForImage(primaryMountPath, srcPath) destPath = join(primaryMountPath, "sharedDisk") newName = get_uuid() + ".raw" destName = join(destPath, newName) doCmd(['cp', srcPath, destName]) return destName secMountPoint = "" try: tmpUuid = get_uuid() secMountPoint = join("/var/cloud/", tmpUuid) OvmStoragePool()._mount(secStorageMountPath, secMountPoint) if toSec: resultPath = copyToSecStorage(secMountPoint, volumeFolderOnSecStorage, volumePath) else: sr = OvmStoragePool()._getSrByNameLable(storagePoolUuid) primaryStoragePath = sr.mountpoint resultPath = copyToPrimary(secMountPoint, volumeFolderOnSecStorage, volumePath, primaryStoragePath) OvmStoragePool()._umount(secMountPoint) # ingratiate bad mgmt server design, it asks 'installPath' but it only wants the volume name without suffix volumeUuid = basename(resultPath).rstrip(".raw") rs = toGson({"installPath":volumeUuid}) return rs except Exception, e: try: if exists(secMountPoint): OvmStoragePool()._umount(secMountPoint) except Exception, e: logger.warning(OvmStoragePool.copyVolume, "umount %s failed"%secMountPoint) errmsg = fmt_err_msg(e) logger.error(OvmStoragePool.copyVolume, errmsg) raise XmlRpcFault(toErrCode(OvmStoragePool, OvmStoragePool.copyVolume), errmsg)