# Copyright 2012 Citrix Systems, Inc. Licensed under the # Apache License, Version 2.0 (the "License"); you may not use this # file except in compliance with the License. Citrix Systems, Inc. # reserves all rights not expressly granted by 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. # # Automatically generated by addcopyright.py at 04/03/2012 '''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_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 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 + "/?" 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("", "\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 = 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