From 18255e985c9c754189bd6019b6152bbb54d7ce7c Mon Sep 17 00:00:00 2001 From: Edison Su Date: Fri, 19 Aug 2011 11:22:36 -0700 Subject: [PATCH] add apikey/securitykey in cloud-cli --- cloud-cli/cloudapis/cloud.py | 254 ++++++++++++++++++++--------------- 1 file changed, 149 insertions(+), 105 deletions(-) diff --git a/cloud-cli/cloudapis/cloud.py b/cloud-cli/cloudapis/cloud.py index 1e44a54e635..4d9cd014fe4 100644 --- a/cloud-cli/cloudapis/cloud.py +++ b/cloud-cli/cloudapis/cloud.py @@ -26,122 +26,166 @@ 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("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()) + @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(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("", "\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 + 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]) - return data + 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: + '''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) - + 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) + 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) + 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():