# 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 ''' Created on May 17, 2011 @author: frank ''' 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])