Skip to content

Commit 066d94f

Browse files
committed
Add cloud-tool into FOSS
1 parent d7bf859 commit 066d94f

11 files changed

Lines changed: 659 additions & 2 deletions

File tree

client/tomcatconf/commands.properties.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ authorizeNetworkGroupIngress=com.cloud.api.commands.AuthorizeNetworkGroupIngress
210210
revokeNetworkGroupIngress=com.cloud.api.commands.RevokeNetworkGroupIngressCmd;11
211211
listNetworkGroups=com.cloud.api.commands.ListNetworkGroupsCmd;11
212212

213-
registerPreallocatedLun=com.cloud.server.api.commands.RegisterPreallocatedLunCmd;1
214-
deletePreallocatedLun=com.cloud.server.api.commands.DeletePreallocatedLunCmd;1
213+
registerPreallocatedLun=com.cloud.api.commands.RegisterPreallocatedLunCmd;1
214+
deletePreallocatedLun=com.cloud.api.commands.DeletePreallocatedLunCmd;1
215215
listPreallocatedLuns=com.cloud.api.commands.ListPreallocatedLunsCmd;1
216216

217217
#### vm group commands

cloud-cli/bindir/cloud-tool

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env python
2+
3+
import sys
4+
import os
5+
6+
sys.path.append(os.path.dirname(os.path.dirname(__file__)))
7+
8+
import cloudtool
9+
10+
ret = cloudtool.main()
11+
if ret: sys.exit(ret)

cloud-cli/cloudapis/__init__.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'''
2+
Created on Aug 2, 2010
3+
4+
@author: rudd-o
5+
'''
6+
7+
import os,pkgutil
8+
9+
def get_all_apis():
10+
apis = []
11+
for x in pkgutil.walk_packages([os.path.dirname(__file__)]):
12+
loader = x[0].find_module(x[1])
13+
try: module = loader.load_module("cloudapis." + x[1])
14+
except ImportError: continue
15+
apis.append(module)
16+
return apis
17+
18+
def lookup_api(api_name):
19+
api = None
20+
matchingapi = [ x for x in get_all_apis() if api_name.replace("-","_") == x.__name__.split(".")[-1] ]
21+
if not matchingapi: api = None
22+
else: api = matchingapi[0]
23+
if api: api = getattr(api,"implementor")
24+
return api
25+

cloud-cli/cloudapis/amazon.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
'''Implements the Amazon API'''
2+
3+
4+
import boto.ec2
5+
import os
6+
from cloudtool.utils import describe,OptionConflictError,OptionValueError
7+
raise ImportError
8+
9+
class AmazonAPI:
10+
11+
@describe("access_key", "Amazon access key")
12+
@describe("secret_key", "Amazon secret key")
13+
@describe("region", "Amazon region")
14+
@describe("endpoint", "Amazon endpoint")
15+
def __init__(self,
16+
access_key=os.environ.get("AWS_ACCESS_KEY_ID",None),
17+
secret_key=os.environ.get("AWS_SECRET_ACCESS_KEY",None),
18+
region=None,
19+
endpoint=None):
20+
if not access_key: raise OptionValueError,"you need to specify an access key"
21+
if not secret_key: raise OptionValueError,"you need to specify a secret key"
22+
if region and endpoint:
23+
raise OptionConflictError,("mutually exclusive with --endpoint",'--region')
24+
self.__dict__.update(locals())
25+
26+
def _get_regions(self):
27+
return boto.ec2.regions(aws_access_key_id=self.access_key,aws_secret_access_key=self.secret_key)
28+
29+
def _get_region(self,name):
30+
try: return [ x for x in self._get_regions() if x.name == name ][0]
31+
except IndexError: raise KeyError,name
32+
33+
def _connect(self):
34+
if self.region:
35+
region = self._get_region(self.region)
36+
self.connection = region.connect(
37+
aws_access_key_id=self.access_key,
38+
aws_secret_access_key=self.secret_key
39+
)
40+
else:
41+
self.connection = boto.ec2.connection.EC2Connection(
42+
host=self.endpoint,
43+
aws_access_key_id=self.access_key,
44+
aws_secret_access_key=self.secret_key
45+
)
46+
def list_regions(self):
47+
"""Lists all regions"""
48+
regions = self._get_regions()
49+
for r in regions: print r
50+
51+
def get_all_images(self):
52+
"""Lists all images"""
53+
self._connect()
54+
images = self.connection.get_all_images()
55+
for i in images: print i
56+
57+
def get_region(self):
58+
"""Gets the region you're connecting to"""
59+
self._connect()
60+
print self.connection.region
61+
62+
63+
implementor = AmazonAPI

cloud-cli/cloudapis/cloud.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
'''Implements the Cloud.com API'''
2+
3+
4+
from cloudtool.utils import describe
5+
import urllib
6+
import urllib2
7+
import os
8+
import xml.dom.minidom
9+
10+
class CloudAPI:
11+
12+
@describe("server", "Management Server host name or address")
13+
@describe("responseformat", "Response format: xml or json")
14+
def __init__(self,
15+
server="127.0.0.1:8096",
16+
responseformat="xml",
17+
):
18+
self.__dict__.update(locals())
19+
20+
def _make_request(self,command,parameters=None):
21+
'''Command is a string, parameters is a dictionary'''
22+
if ":" in self.server:
23+
host,port = self.server.split(":")
24+
port = int(port)
25+
else:
26+
host = self.server
27+
port = 8096
28+
29+
url = "http://" + self.server + "/?"
30+
31+
if not parameters: parameters = {}
32+
parameters["command"] = command
33+
parameters["response"] = self.responseformat
34+
querystring = urllib.urlencode(parameters)
35+
url += querystring
36+
37+
f = urllib2.urlopen(url)
38+
39+
data = f.read()
40+
41+
return data
42+
43+
44+
def load_dynamic_methods():
45+
'''creates smart function objects for every method in the commands.xml file'''
46+
47+
def getText(nodelist):
48+
rc = []
49+
for node in nodelist:
50+
if node.nodeType == node.TEXT_NODE: rc.append(node.data)
51+
return ''.join(rc)
52+
53+
# FIXME figure out installation and packaging
54+
xmlfile = os.path.join("/etc/cloud/cli/","commands.xml")
55+
dom = xml.dom.minidom.parse(xmlfile)
56+
57+
for cmd in dom.getElementsByTagName("command"):
58+
name = getText(cmd.getElementsByTagName('name')[0].childNodes).strip()
59+
assert name
60+
61+
description = cmd.getElementsByTagName('name')[0].getAttribute("description")
62+
if description: description = '"""%s"""' % description
63+
else: description = ''
64+
arguments = []
65+
options = []
66+
descriptions = []
67+
68+
for param in cmd.getElementsByTagName('arg'):
69+
argname = getText(param.childNodes).strip()
70+
assert argname
71+
72+
required = param.getAttribute("required").strip()
73+
if required == 'true': required = True
74+
elif required == 'false': required = False
75+
else: raise AssertionError, "Not reached"
76+
if required: arguments.append(argname)
77+
options.append(argname)
78+
79+
description = param.getAttribute("description").strip()
80+
if description: descriptions.append( (argname,description) )
81+
82+
funcparams = ["self"] + [ "%s=None"%o for o in options ]
83+
funcparams = ", ".join(funcparams)
84+
85+
code = """
86+
def %s(%s):
87+
%s
88+
parms = locals()
89+
del parms["self"]
90+
for arg in %r:
91+
if locals()[arg] is None:
92+
raise TypeError, "%%s is a required option"%%arg
93+
for k,v in parms.items():
94+
if v is None: del parms[k]
95+
output = self._make_request("%s",parms)
96+
return output
97+
"""%(name,funcparams,description,arguments,name)
98+
99+
namespace = {}
100+
exec code.strip() in namespace
101+
102+
func = namespace[name]
103+
for argname,description in descriptions:
104+
func = describe(argname,description)(func)
105+
106+
yield (name,func)
107+
108+
109+
for name,meth in load_dynamic_methods(): setattr(CloudAPI,name,meth)
110+
111+
implementor = CloudAPI
112+
113+
del name,meth,describe,load_dynamic_methods

cloud-cli/cloudtool/__init__.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
'''
2+
Created on Aug 2, 2010
3+
4+
@author: rudd-o
5+
'''
6+
7+
import sys
8+
import cloudapis as apis
9+
import cloudtool.utils as utils
10+
11+
12+
def main(argv=None):
13+
14+
if argv == None:
15+
argv = sys.argv
16+
17+
prelim_args = [ x for x in argv[1:] if not x.startswith('-') ]
18+
parser = utils.get_parser()
19+
20+
api = __import__("cloudapis")
21+
apis = getattr(api, "implementor")
22+
if len(prelim_args) == 0:
23+
parser.error("you need to specify an API as the first argument\n\nSupported APIs:\n" + "\n".join(utils.get_api_list()))
24+
elif len(prelim_args) == 1:
25+
commandlist = utils.get_command_list(apis)
26+
parser.error("you need to specify a command name as the second argument\n\nCommands supported by the %s API:\n"%prelim_args[0] + "\n".join(commandlist))
27+
28+
command = utils.lookup_command_in_api(apis,prelim_args[1])
29+
if not command: parser.error("command %r not supported by the %s API"%(prelim_args[1],prelim_args[0]))
30+
31+
parser = utils.get_parser(apis.__init__,command)
32+
argv = argv[1:]
33+
opts,args,api_optionsdict,cmd_optionsdict = parser.parse_args(argv)
34+
35+
36+
try:
37+
api = apis(**api_optionsdict)
38+
except utils.OptParseError,e:
39+
parser.error(str(e))
40+
41+
command = utils.lookup_command_in_api(api,args[1])
42+
43+
# we now discard the first two arguments as those necessarily are the api and command names
44+
args = args[2:]
45+
46+
try: return command(*args,**cmd_optionsdict)
47+
except TypeError,e: parser.error(str(e))
48+
49+
50+
if __name__ == '__main__':
51+
main(argv)

0 commit comments

Comments
 (0)