import httplib import urllib import base64 import hmac import hashlib import json import xml.dom.minidom import types import time import inspect import cloudstackException from cloudstackAPI import * class cloudConnection(object): def __init__(self, mgtSvr, port=8096, apiKey = None, securityKey = None, asyncTimeout=3600, logging=None): self.apiKey = apiKey self.securityKey = securityKey self.mgtSvr = mgtSvr self.connection = httplib.HTTPConnection("%s:%d"%(mgtSvr,port)) self.port = port self.logging = logging if port == 8096: self.auth = False else: self.auth = True self.asyncTimeout = asyncTimeout def __copy__(self): return cloudConnection(self.mgtSvr, self.port, self.apiKey, self.securityKey, self.asyncTimeout, self.logging) def make_request_with_auth(self, command, requests={}): requests["command"] = command requests["apiKey"] = self.apiKey requests["response"] = "xml" requests = zip(requests.keys(), requests.values()) requests.sort(key=lambda x: str.lower(x[0])) requestUrl = "&".join(["=".join([request[0], urllib.quote_plus(str(request[1]))]) for request in requests]) hashStr = "&".join(["=".join([str.lower(request[0]), urllib.quote_plus(str.lower(str(request[1])))]) for request in requests]) sig = urllib.quote_plus(base64.encodestring(hmac.new(self.securityKey, hashStr, hashlib.sha1).digest()).strip()) requestUrl += "&signature=%s"%sig self.connection.request("GET", "/client/api?%s"%requestUrl) return self.connection.getresponse().read() def make_request_without_auth(self, command, requests={}): requests["command"] = command requests["response"] = "xml" requests = zip(requests.keys(), requests.values()) requestUrl = "&".join(["=".join([request[0], urllib.quote_plus(str(request[1]))]) for request in requests]) self.connection.request("GET", "/&%s"%requestUrl) return self.connection.getresponse().read() def getText(self, elements): if len(elements) < 1: return None if not elements[0].hasChildNodes(): return None if elements[0].childNodes[0].nodeValue is None: return None return elements[0].childNodes[0].nodeValue.strip() def getclassFromName(self, cmd, name): module = inspect.getmodule(cmd) return getattr(module, name)() def parseOneInstance(self, element, instance): ItemsNeedToCheck = {} for attribute in dir(instance): if attribute != "__doc__" and attribute != "__init__" and attribute != "__module__": ItemsNeedToCheck[attribute] = getattr(instance, attribute) for attribute, value in ItemsNeedToCheck.items(): if type(value) == types.ListType: subItem = [] for subElement in element.getElementsByTagName(attribute): newInstance = self.getclassFromName(instance, attribute) self.parseOneInstance(subElement, newInstance) subItem.append(newInstance) setattr(instance, attribute, subItem) continue else: item = element.getElementsByTagName(attribute) if len(item) == 0: continue returnValue = self.getText(item) setattr(instance, attribute, returnValue) def hasErrorCode(self, elements, responseName): errorCode = elements[0].getElementsByTagName("errorcode") if len(errorCode) > 0: erroCodeText = self.getText(errorCode) errorText = elements[0].getElementsByTagName("errortext") if len(errorText) > 0: errorText = self.getText(errorText) errMsg = "errorCode: %s, errorText:%s"%(erroCodeText, errorText) raise cloudstackException.cloudstackAPIException(responseName, errMsg) def paraseReturnXML(self, result, response): responseName = response.__class__.__name__.lower() cls = response.__class__ responseLists = [] morethanOne = False dom = xml.dom.minidom.parseString(result) elements = dom.getElementsByTagName(responseName) if len(elements) == 0: return responseLists self.hasErrorCode(elements, responseName) count = elements[0].getElementsByTagName("count") if len(count) > 0: morethanOne = True for node in elements[0].childNodes: if node.nodeName == "count": continue newInstance = cls() self.parseOneInstance(node, newInstance) responseLists.append(newInstance) else: if elements[0].hasChildNodes(): newInstance = cls() self.parseOneInstance(elements[0], newInstance) responseLists.append(newInstance) return responseLists, morethanOne def paraseResultFromElement(self, elements, response): responseName = response.__class__.__name__.lower() cls = response.__class__ responseLists = [] morethanOne = False newInstance = cls() self.parseOneInstance(elements[0], newInstance) responseLists.append(newInstance) return responseLists, morethanOne def getAsyncJobId(self, response, resultXml): responseName = response.__class__.__name__.lower() dom = xml.dom.minidom.parseString(resultXml) elements = dom.getElementsByTagName(responseName) if len(elements) == 0: raise cloudstackException.cloudstackAPIException("can't find %s"%responseName) self.hasErrorCode(elements, responseName) jobIdEle = elements[0].getElementsByTagName("jobid") if len(jobIdEle) == 0: errMsg = 'can not find jobId in the result:%s'%resultXml raise cloudstackException.cloudstackAPIException(errMsg) jobId = self.getText(jobIdEle) return jobId def pollAsyncJob(self, cmd, response, jobId): commandName = cmd.__class__.__name__.replace("Cmd", "") cmd = queryAsyncJobResult.queryAsyncJobResultCmd() cmd.jobid = jobId while self.asyncTimeout > 0: asyncResponse = queryAsyncJobResult.queryAsyncJobResultResponse() responseName = asyncResponse.__class__.__name__.lower() asyncResponseXml = self.make_request(cmd, asyncResponse, True) dom = xml.dom.minidom.parseString(asyncResponseXml) elements = dom.getElementsByTagName(responseName) if len(elements) == 0: raise cloudstackException.cloudstackAPIException("can't find %s"%responseName) self.hasErrorCode(elements, responseName) jobstatus = self.getText(elements[0].getElementsByTagName("jobstatus")) if jobstatus == "2": jobResult = self.getText(elements[0].getElementsByTagName("jobresult")) raise cloudstackException.cloudstackAPIException(commandName, jobResult) elif jobstatus == "1": jobResultEle = elements[0].getElementsByTagName("jobresult") return self.paraseResultFromElement(jobResultEle, response) time.sleep(5) raise cloudstackException.cloudstackAPIException(commandName, "Async job timeout") def make_request(self, cmd, response, raw=False): commandName = cmd.__class__.__name__.replace("Cmd", "") isAsync = "false" requests = {} required = [] for attribute in dir(cmd): if attribute != "__doc__" and attribute != "__init__" and attribute != "__module__": if attribute == "isAsync": isAsync = getattr(cmd, attribute) elif attribute == "required": required = getattr(cmd, attribute) else: requests[attribute] = getattr(cmd, attribute) for requiredPara in required: if requests[requiredPara] is None: raise cloudstackException.cloudstackAPIException(commandName, "%s is required"%requiredPara) '''remove none value''' for param, value in requests.items(): if value is None: requests.pop(param) if self.logging is not None: self.logging.debug("sending command: " + str(requests)) result = None if self.auth: result = self.make_request_with_auth(commandName, requests) else: result = self.make_request_without_auth(commandName, requests) if self.logging is not None: self.logging.debug("got result: " + result) if result is None: return None if raw: return result if isAsync == "false": result,num = self.paraseReturnXML(result, response) else: jobId = self.getAsyncJobId(response, result) result,num = self.pollAsyncJob(cmd, response, jobId) if num: return result else: if len(result) != 0: return result[0] return None if __name__ == '__main__': xml = '407i-1-407-RS3i-1-407-RS3system1ROOT2011-07-30T14:45:19-0700Runningfalse1CA13kvm-50-2054CentOS 5.5(64-bit) no GUI (KVM)CentOS 5.5(64-bit) no GUI (KVM)false1Small Instance15005121120NetworkFilesystem380203255.255.255.065.19.181.165.19.181.110vlan://65vlan://65GuestDirecttrue06:52:da:00:00:08KVM' conn = cloudConnection(None) print conn.paraseReturnXML(xml, deployVirtualMachine.deployVirtualMachineResponse())