Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Commit

Permalink
Rewrote the whole XML handling mechanism. This is much simpler and al…
Browse files Browse the repository at this point in the history
…so more powerful.

First cut at some EC2 capabilities.  Using new Query interface.  Only a few commands are implemented thus far.
  • Loading branch information
Mitch.Garnaat authored and Mitch.Garnaat committed Dec 14, 2006
1 parent 86abd95 commit b8de0fd
Show file tree
Hide file tree
Showing 14 changed files with 514 additions and 162 deletions.
6 changes: 5 additions & 1 deletion boto/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ def connect_sqs(aws_access_key_id=None, aws_secret_access_key=None):
def connect_s3(aws_access_key_id=None, aws_secret_access_key=None):
from boto.connection import S3Connection
return S3Connection(aws_access_key_id, aws_secret_access_key)


def connect_ec2(aws_access_key_id=None, aws_secret_access_key=None):
from boto.connection import EC2Connection
return EC2Connection(aws_access_key_id, aws_secret_access_key)

84 changes: 51 additions & 33 deletions boto/acl.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,63 +19,81 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.

from boto.user import User

CannedACLStrings = ['private', 'public-read',
'public-read-write', 'authenticated-read']

class Policy:

def __init__(self, parent=None, xml_attrs=None):
def __init__(self, parent=None):
self.parent = parent
self.acl = None

def add_acl(self, acl):
self.acl = acl

# This allows the XMLHandler to set the attributes as they are named
# in the XML response but have the capitalized names converted to
# more conventional looking python variables names automatically
def __setattr__(self, key, value):
if key == 'AccessControlPolicy':
def startElement(self, name, attrs, connection):
if name == 'Owner':
self.owner = User(self)
return self.owner
elif name == 'AccessControlList':
self.acl = ACL(self)
return self.acl
else:
return None

def endElement(self, name, value, connection):
if name == 'Owner':
pass
elif name == 'AccessControlList':
pass
else:
self.__dict__[key] = value
setattr(self, name, value)

class ACL:

def __init__(self, policy=None, xml_attrs=None):
if policy:
policy.add_acl(self)
def __init__(self, policy=None):
self.policy = policy
self.grants = []

def add_grant(self, grant):
self.grants.append(grant)

# This allows the XMLHandler to set the attributes as they are named
# in the XML response but have the capitalized names converted to
# more conventional looking python variables names automatically
def __setattr__(self, key, value):
if key == 'AccessControlList':
def startElement(self, name, attrs, connection):
if name == 'Grant':
self.grants.append(Grant(self))
return self.grants[-1]
else:
return None

def endElement(self, name, value, connection):
if name == 'Grant':
pass
else:
self.__dict__[key] = value
setattr(self, name, value)

class Grant:

def __init__(self, acl=None, xml_attrs=None):
if acl:
acl.add_grant(self)

# This allows the XMLHandler to set the attributes as they are named
# in the XML response but have the capitalized names converted to
# more conventional looking python variables names automatically
def __setattr__(self, key, value):
if key == 'Permission':
self.__dict__['permission'] = value
elif key == 'owner':
self.__dict__['grantee'] = value
elif key == 'Grant':
def __init__(self, acl=None, grantee=None):
self.acl = acl
self.grantee = grantee

def startElement(self, name, attrs, connection):
if name == 'Grantee':
self.grantee = User(self)
if attrs.has_key('xsi:type'):
self.grantee.type = attrs['xsi:type']
else:
self.grantee.type = None
return self.grantee
else:
return None

def endElement(self, name, value, connection):
if name == 'Grantee':
pass
elif name == 'Permission':
self.permission = value
else:
self.__dict__[key] = value
setattr(self, name, value)



37 changes: 18 additions & 19 deletions boto/bucket.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
# IN THE SOFTWARE.

from boto import handler
from boto.acl import Policy, ACL, Grant, CannedACLStrings
from boto.resultset import ResultSet
from boto.acl import Policy, CannedACLStrings
from boto.user import User
from boto.key import Key
from boto.exception import S3ResponseError
Expand All @@ -30,21 +31,21 @@

class Bucket:

def __init__(self, connection=None, name=None, debug=None, xml_attrs=None):
def __init__(self, connection=None, name=None, debug=None):
self.name = name
self.connection = connection
self.debug = debug

# This allows the XMLHandler to set the attributes as they are named
# in the XML response but have the capitalized names converted to
# more conventional looking python variables names automatically
def __setattr__(self, key, value):
if key == 'Name':
self.__dict__['name'] = value
elif key == 'CreationDate':
self.__dict__['creation_date'] = value
def startElement(self, name, attrs, connection):
return None

def endElement(self, name, value, connection):
if name == 'Name':
self.name = value
elif name == 'CreationDate':
self.creation_date = value
else:
self.__dict__[key] = value
setattr(self, name, value)

def lookup(self, key):
path = '/%s/%s' % (self.name, key)
Expand Down Expand Up @@ -84,9 +85,10 @@ def get_all_keys(self, headers=None, **params):
response = self.connection.make_request('GET', path, headers)
body = response.read()
if response.status == 200:
h = handler.XmlHandler(self, {'Owner': User, 'Contents': Key})
rs = ResultSet('Contents', Key)
h = handler.XmlHandler(rs, self)
xml.sax.parseString(body, h)
return h.rs
return rs
else:
raise S3ResponseError(response.status, response.reason)

Expand Down Expand Up @@ -129,13 +131,10 @@ def get_acl(self, key_name=None):
response = self.connection.make_request('GET', path)
body = response.read()
if response.status == 200:
h = handler.XmlHandler(self, {'AccessControlPolicy' : Policy,
'AccessControlList' : ACL,
'Grant': Grant,
'Grantee': User,
'Owner' : User})
policy = Policy(self)
h = handler.XmlHandler(policy, self)
xml.sax.parseString(body, h)
return h.rs[0]
return policy
else:
raise S3ResponseError(response.status, response.reason)

Expand Down
127 changes: 115 additions & 12 deletions boto/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@
from boto import handler
from boto.queue import Queue
from boto.bucket import Bucket
from boto.resultset import ResultSet
from boto.user import User
from boto.image import Image
from boto.keypair import KeyPair
from boto.securitygroup import SecurityGroup
import boto.utils

PORTS_BY_SECURITY = { True: 443, False: 80 }
Expand Down Expand Up @@ -161,9 +165,10 @@ def get_all_queues(self, prefix=''):
body = response.read()
if response.status >= 300:
raise SQSError(response.status, response.reason, body)
h = handler.XmlHandler(self, {'QueueUrl': Queue})
rs = ResultSet('QueueUrl', Queue)
h = handler.XmlHandler(rs, self)
xml.sax.parseString(body, h)
return h.rs
return rs

def create_queue(self, queue_name, visibility_timeout=None):
path = '/?QueueName=%s' % queue_name
Expand All @@ -173,19 +178,20 @@ def create_queue(self, queue_name, visibility_timeout=None):
body = response.read()
if response.status >= 300:
raise SQSError(response.status, response.reason, body)
h = handler.XmlHandler(self, {'QueueUrl' : Queue})
q = Queue(self)
h = handler.XmlHandler(q, self)
xml.sax.parseString(body, h)
self._last_rs = h.rs
return h.rs[0]
return q

def delete_queue(self, queue):
response = self.make_request('DELETE', queue.id)
body = response.read()
if response.status >= 300:
raise SQSError(response.status, response.reason, body)
h = handler.XmlHandler(self, {})
rs = ResultSet()
h = handler.XmlHandler(rs, self)
xml.sax.parseString(body, h)
return h.rs
return rs

class S3Connection(AWSAuthConnection):

Expand All @@ -197,18 +203,28 @@ def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
aws_access_key_id, aws_secret_access_key,
is_secure, port, debug)

def generate_url(self, request, bits=None, expires_in=60):
if bits:
if not isinstance(bits, Bits):
raise BitBucketTypeError('Value must be of type Bits')
self.connection.query_gen.set_expires_in(expires_in)
if request == 'get':
return self.connection.query_gen.get(self.name, bits.key)
elif request == 'delete':
return self.connection.query_gen.delete(self.name, bits.key)
else:
raise BitBucketError('Invalid request: %s' % request)

def get_all_buckets(self):
path = '/'
response = self.make_request('GET', urllib.quote(path))
body = response.read()
if response.status > 300:
raise S3ResponseError(response.status, response.reason)
# h = handler.XmlHandler(self, {'Owner': User,
# 'Bucket': Bucket})
# ignoring Owner for now
h = handler.XmlHandler(self, {'Bucket': Bucket})
rs = ResultSet('Bucket', Bucket)
h = handler.XmlHandler(rs, self)
xml.sax.parseString(body, h)
return h.rs
return rs

def create_bucket(self, bucket_name, headers={}):
path = '/%s' % bucket_name
Expand All @@ -229,3 +245,90 @@ def delete_bucket(self, bucket):
if response.status != 204:
raise S3ResponseError(response.status, response.reason)

class EC2Connection(AWSAuthConnection):

DefaultHost = 'ec2.amazonaws.com'
EC2Version = '2006-10-01'
SignatureVersion = '1'

def __init__(self, aws_access_key_id=None, aws_secret_access_key=None,
is_secure=True, port=None, debug=0):
AWSAuthConnection.__init__(self, self.DefaultHost,
aws_access_key_id, aws_secret_access_key,
is_secure, port, debug)

def make_request(self, action, params=None):
if params == None:
params = {}
h = hmac.new(key=self.aws_secret_access_key, digestmod=sha)
params['Action'] = action
params['Version'] = self.EC2Version
params['AWSAccessKeyId'] = self.aws_access_key_id
params['SignatureVersion'] = self.SignatureVersion
params['Timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime())
keys = params.keys()
keys.sort(cmp = lambda x, y: cmp(x.lower(), y.lower()))
qs = ''
for key in keys:
h.update(key)
h.update(str(params[key]))
qs += key + '=' + urllib.quote(str(params[key])) + '&'
signature = base64.b64encode(h.digest())
qs += 'Signature=' + urllib.quote(signature)
self.connection.request('GET', '/?%s' % qs)
try:
return self.connection.getresponse()
except httplib.HTTPException, e:
self.make_http_connection()
self.connection.request('GET', '/?%s' % qs)
return self.connection.getresponse()

def build_list_params(self, params, items, label):
for i in range(1, len(items)+1):
params['%s.%d' % (label, i)] = items[i-1]

def describe_images(self, image_ids=None, owners=None, executable_by=None):
params = {}
if image_ids:
self.build_list_params(params, image_ids, 'ImageId')
if owners:
self.build_list_params(params, owners, 'Owner')
if executable_by:
self.build_list_params(params, executable_by, 'ExecutableBy')
response = self.make_request('DescribeImages')
body = response.read()
if response.status == 200:
rs = ResultSet('item', Image)
h = handler.XmlHandler(rs, self)
xml.sax.parseString(body, h)
return rs
else:
raise S3ResponseError(response.status, response.reason)

def describe_keypairs(self, keynames=None):
params = {}
if keynames:
self.build_list_params(params, keynames, 'KeyName')
response = self.make_request('DescribeKeyPairs', params)
body = response.read()
if response.status == 200:
rs = ResultSet('item', KeyPair)
h = handler.XmlHandler(rs, self)
xml.sax.parseString(body, h)
return rs
else:
raise S3ResponseError(response.status, response.reason)

def describe_securitygroups(self, groupnames=None):
params = {}
if groupnames:
self.build_list_params(params, groupnames, 'GroupName')
response = self.make_request('DescribeSecurityGroups')
body = response.read()
if response.status == 200:
rs = ResultSet('item', SecurityGroup)
h = handler.XmlHandler(rs, self)
xml.sax.parseString(body, h)
return rs
else:
raise S3ResponseError(response.status, response.reason)
Loading

0 comments on commit b8de0fd

Please sign in to comment.