| # 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. |
| ''' |
| Created on May 17, 2011 |
| |
| ''' |
| from OvmCommonModule import * |
| from OvmDiskModule import * |
| from OvmVifModule import * |
| from OvmHostModule import OvmHost |
| from string import Template |
| from OVSXXenVMConfig import * |
| from OVSSiteVM import start_vm, stop_vm, reset_vm |
| from OVSSiteCluster import * |
| from OvmStoragePoolModule import OvmStoragePool |
| from OVSXXenStore import xen_get_vm_path, xen_get_vnc_port |
| from OVSDB import db_get_vm |
| from OVSXMonitor import xen_get_vm_perf_metrics, xen_get_xm_info |
| from OVSXXenVM import xen_migrate_vm |
| from OVSSiteRMVM import unregister_vm, register_vm, set_vm_status |
| from OVSSiteVMInstall import install_vm_hvm |
| from OVSSiteRMServer import get_master_ip |
| from OVSXXenVMInstall import xen_change_vm_cdrom |
| from OVSXAPIUtil import XenAPIObject, session_login, session_logout |
| |
| |
| logger = OvmLogger("OvmVm") |
| |
| class OvmVmDecoder(json.JSONDecoder): |
| def decode(self, jStr): |
| deDict = asciiLoads(jStr) |
| vm = OvmVm() |
| setAttrFromDict(vm, 'cpuNum', deDict, int) |
| setAttrFromDict(vm, 'memory', deDict, long) |
| setattr(vm, 'rootDisk', toOvmDisk(deDict['rootDisk'])) |
| setattr(vm, 'vifs', toOvmVifList(deDict['vifs'])) |
| setattr(vm, 'disks', toOvmDiskList(deDict['disks'])) |
| setAttrFromDict(vm, 'name', deDict) |
| setAttrFromDict(vm, 'uuid', deDict) |
| setAttrFromDict(vm, 'bootDev', deDict) |
| setAttrFromDict(vm, 'type', deDict) |
| return vm |
| |
| class OvmVmEncoder(json.JSONEncoder): |
| def default(self, obj): |
| if not isinstance(obj, OvmVm): raise Exception("%s is not instance of OvmVm"%type(obj)) |
| dct = {} |
| safeDictSet(obj, dct, 'cpuNum') |
| safeDictSet(obj, dct, 'memory') |
| safeDictSet(obj, dct, 'powerState') |
| safeDictSet(obj, dct, 'name') |
| safeDictSet(obj, dct, 'type') |
| vifs = fromOvmVifList(obj.vifs) |
| dct['vifs'] = vifs |
| rootDisk = fromOvmDisk(obj.rootDisk) |
| dct['rootDisk'] = rootDisk |
| disks = fromOvmDiskList(obj.disks) |
| dct['disks'] = disks |
| return dct |
| |
| def toOvmVm(jStr): |
| return json.loads(jStr, cls=OvmVmDecoder) |
| |
| def fromOvmVm(vm): |
| return normalizeToGson(json.dumps(vm, cls=OvmVmEncoder)) |
| |
| class OvmVm(OvmObject): |
| cpuNum = 0 |
| memory = 0 |
| rootDisk = None |
| vifs = [] |
| disks = [] |
| powerState = '' |
| name = '' |
| bootDev = '' |
| type = '' |
| |
| def _getVifs(self, vmName): |
| vmPath = OvmHost()._vmNameToPath(vmName) |
| domId = OvmHost()._getDomainIdByName(vmName) |
| vifs = successToMap(xen_get_vifs(vmPath)) |
| lst = [] |
| for k in vifs: |
| v = vifs[k] |
| vifName = 'vif' + domId + '.' + k[len('vif'):] |
| vif = OvmVif() |
| (mac, bridge, type) = v.split(',') |
| safeSetAttr(vif, 'name', vifName) |
| safeSetAttr(vif, 'mac', mac) |
| safeSetAttr(vif, 'bridge', bridge) |
| safeSetAttr(vif, 'type', type) |
| lst.append(vif) |
| |
| return lst |
| |
| def _getVifsFromConfig(self, vmPath): |
| vifs = successToMap(xen_get_vifs(vmPath)) |
| lst = [] |
| for k in vifs: |
| v = vifs[k] |
| vif = OvmVif() |
| (mac, bridge, type) = v.split(',') |
| safeSetAttr(vif, 'name', k) |
| safeSetAttr(vif, 'mac', mac) |
| safeSetAttr(vif, 'bridge', bridge) |
| safeSetAttr(vif, 'type', type) |
| lst.append(vif) |
| return lst |
| |
| def _getIsoMountPath(self, vmPath): |
| vmName = basename(vmPath) |
| priStoragePath = vmPath.rstrip(join('running_pool', vmName)) |
| return join(priStoragePath, 'iso_pool', vmName) |
| |
| def _getVmTypeFromConfigFile(self, vmPath): |
| vmType = successToMap(xen_get_vm_type(vmPath))['type'] |
| return vmType.replace('hvm', 'HVM').replace('para', 'PV') |
| |
| def _tapAOwnerFile(self, vmPath): |
| # Create a file with name convention 'host_ip_address' in vmPath |
| # Because xm list doesn't return vm that has been stopped, we scan |
| # primary storage for stopped vm. This file tells us which host it belongs |
| # to. The file is used in OvmHost.getAllVms() |
| self._cleanUpOwnerFile(vmPath) |
| ownerFileName = makeOwnerFileName() |
| fd = open(join(vmPath, ownerFileName), 'w') |
| fd.write(ownerFileName) |
| fd.close() |
| |
| def _cleanUpOwnerFile(self, vmPath): |
| for f in os.listdir(vmPath): |
| fp = join(vmPath, f) |
| if isfile(fp) and f.startswith(OWNER_FILE_PREFIX): |
| os.remove(fp) |
| |
| @staticmethod |
| def create(jsonString): |
| def dumpCfg(vmName, cfgPath): |
| cfgFd = open(cfgPath, 'r') |
| cfg = cfgFd.readlines() |
| cfgFd.close() |
| logger.info(OvmVm.create, "Start %s with configure:\n\n%s\n"%(vmName, "".join(cfg))) |
| |
| def setVifsType(vifs, type): |
| for vif in vifs: |
| vif.type = type |
| |
| def hddBoot(vm, vmPath): |
| vmType = vm.type |
| if vmType == "FROMCONFIGFILE": |
| vmType = OvmVm()._getVmTypeFromConfigFile(vmPath) |
| |
| cfgDict = {} |
| if vmType == "HVM": |
| cfgDict['builder'] = "'hvm'" |
| cfgDict['acpi'] = "1" |
| cfgDict['apic'] = "1" |
| cfgDict['device_model'] = "'/usr/lib/xen/bin/qemu-dm'" |
| cfgDict['kernel'] = "'/usr/lib/xen/boot/hvmloader'" |
| vifType = 'ioemu' |
| else: |
| cfgDict['bootloader'] = "'/usr/bin/pygrub'" |
| vifType = 'netfront' |
| |
| cfgDict['name'] = "'%s'"%vm.name |
| cfgDict['disk'] = "[]" |
| cfgDict['vcpus'] = "''" |
| cfgDict['memory'] = "''" |
| cfgDict['on_crash'] = "'destroy'" |
| cfgDict['on_reboot'] = "'restart'" |
| cfgDict['vif'] = "[]" |
| |
| items = [] |
| for k in cfgDict.keys(): |
| item = " = ".join([k, cfgDict[k]]) |
| items.append(item) |
| vmSpec = "\n".join(items) |
| |
| vmCfg = open(join(vmPath, 'vm.cfg'), 'w') |
| vmCfg.write(vmSpec) |
| vmCfg.close() |
| |
| setVifsType(vm.vifs, vifType) |
| raiseExceptionIfFail(xen_set_vcpus(vmPath, vm.cpuNum)) |
| raiseExceptionIfFail(xen_set_memory(vmPath, BytesToM(vm.memory))) |
| raiseExceptionIfFail(xen_add_disk(vmPath, vm.rootDisk.path, mode=vm.rootDisk.type)) |
| vifs = [OvmVif.toXenString(v) for v in vm.vifs] |
| for vif in vifs: |
| raiseExceptionIfFail(xen_set_vifs(vmPath, vif)) |
| |
| for disk in vm.disks: |
| raiseExceptionIfFail(xen_add_disk(vmPath, disk.path, mode=disk.type)) |
| |
| raiseExceptionIfFail(xen_set_vm_vnc_password(vmPath, "")) |
| cfgFile = join(vmPath, 'vm.cfg') |
| # only HVM supports attaching cdrom |
| if vmType == 'HVM': |
| # Add an empty "hdc:cdrom" entry in config. Fisrt we set boot order to 'd' that is cdrom boot, |
| # then 'hdc:cdrom' entry will be in disk list. Second, change boot order to 'c' which |
| # is harddisk boot. VM can not start with an empty 'hdc:cdrom' when boot order is 'd'. |
| # it's tricky ! |
| raiseExceptionIfFail(xen_config_boot_sequence(vmPath, 'd')) |
| raiseExceptionIfFail(xen_config_boot_sequence(vmPath, 'c')) |
| |
| raiseExceptionIfFail(xen_correct_cfg(cfgFile, vmPath)) |
| xen_correct_qos_cfg(cfgFile) |
| dumpCfg(vm.name, cfgFile) |
| server = successToMap(get_master_ip())['ip'] |
| raiseExceptionIfFail(start_vm(vmPath, server)) |
| rs = SUCC() |
| return rs |
| |
| def cdBoot(vm, vmPath): |
| isoMountPath = None |
| try: |
| cdrom = None |
| for disk in vm.disks: |
| if disk.isIso == True: |
| cdrom = disk |
| break |
| if not cdrom: raise Exception("Cannot find Iso in disks") |
| |
| isoOnSecStorage = dirname(cdrom.path) |
| isoName = basename(cdrom.path) |
| isoMountPath = OvmVm()._getIsoMountPath(vmPath) |
| OvmStoragePool()._mount(isoOnSecStorage, isoMountPath) |
| isoPath = join(isoMountPath, isoName) |
| if not exists(isoPath): |
| raise Exception("Cannot found iso %s at %s which mounts to %s"%(isoName, isoOnSecStorage, isoMountPath)) |
| |
| stdout = run_cmd(args=['file', isoPath]) |
| if not stdout.strip().endswith("(bootable)"): raise Exception("ISO %s is not bootable"%cdrom.path) |
| |
| #now alter cdrom to correct path |
| cdrom.path = isoPath |
| if len(vm.vifs) != 0: |
| vif = vm.vifs[0] |
| #ISO boot must be HVM |
| vifCfg = ','.join([vif.mac, vif.bridge, 'ioemu']) |
| else: |
| vifCfg = '' |
| |
| rootDiskSize = os.path.getsize(vm.rootDisk.path) |
| rooDiskCfg = ':'.join([join(vmPath, basename(vm.rootDisk.path)), str(BytesToG(rootDiskSize)), 'True']) |
| disks = [rooDiskCfg] |
| for d in vm.disks: |
| if d.isIso: continue |
| size = os.path.getsize(d.path) |
| cfg = ':'.join([d.path, str(BytesToG(size)), 'True']) |
| disks.append(cfg) |
| disksCfg = ','.join(disks) |
| server = successToMap(get_master_ip())['ip'] |
| |
| raiseExceptionIfFail(install_vm_hvm(vmPath, BytesToM(vm.memory), vm.cpuNum, vifCfg, disksCfg, cdrom.path, vncpassword='', dedicated_server=server)) |
| rs = SUCC() |
| return rs |
| except Exception, e: |
| if isoMountPath and OvmStoragePool()._isMounted(isoMountPath): |
| doCmd(['umount', '-f', isoMountPath]) |
| errmsg = fmt_err_msg(e) |
| raise Exception(errmsg) |
| |
| try: |
| vm = toOvmVm(jsonString) |
| logger.debug(OvmVm.create, "creating vm, spec:%s"%jsonString) |
| rootDiskPath = vm.rootDisk.path |
| if not exists(rootDiskPath): raise Exception("Cannot find root disk %s"%rootDiskPath) |
| |
| rootDiskDir = dirname(rootDiskPath) |
| vmPath = join(dirname(rootDiskDir), vm.name) |
| if not exists(vmPath): |
| doCmd(['ln', '-s', rootDiskDir, vmPath]) |
| vmNameFile = open(join(rootDiskDir, 'vmName'), 'w') |
| vmNameFile.write(vm.name) |
| vmNameFile.close() |
| |
| OvmVm()._tapAOwnerFile(rootDiskDir) |
| # set the VM to DOWN before starting, OVS agent will check this status |
| set_vm_status(vmPath, 'DOWN') |
| if vm.bootDev == "HDD": |
| return hddBoot(vm, vmPath) |
| elif vm.bootDev == "CD": |
| return cdBoot(vm, vmPath) |
| else: |
| raise Exception("Unkown bootdev %s for %s"%(vm.bootDev, vm.name)) |
| |
| except Exception, e: |
| errmsg = fmt_err_msg(e) |
| logger.error(OvmVm.create, errmsg) |
| raise XmlRpcFault(toErrCode(OvmVm, OvmVm.create), errmsg) |
| |
| @staticmethod |
| def stop(vmName): |
| try: |
| try: |
| OvmHost()._getDomainIdByName(vmName) |
| except NoVmFoundException, e: |
| logger.info(OvmVm.stop, "vm %s is already stopped"%vmName) |
| return SUCC() |
| |
| logger.info(OvmVm.stop, "Stop vm %s"%vmName) |
| try: |
| vmPath = OvmHost()._vmNameToPath(vmName) |
| except Exception, e: |
| errmsg = fmt_err_msg(e) |
| logger.info(OvmVm.stop, "Cannot find link for vm %s on primary storage, treating it as stopped\n %s"%(vmName, errmsg)) |
| return SUCC() |
| # set the VM to RUNNING before stopping, OVS agent will check this status |
| set_vm_status(vmPath, 'RUNNING') |
| raiseExceptionIfFail(stop_vm(vmPath)) |
| return SUCC() |
| except Exception, e: |
| errmsg = fmt_err_msg(e) |
| logger.error(OvmVm.stop, errmsg) |
| raise XmlRpcFault(toErrCode(OvmVm, OvmVm.stop), errmsg) |
| |
| @staticmethod |
| def reboot(vmName): |
| try: |
| #=================================================================== |
| # Xend has a bug of reboot. If reboot vm too quick, xend return success |
| # but actually it refused reboot (seen from log) |
| # vmPath = successToMap(xen_get_vm_path(vmName))['path'] |
| # raiseExceptionIfFail(reset_vm(vmPath)) |
| #=================================================================== |
| vmPath = OvmHost()._vmNameToPath(vmName) |
| OvmVm.stop(vmName) |
| raiseExceptionIfFail(start_vm(vmPath)) |
| vncPort= successToMap(xen_get_vnc_port(vmName))['vnc_port'] |
| logger.info(OvmVm.stop, "reboot vm %s, new vncPort is %s"%(vmName, vncPort)) |
| return toGson({"vncPort":str(vncPort)}) |
| except Exception, e: |
| errmsg = fmt_err_msg(e) |
| logger.error(OvmVm.reboot, errmsg) |
| raise XmlRpcFault(toErrCode(OvmVm, OvmVm.reboot), errmsg) |
| |
| @staticmethod |
| def getDetails(vmName): |
| try: |
| vm = OvmVm() |
| |
| try: |
| OvmHost()._getDomainIdByName(vmName) |
| vmPath = OvmHost()._vmNameToPath(vmName) |
| vifsFromConfig = False |
| except NoVmFoundException, e: |
| vmPath = OvmHost()._getVmPathFromPrimaryStorage(vmName) |
| vifsFromConfig = True |
| |
| |
| if not isdir(vmPath): |
| # The case is, when vm starting was not completed at primaryStroageDownload or createVolume(e.g. mgmt server stop), the mgmt |
| # server will keep vm state in staring, then a stop command will be sent. The stop command will delete bridges that vm attaches, |
| # by retriving birdge info by OvmVm.getDetails(). In this case, the vm doesn't exists, so returns a fake object here. |
| fakeDisk = OvmDisk() |
| vm.rootDisk = fakeDisk |
| else: |
| if vifsFromConfig: |
| vm.vifs.extend(vm._getVifsFromConfig(vmPath)) |
| else: |
| vm.vifs.extend(vm._getVifs(vmName)) |
| |
| safeSetAttr(vm, 'name', vmName) |
| disks = successToMap(xen_get_vdisks(vmPath))['vdisks'].split(',') |
| rootDisk = None |
| #BUG: there is no way to get type of disk, assume all are "w" |
| for d in disks: |
| if vmName in d: |
| rootDisk = OvmDisk() |
| safeSetAttr(rootDisk, 'path', d) |
| safeSetAttr(rootDisk, 'type', "w") |
| continue |
| disk = OvmDisk() |
| safeSetAttr(disk, 'path', d) |
| safeSetAttr(disk, 'type', "w") |
| vm.disks.append(disk) |
| if not rootDisk: raise Exception("Cannot find root disk for vm %s"%vmName) |
| safeSetAttr(vm, 'rootDisk', rootDisk) |
| vcpus = int(successToMap(xen_get_vcpus(vmPath))['vcpus']) |
| safeSetAttr(vm, 'cpuNum', vcpus) |
| memory = MtoBytes(int(successToMap(xen_get_memory(vmPath))['memory'])) |
| safeSetAttr(vm, 'memory', memory) |
| vmStatus = db_get_vm(vmPath) |
| safeSetAttr(vm, 'powerState', vmStatus['status']) |
| vmType = successToMap(xen_get_vm_type(vmPath))['type'].replace('hvm', 'HVM').replace('para', 'PV') |
| safeSetAttr(vm, 'type', vmType) |
| |
| rs = fromOvmVm(vm) |
| logger.info(OvmVm.getDetails, rs) |
| return rs |
| except Exception, e: |
| errmsg = fmt_err_msg(e) |
| logger.error(OvmVm.getDetails, errmsg) |
| raise XmlRpcFault(toErrCode(OvmVm, OvmVm.getDetails), errmsg) |
| |
| @staticmethod |
| def getVmStats(vmName): |
| def getVcpuNumAndUtils(): |
| try: |
| session = session_login() |
| refs = session.xenapi.VM.get_by_name_label(vmName) |
| if len(refs) == 0: |
| raise Exception("No ref for %s found in xenapi VM objects"%vmName) |
| vm = XenAPIObject('VM', session, refs[0]) |
| VM_metrics = XenAPIObject("VM_metrics", session, vm.get_metrics()) |
| items = VM_metrics.get_VCPUs_utilisation().items() |
| nvCpus = len(items) |
| if nvCpus == 0: |
| raise Exception("vm %s has 0 vcpus !!!"%vmName) |
| |
| xmInfo = successToMap(xen_get_xm_info()) |
| nCpus = int(xmInfo['nr_cpus']) |
| totalUtils = 0.0 |
| # CPU utlization of VM = (total cpu utilization of each vcpu) / number of physical cpu |
| for num, util in items: |
| totalUtils += float(util) |
| avgUtils = float(totalUtils/nCpus) * 100 |
| return (nvCpus, avgUtils) |
| finally: |
| session_logout() |
| |
| |
| try: |
| try: |
| OvmHost()._getDomainIdByName(vmName) |
| vmPath = OvmHost()._vmNameToPath(vmName) |
| (nvcpus, avgUtils) = getVcpuNumAndUtils() |
| vifs = successToMap(xen_get_vifs(vmPath)) |
| rxBytes = 0 |
| txBytes = 0 |
| vifs = OvmVm()._getVifs(vmName) |
| for vif in vifs: |
| rxp = join('/sys/class/net', vif.name, 'statistics/rx_bytes') |
| txp = join("/sys/class/net/", vif.name, "statistics/tx_bytes") |
| if not exists(rxp): raise Exception('can not find %s'%rxp) |
| if not exists(txp): raise Exception('can not find %s'%txp) |
| rxBytes += long(doCmd(['cat', rxp])) / 1000 |
| txBytes += long(doCmd(['cat', txp])) / 1000 |
| except NoVmFoundException, e: |
| vmPath = OvmHost()._getVmPathFromPrimaryStorage(vmName) |
| nvcpus = int(successToMap(xen_get_vcpus(vmPath))['vcpus']) |
| avgUtils = 0 |
| rxBytes = 0 |
| txBytes = 0 |
| |
| rs = toGson({"cpuNum":nvcpus, "cpuUtil":avgUtils, "rxBytes":rxBytes, "txBytes":txBytes}) |
| logger.debug(OvmVm.getVmStats, rs) |
| return rs |
| except Exception, e: |
| errmsg = fmt_err_msg(e) |
| logger.error(OvmVm.getVmStats, errmsg) |
| raise XmlRpcFault(toErrCode(OvmVm, OvmVm.getVmStats), errmsg) |
| |
| @staticmethod |
| def migrate(vmName, targetHost): |
| try: |
| vmPath = OvmHost()._vmNameToPath(vmName) |
| raiseExceptionIfFail(xen_migrate_vm(vmPath, targetHost)) |
| unregister_vm(vmPath) |
| OvmVm()._cleanUpOwnerFile(vmPath) |
| return SUCC() |
| except Exception, e: |
| errmsg = fmt_err_msg(e) |
| logger.error(OvmVm.migrate, errmsg) |
| raise XmlRpcFault(toErrCode(OvmVm, OvmVm.migrate), errmsg) |
| |
| @staticmethod |
| def register(vmName): |
| try: |
| vmPath = OvmHost()._vmNameToPath(vmName) |
| raiseExceptionIfFail(register_vm(vmPath)) |
| OvmVm()._tapAOwnerFile(vmPath) |
| vncPort= successToMap(xen_get_vnc_port(vmName))['vnc_port'] |
| rs = toGson({"vncPort":str(vncPort)}) |
| logger.debug(OvmVm.register, rs) |
| return rs |
| except Exception, e: |
| errmsg = fmt_err_msg(e) |
| logger.error(OvmVm.register, errmsg) |
| raise XmlRpcFault(toErrCode(OvmVm, OvmVm.register), errmsg) |
| |
| @staticmethod |
| def getVncPort(vmName): |
| try: |
| vncPort= successToMap(xen_get_vnc_port(vmName))['vnc_port'] |
| rs = toGson({"vncPort":vncPort}) |
| logger.debug(OvmVm.getVncPort, rs) |
| return rs |
| except Exception, e: |
| errmsg = fmt_err_msg(e) |
| logger.error(OvmVm.getVncPort, errmsg) |
| raise XmlRpcFault(toErrCode(OvmVm, OvmVm.getVncPort), errmsg) |
| |
| @staticmethod |
| def detachOrAttachIso(vmName, iso, isAttach): |
| try: |
| if vmName in OvmHost.getAllVms(): |
| scope = 'both' |
| vmPath = OvmHost()._vmNameToPath(vmName) |
| else: |
| scope = 'cfg' |
| vmPath = OvmHost()._getVmPathFromPrimaryStorage(vmName) |
| |
| vmType = OvmVm()._getVmTypeFromConfigFile(vmPath) |
| if vmType != 'HVM': |
| raise Exception("Only HVM supports attaching/detaching ISO") |
| |
| if not isAttach: |
| iso = '' |
| else: |
| isoName = basename(iso) |
| isoMountPoint = OvmVm()._getIsoMountPath(vmPath) |
| isoOnSecStorage = dirname(iso) |
| OvmStoragePool()._mount(isoOnSecStorage, isoMountPoint) |
| iso = join(isoMountPoint, isoName) |
| |
| exceptionIfNoSuccess(xen_change_vm_cdrom(vmPath, iso, scope)) |
| return SUCC() |
| except Exception, e: |
| errmsg = fmt_err_msg(e) |
| logger.error(OvmVm.detachOrAttachIso, errmsg) |
| raise XmlRpcFault(toErrCode(OvmVm, OvmVm.detachOrAttachIso), errmsg) |
| |
| if __name__ == "__main__": |
| import sys |
| print OvmVm.getDetails(sys.argv[1]) |
| #print OvmVm.getVmStats(sys.argv[1]) |