mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			155 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			155 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| 
 | |
|   #
 | |
|   # Copyright (C) 2010 Cloud.com, Inc.  All rights reserved.
 | |
|   # 
 | |
|   # This software is licensed under the GNU General Public License v3 or later.
 | |
|   # 
 | |
|   # It is free software: you can redistribute it and/or modify
 | |
|   # it under the terms of the GNU General Public License as published by
 | |
|   # the Free Software Foundation, either version 3 of the License, or any later version.
 | |
|   # This program is distributed in the hope that it will be useful,
 | |
|   # but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|   # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|   # GNU General Public License for more details.
 | |
|   # 
 | |
|   # You should have received a copy of the GNU General Public License
 | |
|   # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|   #
 | |
|  
 | |
| 
 | |
| '''Implements the Cloud.com API'''
 | |
| 
 | |
| 
 | |
| from cloudtool.utils import describe
 | |
| import urllib
 | |
| import urllib2
 | |
| import os
 | |
| import xml.dom.minidom
 | |
| import re
 | |
| 
 | |
| class CloudAPI:
 | |
|     
 | |
| 	@describe("server", "Management Server host name or address")
 | |
| 	@describe("responseformat", "Response format: xml or json")
 | |
| 	@describe("stripxml", "True if xml tags have to be stripped in the output, false otherwise")
 | |
| 	def __init__(self,
 | |
| 			server="127.0.0.1:8096",
 | |
| 			responseformat="xml",
 | |
| 			stripxml="true"
 | |
| 			):
 | |
| 		self.__dict__.update(locals())
 | |
|         
 | |
| 	def _make_request(self,command,parameters=None):
 | |
| 		'''Command is a string, parameters is a dictionary'''
 | |
| 		if ":" in self.server:
 | |
| 			host,port = self.server.split(":")
 | |
| 			port = int(port)
 | |
| 		else:
 | |
| 			host = self.server
 | |
| 			port = 8096
 | |
| 		
 | |
| 		url = "http://" + self.server + "/?"
 | |
| 		
 | |
| 		if not parameters: parameters = {}
 | |
| 		parameters["command"] = command
 | |
| 		parameters["response"] = self.responseformat
 | |
| 		querystring = urllib.urlencode(parameters)
 | |
| 		url += querystring
 | |
| 		
 | |
| 		f = urllib2.urlopen(url)
 | |
| 		data = f.read()
 | |
| 		if self.stripxml == "true":
 | |
| 			data=re.sub("<\?.*\?>", "\n", data);
 | |
| 			data=re.sub("</[a-z]*>", "\n", data);
 | |
| 			data=data.replace(">", "=");
 | |
| 			data=data.replace("=<", "\n");
 | |
| 			data=data.replace("\n<", "\n");
 | |
| 			data=re.sub("\n.*cloud-stack-version=.*", "", data);
 | |
| 			data=data.replace("\n\n\n", "\n");
 | |
|                 else:
 | |
|                         data="\n"+data+"\n"
 | |
|                 return data
 | |
| 
 | |
| 		return data
 | |
| 
 | |
| 
 | |
| def load_dynamic_methods():
 | |
| 	'''creates smart function objects for every method in the commands.xml file'''
 | |
| 	
 | |
| 	def getText(nodelist):
 | |
| 		rc = []
 | |
| 		for node in nodelist:
 | |
| 			if node.nodeType == node.TEXT_NODE: rc.append(node.data)
 | |
| 		return ''.join(rc)
 | |
| 	
 | |
| 	# FIXME figure out installation and packaging
 | |
| 	xmlfile = os.path.join("/etc/cloud/cli/","commands.xml")
 | |
| 	dom = xml.dom.minidom.parse(xmlfile)
 | |
| 	
 | |
| 	for cmd in dom.getElementsByTagName("command"):
 | |
| 		name = getText(cmd.getElementsByTagName('name')[0].childNodes).strip()
 | |
| 		assert name
 | |
| 		
 | |
| 		description = getText(cmd.getElementsByTagName('description')[0].childNodes).strip()
 | |
| 		if description: 
 | |
|                     description = '"""%s"""' % description
 | |
| 		else: description = ''
 | |
| 		arguments = []
 | |
| 		options = []
 | |
| 		descriptions = []
 | |
| 	
 | |
| 		for param in cmd.getElementsByTagName("request")[0].getElementsByTagName("arg"):
 | |
| 			argname = getText(param.getElementsByTagName('name')[0].childNodes).strip()
 | |
| 			assert argname
 | |
| 			
 | |
| 			required = getText(param.getElementsByTagName('required')[0].childNodes).strip()
 | |
| 			if required == 'true': required = True
 | |
| 			elif required == 'false': required = False
 | |
| 			else: raise AssertionError, "Not reached"
 | |
| 			if required: arguments.append(argname)
 | |
| 			options.append(argname)
 | |
| 			
 | |
|                         #import ipdb; ipdb.set_trace()
 | |
| 			requestDescription = param.getElementsByTagName('description')
 | |
| 			if requestDescription:			
 | |
| 			    descriptionParam = getText(requestDescription[0].childNodes)
 | |
|                         else: 
 | |
|                             descriptionParam = ''
 | |
| 			if descriptionParam: descriptions.append( (argname,descriptionParam) )
 | |
| 		
 | |
| 		funcparams = ["self"] + [ "%s=None"%o for o in options ]
 | |
| 		funcparams = ", ".join(funcparams)
 | |
| 		
 | |
| 		code = """
 | |
| 		def %s(%s):
 | |
| 			%s
 | |
| 			parms = locals()
 | |
| 			del parms["self"]
 | |
| 			for arg in %r:
 | |
| 				if locals()[arg] is None:
 | |
| 					raise TypeError, "%%s is a required option"%%arg 
 | |
| 			for k,v in parms.items():
 | |
| 				if v is None: del parms[k]
 | |
| 			output = self._make_request("%s",parms)
 | |
| 			return output
 | |
| 		"""%(name,funcparams,description,arguments,name)
 | |
| 
 | |
| 		namespace = {}
 | |
| 		exec code.strip() in namespace
 | |
| 		
 | |
| 		func = namespace[name]
 | |
| 		for argname,description in descriptions:
 | |
| 			func = describe(argname,description)(func)
 | |
| 		
 | |
| 		yield (name,func)
 | |
| 
 | |
| 
 | |
| for name,meth in load_dynamic_methods(): 
 | |
|     setattr(CloudAPI, name, meth)
 | |
| 
 | |
| implementor = CloudAPI
 | |
| 
 | |
| del name,meth,describe,load_dynamic_methods
 | |
| 
 | |
| 
 |