CLOUDSTACK-3096: format codegenerator.py

This commit is contained in:
Daan Hoogland 2013-06-21 15:18:48 +02:00 committed by Sebastien Goasguen
parent f0ab05dc04
commit 1c50f1c756
1 changed files with 86 additions and 57 deletions

View File

@ -5,9 +5,9 @@
# 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
@ -23,6 +23,7 @@ import os
import sys
import urllib2
class cmdParameterProperty(object):
def __init__(self):
self.name = None
@ -30,7 +31,8 @@ class cmdParameterProperty(object):
self.desc = ""
self.type = "planObject"
self.subProperties = []
class cloudStackCmd:
def __init__(self):
self.name = ""
@ -42,8 +44,9 @@ class cloudStackCmd:
class codeGenerator:
"""
Apache CloudStack- marvin python classes can be generated from the json returned by API discovery or from the
xml spec of commands generated by the ApiDocWriter. This class provides helper methods for these uses.
Apache CloudStack- marvin python classes can be generated from the json
returned by API discovery or from the xml spec of commands generated by
the ApiDocWriter. This class provides helper methods for these uses.
"""
space = " "
cmdsName = []
@ -91,16 +94,18 @@ class codeGenerator:
def generateSubClass(self, name, properties):
'''generate code for sub list'''
subclass = 'class %s:\n'%name
subclass = 'class %s:\n' % name
subclass += self.space + "def __init__(self):\n"
for pro in properties:
if pro.desc is not None:
subclass += self.space + self.space + '""""%s"""\n'%pro.desc
if len (pro.subProperties) > 0:
subclass += self.space + self.space + 'self.%s = []\n'%pro.name
subclass += self.space + self.space + '""""%s"""\n' % pro.desc
if len(pro.subProperties) > 0:
subclass += self.space + self.space
subclass += 'self.%s = []\n' % pro.name
self.generateSubClass(pro.name, pro.subProperties)
else:
subclass += self.space + self.space + 'self.%s = None\n'%pro.name
subclass += self.space + self.space
subclass += 'self.%s = None\n' % pro.name
self.subclass.append(subclass)
@ -110,17 +115,18 @@ class codeGenerator:
self.cmdsName.append(self.cmd.name)
self.code = self.license
self.code += "\n"
self.code += '"""%s"""\n'%self.cmd.desc
self.code += '"""%s"""\n' % self.cmd.desc
self.code += 'from baseCmd import *\n'
self.code += 'from baseResponse import *\n'
self.code += "class %sCmd (baseCmd):\n"%self.cmd.name
self.code += "class %sCmd (baseCmd):\n" % self.cmd.name
self.code += self.space + "def __init__(self):\n"
self.code += self.space + self.space + 'self.isAsync = "%s"\n' %str(self.cmd.async).lower()
self.code += self.space + self.space
self.code += 'self.isAsync = "%s"\n' % str(self.cmd.async).lower()
for req in self.cmd.request:
if req.desc is not None:
self.code += self.space + self.space + '"""%s"""\n'%req.desc
self.code += self.space + self.space + '"""%s"""\n' % req.desc
if req.required == "true":
self.code += self.space + self.space + '"""Required"""\n'
@ -128,7 +134,8 @@ class codeGenerator:
if req.type == "list" or req.type == "map":
value = "[]"
self.code += self.space + self.space + 'self.%s = %s\n'%(req.name,value)
self.code += self.space + self.space
self.code += 'self.%s = %s\n' % (req.name, value)
if req.required == "true":
self.required.append(req.name)
@ -138,30 +145,33 @@ class codeGenerator:
self.code += "]\n"
self.required = []
"""generate response code"""
subItems = {}
self.code += "\n"
self.code += 'class %sResponse (baseResponse):\n'%self.cmd.name
self.code += 'class %sResponse (baseResponse):\n' % self.cmd.name
self.code += self.space + "def __init__(self):\n"
if len(self.cmd.response) == 0:
self.code += self.space + self.space + "pass"
else:
for res in self.cmd.response:
if res.desc is not None:
self.code += self.space + self.space + '"""%s"""\n'%res.desc
self.code += self.space + self.space
self.code += '"""%s"""\n' % res.desc
if len(res.subProperties) > 0:
self.code += self.space + self.space + 'self.%s = []\n'%res.name
self.code += self.space + self.space
self.code += 'self.%s = []\n' % res.name
self.generateSubClass(res.name, res.subProperties)
else:
self.code += self.space + self.space + 'self.%s = None\n'%res.name
self.code += self.space + self.space
self.code += 'self.%s = None\n' % res.name
self.code += '\n'
for subclass in self.subclass:
self.code += subclass + "\n"
fp = open(self.outputFolder + "/cloudstackAPI/%s.py"%self.cmd.name, "w")
fp = open(self.outputFolder + "/cloudstackAPI/%s.py" % self.cmd.name,
"w")
fp.write(self.code)
fp.close()
self.code = ""
@ -180,20 +190,26 @@ class codeGenerator:
body += "\n"
body += self.space + 'def __copy__(self):\n'
body += self.space + self.space + 'return CloudStackAPIClient(copy.copy(self.connection))\n'
body += self.space + self.space
body += 'return CloudStackAPIClient(copy.copy(self.connection))\n'
body += "\n"
for cmdName in self.cmdsName:
body += self.space + 'def %s(self, command, method="GET"):\n'%cmdName
body += self.space + self.space + 'response = %sResponse()\n'%cmdName
body += self.space + self.space + 'response = self.connection.marvin_request(command, response_type=response, method=method)\n'
body += self.space
body += 'def %s(self, command, method="GET"):\n' % cmdName
body += self.space + self.space
body += 'response = %sResponse()\n' % cmdName
body += self.space + self.space
body += 'response = self.connection.marvin_request(command,'
body += ' response_type=response, method=method)\n'
body += self.space + self.space + 'return response\n'
body += '\n'
imports += 'from %s import %sResponse\n'%(cmdName, cmdName)
initCmdsList += '"%s",'%cmdName
imports += 'from %s import %sResponse\n' % (cmdName, cmdName)
initCmdsList += '"%s",' % cmdName
fp = open(self.outputFolder + '/cloudstackAPI/cloudstackAPIClient.py', 'w')
fp = open(self.outputFolder + '/cloudstackAPI/cloudstackAPIClient.py',
'w')
fp.write(self.license)
for item in [header, imports, body]:
fp.write(item)
@ -224,11 +240,14 @@ class codeGenerator:
def constructResponseFromXML(self, response):
paramProperty = cmdParameterProperty()
paramProperty.name = getText(response.getElementsByTagName('name'))
paramProperty.desc = getText(response.getElementsByTagName('description'))
paramProperty.desc = getText(response.
getElementsByTagName('description'))
if paramProperty.name.find('(*)') != -1:
'''This is a list'''
paramProperty.name = paramProperty.name.split('(*)')[0]
for subresponse in response.getElementsByTagName('arguments')[0].getElementsByTagName('arg'):
argList = response.getElementsByTagName('arguments')[0].\
getElementsByTagName('arg')
for subresponse in argList:
subProperty = self.constructResponseFromXML(subresponse)
paramProperty.subProperties.append(subProperty)
return paramProperty
@ -248,10 +267,13 @@ class codeGenerator:
if async:
csCmd.async = async
for param in cmd.getElementsByTagName("request")[0].getElementsByTagName("arg"):
argList = cmd.getElementsByTagName("request")[0].\
getElementsByTagName("arg")
for param in argList:
paramProperty = cmdParameterProperty()
paramProperty.name = getText(param.getElementsByTagName('name'))
paramProperty.name =\
getText(param.getElementsByTagName('name'))
assert paramProperty.name
required = param.getElementsByTagName('required')
@ -288,18 +310,19 @@ class codeGenerator:
def constructResponseFromJSON(self, response):
paramProperty = cmdParameterProperty()
if response.has_key('name'):
if 'name' in response:
paramProperty.name = response['name']
assert paramProperty.name, "%s has no property name"%response
assert paramProperty.name, "%s has no property name" % response
if response.has_key('description'):
if 'description' in response:
paramProperty.desc = response['description']
if response.has_key('type'):
if 'type' in response:
if response['type'] in ['list', 'map', 'set']:
#Here list becomes a subproperty
if response.has_key('response'):
if 'response' in response:
for innerResponse in response['response']:
subProperty = self.constructResponseFromJSON(innerResponse)
subProperty =\
self.constructResponseFromJSON(innerResponse)
paramProperty.subProperties.append(subProperty)
paramProperty.type = response['type']
return paramProperty
@ -311,45 +334,45 @@ class codeGenerator:
jsonOut = apiStream.readlines()
assert len(jsonOut) > 0
apiDict = json.loads(jsonOut[0])
if not apiDict.has_key('listapisresponse'):
if not 'listapisresponse' in apiDict:
raise Exception("API discovery plugin response failed")
if not apiDict['listapisresponse'].has_key('count'):
if not 'count' in apiDict['listapisresponse']:
raise Exception("Malformed api response")
apilist = apiDict['listapisresponse']['api']
cmds = []
for cmd in apilist:
csCmd = cloudStackCmd()
if cmd.has_key('name'):
if 'name' in cmd:
csCmd.name = cmd['name']
assert csCmd.name
if cmd.has_key('description'):
if 'description' in cmd:
csCmd.desc = cmd['description']
if cmd.has_key('isasync'):
if 'isasync' in cmd:
csCmd.async = cmd['isasync']
for param in cmd['params']:
paramProperty = cmdParameterProperty()
if param.has_key('name'):
if 'name' in param:
paramProperty.name = param['name']
assert paramProperty.name
if param.has_key('required'):
if 'required' in param:
paramProperty.required = param['required']
if param.has_key('description'):
if 'description' in param:
paramProperty.desc = param['description']
if param.has_key('type'):
if 'type' in param:
paramProperty.type = param['type']
csCmd.request.append(paramProperty)
for response in cmd['response']:
#FIXME: ExtractImage related APIs return empty dicts in response
#FIXME: ExtractImage related APIs return empty dicts in response
if len(response) > 0:
paramProperty = self.constructResponseFromJSON(response)
csCmd.response.append(paramProperty)
@ -359,7 +382,8 @@ class codeGenerator:
def generateCodeFromJSON(self, endpointUrl):
"""
Api Discovery plugin returns the supported APIs of a CloudStack endpoint.
Api Discovery plugin returns the supported APIs of a CloudStack
endpoint.
@return: The classes in cloudstackAPI/ formed from api discovery json
"""
if endpointUrl.find('response=json') >= 0:
@ -369,30 +393,34 @@ class codeGenerator:
self.generate(cmd)
self.finalize()
def getText(elements):
return elements[0].childNodes[0].nodeValue.strip()
if __name__ == "__main__":
parser = OptionParser()
parser.add_option("-o", "--output", dest="output",
help="The path to the generated code entities, default is .")
help="The path to the generated code entities, default\
is .")
parser.add_option("-s", "--specfile", dest="spec",
help="The path and name of the api spec xml file, default is /etc/cloud/cli/commands.xml")
help="The path and name of the api spec xml file,\
default is /etc/cloud/cli/commands.xml")
parser.add_option("-e", "--endpoint", dest="endpoint",
help="The endpoint mgmt server (with open 8096) where apis are discovered, default is localhost")
help="The endpoint mgmt server (with open 8096) where\
apis are discovered, default is localhost")
(options, args) = parser.parse_args()
folder = "."
if options.output is not None:
folder = options.output
apiModule=folder + "/cloudstackAPI"
apiModule = folder + "/cloudstackAPI"
if not os.path.exists(apiModule):
try:
os.mkdir(apiModule)
except:
print "Failed to create folder %s, due to %s"%(apiModule,sys.exc_info())
print "Failed to create folder %s, due to %s" % (apiModule,
sys.exc_info())
print parser.print_help()
exit(2)
@ -400,7 +428,7 @@ if __name__ == "__main__":
if options.spec is not None:
apiSpecFile = options.spec
if not os.path.exists(apiSpecFile):
print "the spec file %s does not exists"%apiSpecFile
print "the spec file %s does not exists" % apiSpecFile
print parser.print_help()
exit(1)
@ -408,5 +436,6 @@ if __name__ == "__main__":
if options.spec is not None:
cg.generateCodeFromXML(apiSpecFile)
elif options.endpoint is not None:
endpointUrl='http://%s:8096/client/api?command=listApis&response=json'%options.endpoint
endpointUrl = 'http://%s:8096/client/api?command=listApis&\
response=json' % options.endpoint
cg.generateCodeFromJSON(endpointUrl)