# 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. import urllib2 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 * import jsonHelper 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.port = port self.logging = logging if port == 8096: self.auth = False else: self.auth = True self.retries = 5 self.asyncTimeout = asyncTimeout def close(self): try: self.connection.close() except: pass 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"] = "json" request = zip(requests.keys(), requests.values()) request.sort(key=lambda x: str.lower(x[0])) requestUrl = "&".join(["=".join([r[0], urllib.quote_plus(str(r[1]))]) for r in request]) hashStr = "&".join(["=".join([str.lower(r[0]), str.lower(urllib.quote_plus(str(r[1]))).replace("+", "%20")]) for r in request]) sig = urllib.quote_plus(base64.encodestring(hmac.new(self.securityKey, hashStr, hashlib.sha1).digest()).strip()) requestUrl += "&signature=%s"%sig try: self.connection = urllib2.urlopen("http://%s:%d/client/api?%s"%(self.mgtSvr, self.port, requestUrl)) self.logging.debug("sending GET request: %s"%requestUrl) response = self.connection.read() self.logging.info("got response: %s"%response) except IOError, e: if hasattr(e, 'reason'): self.logging.critical("failed to reach %s because of %s"%(self.mgtSvr, e.reason)) elif hasattr(e, 'code'): self.logging.critical("server returned %d error code"%e.code) except HTTPException, h: self.logging.debug("encountered http Exception %s"%h.args) if self.retries > 0: self.retries = self.retries - 1 self.make_request_with_auth(command, requests) else: self.retries = 5 raise h else: return response def make_request_without_auth(self, command, requests={}): requests["command"] = command requests["response"] = "json" requests = zip(requests.keys(), requests.values()) requestUrl = "&".join(["=".join([request[0], urllib.quote_plus(str(request[1]))]) for request in requests]) self.connection = urllib2.urlopen("http://%s:%d/client/api?%s"%(self.mgtSvr, self.port, requestUrl)) self.logging.debug("sending GET request without auth: %s"%requestUrl) response = self.connection.read() self.logging.info("got response: %s"%response) return response def pollAsyncJob(self, jobId, response): cmd = queryAsyncJobResult.queryAsyncJobResultCmd() cmd.jobid = jobId timeout = self.asyncTimeout while timeout > 0: asyncResonse = self.make_request(cmd, response, True) if asyncResonse.jobstatus == 2: raise cloudstackException.cloudstackAPIException("asyncquery", asyncResonse.jobresult) elif asyncResonse.jobstatus == 1: return asyncResonse time.sleep(5) self.logging.debug("job: %s still processing, will timeout in %ds"%(jobId, timeout)) timeout = timeout - 5 raise cloudstackException.cloudstackAPIException("asyncquery", "Async job timeout %s"%jobId) def make_request(self, cmd, response = None, 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) elif isinstance(value, list): if len(value) == 0: requests.pop(param) else: if not isinstance(value[0], dict): requests[param] = ",".join(value) else: requests.pop(param) i = 0 for v in value: for key, val in v.iteritems(): requests["%s[%d].%s"%(param,i,key)] = val i = i + 1 if self.logging is not None: self.logging.info("sending command: %s %s"%(commandName, 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 result is None: return None result = jsonHelper.getResultObj(result, response) if raw or isAsync == "false": return result else: asynJobId = result.jobid result = self.pollAsyncJob(asynJobId, response) return result.jobresult 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())