diff --git a/tools/testClient/jsonHelper.py b/tools/testClient/jsonHelper.py new file mode 100644 index 00000000000..9c692159df9 --- /dev/null +++ b/tools/testClient/jsonHelper.py @@ -0,0 +1,169 @@ +import cloudstackException +import json +import inspect +from cloudstackAPI import * +class jsonLoader: + '''The recursive class for building and representing objects with.''' + def __init__(self, obj): + for k in obj: + v = obj[k] + if isinstance(v, dict): + setattr(self, k, jsonLoader(v)) + elif isinstance(v, (list, tuple)): + setattr(self, k, [jsonLoader(elem) for elem in v]) + else: + setattr(self,k,v) + def __getattr__(self, val): + if val in self.__dict__: + return self.__dict__[val] + else: + return None + def __repr__(self): + return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v) in self.__dict__.iteritems())) + def __str__(self): + return '{%s}' % str(', '.join('%s : %s' % (k, repr(v)) for (k, v) in self.__dict__.iteritems())) + + +class jsonDump: + @staticmethod + def __serialize(obj): + """Recursively walk object's hierarchy.""" + if isinstance(obj, (bool, int, long, float, basestring)): + return obj + elif isinstance(obj, dict): + obj = obj.copy() + newobj = {} + for key in obj: + if obj[key] is not None: + if (isinstance(obj[key], list) and len(obj[key]) == 0): + continue + newobj[key] = jsonDump.__serialize(obj[key]) + + return newobj + elif isinstance(obj, list): + return [jsonDump.__serialize(item) for item in obj] + elif isinstance(obj, tuple): + return tuple(jsonDump.__serialize([item for item in obj])) + elif hasattr(obj, '__dict__'): + return jsonDump.__serialize(obj.__dict__) + else: + return repr(obj) # Don't know how to handle, convert to string + + @staticmethod + def dump(obj): + return jsonDump.__serialize(obj) + +def getclassFromName(cmd, name): + module = inspect.getmodule(cmd) + return getattr(module, name)() + +def finalizeResultObj(result, responseName, responsecls): + if responsecls is None and responseName.endswith("response") and responseName != "queryasyncjobresultresponse": + '''infer the response class from the name''' + moduleName = responseName.replace("response", "") + try: + responsecls = getclassFromName(moduleName, responseName) + except: + pass + + if responseName is not None and responseName == "queryasyncjobresultresponse" and responsecls is not None and result.jobresult is not None: + result.jobresult = finalizeResultObj(result.jobresult, None, responsecls) + return result + elif responsecls is not None: + for k,v in result.__dict__.iteritems(): + if k in responsecls.__dict__: + return result + + attr = result.__dict__.keys()[0] + + value = getattr(result, attr) + if not isinstance(value, jsonLoader): + return result + + findObj = False + for k,v in value.__dict__.iteritems(): + if k in responsecls.__dict__: + findObj = True + break + if findObj: + return value + else: + return result + else: + return result + + + +def getResultObj(returnObj, responsecls=None): + returnObj = json.loads(returnObj) + + if len(returnObj) == 0: + return None + responseName = returnObj.keys()[0] + + response = returnObj[responseName] + if len(response) == 0: + return None + + result = jsonLoader(response) + if result.errorcode is not None: + errMsg = "errorCode: %s, errorText:%s"%(result.errorcode, result.errortext) + raise cloudstackException.cloudstackAPIException(responseName.replace("response", ""), errMsg) + + if result.count is not None: + for key in result.__dict__.iterkeys(): + if key == "count": + continue + else: + return getattr(result, key) + else: + return finalizeResultObj(result, responseName, responsecls) + +if __name__ == "__main__": + + result = '{ "listzonesresponse" : { "count":1 ,"zone" : [ {"id":1,"name":"test0","dns1":"8.8.8.8","dns2":"4.4.4.4","internaldns1":"192.168.110.254","internaldns2":"192.168.110.253","networktype":"Basic","securitygroupsenabled":true,"allocationstate":"Enabled","zonetoken":"5e818a11-6b00-3429-9a07-e27511d3169a","dhcpprovider":"DhcpServer"} ] } }' + zones = getResultObj(result) + print zones[0].id + res = authorizeSecurityGroupIngress.authorizeSecurityGroupIngressResponse() + result = '{ "queryasyncjobresultresponse" : {"jobid":10,"jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"securitygroup":{"id":1,"name":"default","description":"Default Security Group","account":"admin","domainid":1,"domain":"ROOT","ingressrule":[{"ruleid":1,"protocol":"tcp","startport":22,"endport":22,"securitygroupname":"default","account":"a"},{"ruleid":2,"protocol":"tcp","startport":22,"endport":22,"securitygroupname":"default","account":"b"}]}}} }' + asynJob = getResultObj(result, res) + print asynJob.jobid, repr(asynJob.jobresult) + print asynJob.jobresult.ingressrule[0].account + + result = '{ "queryasyncjobresultresponse" : {"errorcode" : 431, "errortext" : "Unable to execute API command queryasyncjobresultresponse due to missing parameter jobid"} }' + try: + asynJob = getResultObj(result) + except cloudstackException.cloudstackAPIException, e: + print e + + result = '{ "queryasyncjobresultresponse" : {} }' + asynJob = getResultObj(result) + print asynJob + + result = '{}' + asynJob = getResultObj(result) + print asynJob + + result = '{ "createzoneresponse" : { "zone" : {"id":1,"name":"test0","dns1":"8.8.8.8","dns2":"4.4.4.4","internaldns1":"192.168.110.254","internaldns2":"192.168.110.253","networktype":"Basic","securitygroupsenabled":true,"allocationstate":"Enabled","zonetoken":"3442f287-e932-3111-960b-514d1f9c4610","dhcpprovider":"DhcpServer"} } }' + res = createZone.createZoneResponse() + zone = getResultObj(result, res) + print zone.id + + result = '{ "attachvolumeresponse" : {"jobid":24} }' + res = attachVolume.attachVolumeResponse() + res = getResultObj(result, res) + print res + + result = '{ "listtemplatesresponse" : { } }' + print getResultObj(result, listTemplates.listTemplatesResponse()) + + result = '{ "queryasyncjobresultresponse" : {"jobid":34,"jobstatus":2,"jobprocstatus":0,"jobresultcode":530,"jobresulttype":"object","jobresult":{"errorcode":431,"errortext":"Please provide either a volume id, or a tuple(device id, instance id)"}} }' + print getResultObj(result, listTemplates.listTemplatesResponse()) + result = '{ "queryasyncjobresultresponse" : {"jobid":41,"jobstatus":1,"jobprocstatus":0,"jobresultcode":0,"jobresulttype":"object","jobresult":{"virtualmachine":{"id":37,"name":"i-2-37-TEST","displayname":"i-2-37-TEST","account":"admin","domainid":1,"domain":"ROOT","created":"2011-08-25T11:13:42-0700","state":"Running","haenable":false,"zoneid":1,"zonename":"test0","hostid":5,"hostname":"SimulatedAgent.1e629060-f547-40dd-b792-57cdc4b7d611","templateid":10,"templatename":"CentOS 5.3(64-bit) no GUI (Simulator)","templatedisplaytext":"CentOS 5.3(64-bit) no GUI (Simulator)","passwordenabled":false,"serviceofferingid":7,"serviceofferingname":"Small Instance","cpunumber":1,"cpuspeed":500,"memory":512,"guestosid":11,"rootdeviceid":0,"rootdevicetype":"NetworkFilesystem","securitygroup":[{"id":1,"name":"default","description":"Default Security Group"}],"nic":[{"id":43,"networkid":204,"netmask":"255.255.255.0","gateway":"192.168.1.1","ipaddress":"192.168.1.27","isolationuri":"ec2://untagged","broadcasturi":"vlan://untagged","traffictype":"Guest","type":"Direct","isdefault":true,"macaddress":"06:56:b8:00:00:53"}],"hypervisor":"Simulator"}}} }' + vm = getResultObj(result, deployVirtualMachine.deployVirtualMachineResponse()) + print vm.jobresult.id + + cmd = deployVirtualMachine.deployVirtualMachineCmd() + responsename = cmd.__class__.__name__.replace("Cmd", "Response") + response = getclassFromName(cmd, responsename) + print response.id \ No newline at end of file