mirror of
				https://github.com/apache/cloudstack.git
				synced 2025-10-26 08:42:29 +01:00 
			
		
		
		
	This reverts commit c459dfe62c57c72ced99ddb0c7d8a9547a380305, reversing changes made to 2afb739f0656d14e5c298bf469f797fff6da221b.
		
			
				
	
	
		
			199 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			199 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # 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.
 | |
| 
 | |
| 
 | |
| 
 | |
| '''Implements the CloudStack API'''
 | |
| 
 | |
| 
 | |
| from cloudtool.utils import describe
 | |
| import urllib
 | |
| import urllib2
 | |
| import os
 | |
| import xml.dom.minidom
 | |
| import re
 | |
| import base64
 | |
| import hmac
 | |
| import hashlib
 | |
| import httplib
 | |
| 
 | |
| class CloudAPI:
 | |
| 
 | |
|     @describe("server", "Management Server host name or address")
 | |
|     @describe("apikey", "Management Server apiKey")
 | |
|     @describe("securitykey", "Management Server securityKey")
 | |
|     @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",
 | |
|             apiKey=None,
 | |
|             securityKey=None
 | |
|             ):
 | |
|         self.__dict__.update(locals())
 | |
| 
 | |
|     def _make_request_with_keys(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
 | |
|         return requestUrl
 | |
| 
 | |
| 
 | |
|     def _make_request_with_auth(self, command, requests):
 | |
|         self.connection = httplib.HTTPConnection("%s"%(self.server))
 | |
|         requests["command"] = command
 | |
|         requests["apiKey"] = self.apiKey
 | |
|         requests["response"] = self.responseformat
 | |
|         requests = zip(requests.keys(), requests.values())
 | |
|         requests.sort(key=lambda x: str.lower(x[0]))
 | |
| 
 | |
|         requestUrl = "&".join(["=".join([request[0], urllib.quote(str(request[1],""))]) for request in requests])
 | |
|         hashStr = "&".join(["=".join([str.lower(request[0]), urllib.quote(str.lower(str(request[1])),"")]) for request in requests])
 | |
| 
 | |
|         sig = urllib.quote_plus(base64.encodestring(hmac.new(self.securityKey, str.lower(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(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 + "/client/api?"
 | |
| 
 | |
|         if not parameters: parameters = {}
 | |
|         if self.apiKey is not None and self.securityKey is not None:
 | |
|             return self._make_request_with_auth(command, parameters)
 | |
|         else:
 | |
|             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");
 | |
| 
 | |
|         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 = dict(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
 | |
| 
 | |
| 
 |