Logo Search packages:      
Sourcecode: s3cmd version File versions  Download package

SimpleDB.py

## Amazon SimpleDB library
## Author: Michal Ludvig <michal@logix.cz>
##         http://www.logix.cz/michal
## License: GPL Version 2

"""
Low-level class for working with Amazon SimpleDB
"""

import time
import urllib
import base64
import hmac
import sha
import httplib
from logging import debug, info, warning, error

from Utils import convertTupleListToDict
from SortedDict import SortedDict
from Exceptions import *

class SimpleDB(object):
      # API Version
      # See http://docs.amazonwebservices.com/AmazonSimpleDB/2007-11-07/DeveloperGuide/
      Version = "2007-11-07"
      SignatureVersion = 1

      def __init__(self, config):
            self.config = config

      ## ------------------------------------------------
      ## Methods implementing SimpleDB API
      ## ------------------------------------------------

      def ListDomains(self, MaxNumberOfDomains = 100):
            '''
            Lists all domains associated with our Access Key. Returns 
            domain names up to the limit set by MaxNumberOfDomains.
            '''
            parameters = SortedDict()
            parameters['MaxNumberOfDomains'] = MaxNumberOfDomains
            return self.send_request("ListDomains", DomainName = None, parameters = parameters)

      def CreateDomain(self, DomainName):
            return self.send_request("CreateDomain", DomainName = DomainName)

      def DeleteDomain(self, DomainName):
            return self.send_request("DeleteDomain", DomainName = DomainName)

      def PutAttributes(self, DomainName, ItemName, Attributes):
            parameters = SortedDict()
            parameters['ItemName'] = ItemName
            seq = 0
            for attrib in Attributes:
                  if type(Attributes[attrib]) == type(list()):
                        for value in Attributes[attrib]:
                              parameters['Attribute.%d.Name' % seq] = attrib
                              parameters['Attribute.%d.Value' % seq] = unicode(value)
                              seq += 1
                  else:
                        parameters['Attribute.%d.Name' % seq] = attrib
                        parameters['Attribute.%d.Value' % seq] = unicode(Attributes[attrib])
                        seq += 1
            ## TODO:
            ## - support for Attribute.N.Replace
            ## - support for multiple values for one attribute
            return self.send_request("PutAttributes", DomainName = DomainName, parameters = parameters)

      def GetAttributes(self, DomainName, ItemName, Attributes = []):
            parameters = SortedDict()
            parameters['ItemName'] = ItemName
            seq = 0
            for attrib in Attributes:
                  parameters['AttributeName.%d' % seq] = attrib
                  seq += 1
            return self.send_request("GetAttributes", DomainName = DomainName, parameters = parameters)

      def DeleteAttributes(self, DomainName, ItemName, Attributes = {}):
            """
            Remove specified Attributes from ItemName.
            Attributes parameter can be either:
            - not specified, in which case the whole Item is removed
            - list, e.g. ['Attr1', 'Attr2'] in which case these parameters are removed
            - dict, e.g. {'Attr' : 'One', 'Attr' : 'Two'} in which case the 
              specified values are removed from multi-value attributes.
            """
            parameters = SortedDict()
            parameters['ItemName'] = ItemName
            seq = 0
            for attrib in Attributes:
                  parameters['Attribute.%d.Name' % seq] = attrib
                  if type(Attributes) == type(dict()):
                        parameters['Attribute.%d.Value' % seq] = unicode(Attributes[attrib])
                  seq += 1
            return self.send_request("DeleteAttributes", DomainName = DomainName, parameters = parameters)

      def Query(self, DomainName, QueryExpression = None, MaxNumberOfItems = None, NextToken = None):
            parameters = SortedDict()
            if QueryExpression:
                  parameters['QueryExpression'] = QueryExpression
            if MaxNumberOfItems:
                  parameters['MaxNumberOfItems'] = MaxNumberOfItems
            if NextToken:
                  parameters['NextToken'] = NextToken
            return self.send_request("Query", DomainName = DomainName, parameters = parameters)
            ## Handle NextToken? Or maybe not - let the upper level do it

      ## ------------------------------------------------
      ## Low-level methods for handling SimpleDB requests
      ## ------------------------------------------------

      def send_request(self, *args, **kwargs):
            request = self.create_request(*args, **kwargs)
            #debug("Request: %s" % repr(request))
            conn = self.get_connection()
            conn.request("GET", self.format_uri(request['uri_params']))
            http_response = conn.getresponse()
            response = {}
            response["status"] = http_response.status
            response["reason"] = http_response.reason
            response["headers"] = convertTupleListToDict(http_response.getheaders())
            response["data"] =  http_response.read()
            conn.close()

            if response["status"] < 200 or response["status"] > 299:
                  debug("Response: " + str(response))
                  raise S3Error(response)

            return response

      def create_request(self, Action, DomainName, parameters = None):
            if not parameters:
                  parameters = SortedDict()
            parameters['AWSAccessKeyId'] = self.config.access_key
            parameters['Version'] = self.Version
            parameters['SignatureVersion'] = self.SignatureVersion
            parameters['Action'] = Action
            parameters['Timestamp'] = time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
            if DomainName:
                  parameters['DomainName'] = DomainName
            parameters['Signature'] = self.sign_request(parameters)
            parameters.keys_return_lowercase = False
            uri_params = urllib.urlencode(parameters)
            request = {}
            request['uri_params'] = uri_params
            request['parameters'] = parameters
            return request

      def sign_request(self, parameters):
            h = ""
            parameters.keys_sort_lowercase = True
            parameters.keys_return_lowercase = False
            for key in parameters:
                  h += "%s%s" % (key, parameters[key])
            #debug("SignRequest: %s" % h)
            return base64.encodestring(hmac.new(self.config.secret_key, h, sha).digest()).strip()

      def get_connection(self):
            if self.config.proxy_host != "":
                  return httplib.HTTPConnection(self.config.proxy_host, self.config.proxy_port)
            else:
                  if self.config.use_https:
                        return httplib.HTTPSConnection(self.config.simpledb_host)
                  else:
                        return httplib.HTTPConnection(self.config.simpledb_host)

      def format_uri(self, uri_params):
            if self.config.proxy_host != "":
                  uri = "http://%s/?%s" % (self.config.simpledb_host, uri_params)
            else:
                  uri = "/?%s" % uri_params
            #debug('format_uri(): ' + uri)
            return uri

Generated by  Doxygen 1.6.0   Back to index