Implemented https://trello.com/c/LLwK80P5 - Added eu-central-1 region and updated boto to 2.34.0.

This commit is contained in:
Chris Church
2014-10-23 13:35:53 -04:00
parent 253a6cffd4
commit 7834bfab70
117 changed files with 8035 additions and 7062 deletions

View File

@@ -7,7 +7,7 @@ anyjson==0.3.3 (anyjson/*)
argparse==1.2.1 (argparse.py, needed for Python 2.6 support) argparse==1.2.1 (argparse.py, needed for Python 2.6 support)
Babel==1.3 (babel/*, excluded bin/pybabel) Babel==1.3 (babel/*, excluded bin/pybabel)
billiard==3.3.0.16 (billiard/*, funtests/*, excluded _billiard.so) billiard==3.3.0.16 (billiard/*, funtests/*, excluded _billiard.so)
boto==2.32.1 (boto/*, excluded bin/asadmin, bin/bundle_image, bin/cfadmin, boto==2.34.0 (boto/*, excluded bin/asadmin, bin/bundle_image, bin/cfadmin,
bin/cq, bin/cwutil, bin/dynamodb_dump, bin/dynamodb_load, bin/elbadmin, bin/cq, bin/cwutil, bin/dynamodb_dump, bin/dynamodb_load, bin/elbadmin,
bin/fetch_file, bin/glacier, bin/instance_events, bin/kill_instance, bin/fetch_file, bin/glacier, bin/instance_events, bin/kill_instance,
bin/launch_instance, bin/list_instances, bin/lss3, bin/mturk, bin/launch_instance, bin/list_instances, bin/lss3, bin/mturk,

View File

@@ -38,7 +38,7 @@ import logging.config
from boto.compat import urlparse from boto.compat import urlparse
from boto.exception import InvalidUriError from boto.exception import InvalidUriError
__version__ = '2.32.1' __version__ = '2.34.0'
Version = __version__ # for backware compatibility Version = __version__ # for backware compatibility
# http://bugs.python.org/issue7980 # http://bugs.python.org/issue7980
@@ -858,6 +858,76 @@ def connect_logs(aws_access_key_id=None,
**kwargs **kwargs
) )
def connect_route53domains(aws_access_key_id=None,
aws_secret_access_key=None,
**kwargs):
"""
Connect to Amazon Route 53 Domains
:type aws_access_key_id: string
:param aws_access_key_id: Your AWS Access Key ID
:type aws_secret_access_key: string
:param aws_secret_access_key: Your AWS Secret Access Key
rtype: :class:`boto.route53.domains.layer1.Route53DomainsConnection`
:return: A connection to the Amazon Route 53 Domains service
"""
from boto.route53.domains.layer1 import Route53DomainsConnection
return Route53DomainsConnection(
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
**kwargs
)
def connect_cognito_identity(aws_access_key_id=None,
aws_secret_access_key=None,
**kwargs):
"""
Connect to Amazon Cognito Identity
:type aws_access_key_id: string
:param aws_access_key_id: Your AWS Access Key ID
:type aws_secret_access_key: string
:param aws_secret_access_key: Your AWS Secret Access Key
rtype: :class:`boto.cognito.identity.layer1.CognitoIdentityConnection`
:return: A connection to the Amazon Cognito Identity service
"""
from boto.cognito.identity.layer1 import CognitoIdentityConnection
return CognitoIdentityConnection(
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
**kwargs
)
def connect_cognito_sync(aws_access_key_id=None,
aws_secret_access_key=None,
**kwargs):
"""
Connect to Amazon Cognito Sync
:type aws_access_key_id: string
:param aws_access_key_id: Your AWS Access Key ID
:type aws_secret_access_key: string
:param aws_secret_access_key: Your AWS Secret Access Key
rtype: :class:`boto.cognito.sync.layer1.CognitoSyncConnection`
:return: A connection to the Amazon Cognito Sync service
"""
from boto.cognito.sync.layer1 import CognitoSyncConnection
return CognitoSyncConnection(
aws_access_key_id=aws_access_key_id,
aws_secret_access_key=aws_secret_access_key,
**kwargs
)
def storage_uri(uri_str, default_scheme='file', debug=0, validate=True, def storage_uri(uri_str, default_scheme='file', debug=0, validate=True,
bucket_storage_uri_class=BucketStorageUri, bucket_storage_uri_class=BucketStorageUri,
suppress_consec_slashes=True, is_latest=False): suppress_consec_slashes=True, is_latest=False):

View File

@@ -37,8 +37,6 @@ import datetime
from email.utils import formatdate from email.utils import formatdate
import hmac import hmac
import os import os
import sys
import time
import posixpath import posixpath
from boto.compat import urllib, encodebytes from boto.compat import urllib, encodebytes
@@ -317,6 +315,8 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
for name, value in http_request.headers.items(): for name, value in http_request.headers.items():
lname = name.lower() lname = name.lower()
if lname.startswith('x-amz'): if lname.startswith('x-amz'):
if isinstance(value, bytes):
value = value.decode('utf-8')
headers_to_sign[name] = value headers_to_sign[name] = value
return headers_to_sign return headers_to_sign
@@ -345,7 +345,7 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
for param in sorted(http_request.params): for param in sorted(http_request.params):
value = boto.utils.get_utf8_value(http_request.params[param]) value = boto.utils.get_utf8_value(http_request.params[param])
l.append('%s=%s' % (urllib.parse.quote(param, safe='-_.~'), l.append('%s=%s' % (urllib.parse.quote(param, safe='-_.~'),
urllib.parse.quote(value.decode('utf-8'), safe='-_.~'))) urllib.parse.quote(value, safe='-_.~')))
return '&'.join(l) return '&'.join(l)
def canonical_headers(self, headers_to_sign): def canonical_headers(self, headers_to_sign):
@@ -376,7 +376,7 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
path = http_request.auth_path path = http_request.auth_path
# Normalize the path # Normalize the path
# in windows normpath('/') will be '\\' so we chane it back to '/' # in windows normpath('/') will be '\\' so we chane it back to '/'
normalized = posixpath.normpath(path).replace('\\','/') normalized = posixpath.normpath(path).replace('\\', '/')
# Then urlencode whatever's left. # Then urlencode whatever's left.
encoded = urllib.parse.quote(normalized) encoded = urllib.parse.quote(normalized)
if len(path) > 1 and path.endswith('/'): if len(path) > 1 and path.endswith('/'):
@@ -472,7 +472,7 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
def signature(self, http_request, string_to_sign): def signature(self, http_request, string_to_sign):
key = self._provider.secret_key key = self._provider.secret_key
k_date = self._sign(('AWS4' + key).encode('utf-8'), k_date = self._sign(('AWS4' + key).encode('utf-8'),
http_request.timestamp) http_request.timestamp)
k_region = self._sign(k_date, http_request.region_name) k_region = self._sign(k_date, http_request.region_name)
k_service = self._sign(k_region, http_request.service_name) k_service = self._sign(k_region, http_request.service_name)
k_signing = self._sign(k_service, 'aws4_request') k_signing = self._sign(k_service, 'aws4_request')
@@ -494,10 +494,25 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
if self._provider.security_token: if self._provider.security_token:
req.headers['X-Amz-Security-Token'] = self._provider.security_token req.headers['X-Amz-Security-Token'] = self._provider.security_token
qs = self.query_string(req) qs = self.query_string(req)
if qs and req.method == 'POST':
qs_to_post = qs
# We do not want to include any params that were mangled into
# the params if performing s3-sigv4 since it does not
# belong in the body of a post for some requests. Mangled
# refers to items in the query string URL being added to the
# http response params. However, these params get added to
# the body of the request, but the query string URL does not
# belong in the body of the request. ``unmangled_resp`` is the
# response that happened prior to the mangling. This ``unmangled_req``
# kwarg will only appear for s3-sigv4.
if 'unmangled_req' in kwargs:
qs_to_post = self.query_string(kwargs['unmangled_req'])
if qs_to_post and req.method == 'POST':
# Stash request parameters into post body # Stash request parameters into post body
# before we generate the signature. # before we generate the signature.
req.body = qs req.body = qs_to_post
req.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8' req.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=UTF-8'
req.headers['Content-Length'] = str(len(req.body)) req.headers['Content-Length'] = str(len(req.body))
else: else:
@@ -549,6 +564,17 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
encoded = urllib.parse.quote(unquoted) encoded = urllib.parse.quote(unquoted)
return encoded return encoded
def canonical_query_string(self, http_request):
# Note that we just do not return an empty string for
# POST request. Query strings in url are included in canonical
# query string.
l = []
for param in sorted(http_request.params):
value = boto.utils.get_utf8_value(http_request.params[param])
l.append('%s=%s' % (urllib.parse.quote(param, safe='-_.~'),
urllib.parse.quote(value, safe='-_.~')))
return '&'.join(l)
def host_header(self, host, http_request): def host_header(self, host, http_request):
port = http_request.port port = http_request.port
secure = http_request.protocol == 'https' secure = http_request.protocol == 'https'
@@ -568,7 +594,7 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
# Hooray for the only difference! The main SigV4 signer only does # Hooray for the only difference! The main SigV4 signer only does
# ``Host`` + ``x-amz-*``. But S3 wants pretty much everything # ``Host`` + ``x-amz-*``. But S3 wants pretty much everything
# signed, except for authorization itself. # signed, except for authorization itself.
if not lname in ['authorization']: if lname not in ['authorization']:
headers_to_sign[name] = value headers_to_sign[name] = value
return headers_to_sign return headers_to_sign
@@ -581,8 +607,8 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
# - s3-us-west-2.amazonaws.com (Specific region) # - s3-us-west-2.amazonaws.com (Specific region)
# - bukkit.s3.amazonaws.com (Vhosted Classic) # - bukkit.s3.amazonaws.com (Vhosted Classic)
# - bukkit.s3-ap-northeast-1.amazonaws.com (Vhosted specific region) # - bukkit.s3-ap-northeast-1.amazonaws.com (Vhosted specific region)
# - s3.cn-north-1.amazonaws.com.cn - (Bejing region) # - s3.cn-north-1.amazonaws.com.cn - (Beijing region)
# - bukkit.s3.cn-north-1.amazonaws.com.cn - (Vhosted Bejing region) # - bukkit.s3.cn-north-1.amazonaws.com.cn - (Vhosted Beijing region)
parts = self.split_host_parts(host) parts = self.split_host_parts(host)
if self.region_name is not None: if self.region_name is not None:
@@ -641,6 +667,13 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
if modified_req.params is None: if modified_req.params is None:
modified_req.params = {} modified_req.params = {}
else:
# To keep the original request object untouched. We must make
# a copy of the params dictionary. Because the copy of the
# original request directly refers to the params dictionary
# of the original request.
copy_params = req.params.copy()
modified_req.params = copy_params
raw_qs = parsed_path.query raw_qs = parsed_path.query
existing_qs = urllib.parse.parse_qs( existing_qs = urllib.parse.parse_qs(
@@ -665,14 +698,15 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
return super(S3HmacAuthV4Handler, self).payload(http_request) return super(S3HmacAuthV4Handler, self).payload(http_request)
def add_auth(self, req, **kwargs): def add_auth(self, req, **kwargs):
if not 'x-amz-content-sha256' in req.headers: if 'x-amz-content-sha256' not in req.headers:
if '_sha256' in req.headers: if '_sha256' in req.headers:
req.headers['x-amz-content-sha256'] = req.headers.pop('_sha256') req.headers['x-amz-content-sha256'] = req.headers.pop('_sha256')
else: else:
req.headers['x-amz-content-sha256'] = self.payload(req) req.headers['x-amz-content-sha256'] = self.payload(req)
updated_req = self.mangle_path_and_params(req)
req = self.mangle_path_and_params(req) return super(S3HmacAuthV4Handler, self).add_auth(updated_req,
return super(S3HmacAuthV4Handler, self).add_auth(req, **kwargs) unmangled_req=req,
**kwargs)
def presign(self, req, expires, iso_date=None): def presign(self, req, expires, iso_date=None):
""" """
@@ -703,6 +737,10 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
if self._provider.security_token: if self._provider.security_token:
params['X-Amz-Security-Token'] = self._provider.security_token params['X-Amz-Security-Token'] = self._provider.security_token
headers_to_sign = self.headers_to_sign(req)
l = sorted(['%s' % n.lower().strip() for n in headers_to_sign])
params['X-Amz-SignedHeaders'] = ';'.join(l)
req.params.update(params) req.params.update(params)
cr = self.canonical_request(req) cr = self.canonical_request(req)
@@ -749,7 +787,6 @@ class QueryAuthHandler(AuthHandler):
def add_auth(self, http_request, **kwargs): def add_auth(self, http_request, **kwargs):
headers = http_request.headers headers = http_request.headers
params = http_request.params
qs = self._build_query_string( qs = self._build_query_string(
http_request.params http_request.params
) )
@@ -897,7 +934,7 @@ class POSTPathQSV2AuthHandler(QuerySignatureV2AuthHandler, AuthHandler):
# already be there, we need to get rid of that and rebuild it # already be there, we need to get rid of that and rebuild it
req.path = req.path.split('?')[0] req.path = req.path.split('?')[0]
req.path = (req.path + '?' + qs + req.path = (req.path + '?' + qs +
'&Signature=' + urllib.parse.quote_plus(signature)) '&Signature=' + urllib.parse.quote_plus(signature))
def get_auth_handler(host, config, provider, requested_capability=None): def get_auth_handler(host, config, provider, requested_capability=None):
@@ -923,7 +960,6 @@ def get_auth_handler(host, config, provider, requested_capability=None):
""" """
ready_handlers = [] ready_handlers = []
auth_handlers = boto.plugin.get_plugin(AuthHandler, requested_capability) auth_handlers = boto.plugin.get_plugin(AuthHandler, requested_capability)
total_handlers = len(auth_handlers)
for handler in auth_handlers: for handler in auth_handlers:
try: try:
ready_handlers.append(handler(host, config, provider)) ready_handlers.append(handler(host, config, provider))
@@ -934,9 +970,9 @@ def get_auth_handler(host, config, provider, requested_capability=None):
checked_handlers = auth_handlers checked_handlers = auth_handlers
names = [handler.__name__ for handler in checked_handlers] names = [handler.__name__ for handler in checked_handlers]
raise boto.exception.NoAuthHandlerFound( raise boto.exception.NoAuthHandlerFound(
'No handler was ready to authenticate. %d handlers were checked.' 'No handler was ready to authenticate. %d handlers were checked.'
' %s ' ' %s '
'Check your credentials' % (len(names), str(names))) 'Check your credentials' % (len(names), str(names)))
# We select the last ready auth handler that was loaded, to allow users to # We select the last ready auth handler that was loaded, to allow users to
# customize how auth works in environments where there are shared boto # customize how auth works in environments where there are shared boto
@@ -964,7 +1000,8 @@ def detect_potential_sigv4(func):
# ``boto/iam/connection.py``, as several things there are also # ``boto/iam/connection.py``, as several things there are also
# endpoint-related. # endpoint-related.
if getattr(self.region, 'endpoint', ''): if getattr(self.region, 'endpoint', ''):
if '.cn-' in self.region.endpoint: if '.cn-' in self.region.endpoint or \
'.eu-central' in self.region.endpoint:
return ['hmac-v4'] return ['hmac-v4']
return func(self) return func(self)
@@ -983,7 +1020,7 @@ def detect_potential_s3sigv4(func):
# If you're making changes here, you should also check # If you're making changes here, you should also check
# ``boto/iam/connection.py``, as several things there are also # ``boto/iam/connection.py``, as several things there are also
# endpoint-related. # endpoint-related.
if '.cn-' in self.host: if '.cn-' in self.host or '.eu-central' in self.host:
return ['hmac-v4-s3'] return ['hmac-v4-s3']
return func(self) return func(self)

View File

@@ -14,7 +14,7 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
@@ -25,8 +25,10 @@ Defines an interface which all Auth handlers need to implement.
from boto.plugin import Plugin from boto.plugin import Plugin
class NotReadyToAuthenticate(Exception): class NotReadyToAuthenticate(Exception):
pass pass
class AuthHandler(Plugin): class AuthHandler(Plugin):
@@ -37,10 +39,10 @@ class AuthHandler(Plugin):
:type host: string :type host: string
:param host: The host to which the request is being sent. :param host: The host to which the request is being sent.
:type config: boto.pyami.Config :type config: boto.pyami.Config
:param config: Boto configuration. :param config: Boto configuration.
:type provider: boto.provider.Provider :type provider: boto.provider.Provider
:param provider: Provider details. :param provider: Provider details.
Raises: Raises:

File diff suppressed because it is too large Load Diff

View File

@@ -1,773 +0,0 @@
# Copyright (c) 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
try:
import json
except ImportError:
import simplejson as json
import boto
from boto.connection import AWSQueryConnection
from boto.regioninfo import RegionInfo
from boto.exception import JSONResponseError
from boto.cloudformation import exceptions
class CloudFormationConnection(AWSQueryConnection):
"""
AWS CloudFormation
AWS CloudFormation enables you to create and manage AWS
infrastructure deployments predictably and repeatedly. AWS
CloudFormation helps you leverage AWS products such as Amazon EC2,
EBS, Amazon SNS, ELB, and Auto Scaling to build highly-reliable,
highly scalable, cost effective applications without worrying
about creating and configuring the underlying AWS infrastructure.
With AWS CloudFormation, you declare all of your resources and
dependencies in a template file. The template defines a collection
of resources as a single unit called a stack. AWS CloudFormation
creates and deletes all member resources of the stack together and
manages all dependencies between the resources for you.
For more information about this product, go to the `CloudFormation
Product Page`_.
Amazon CloudFormation makes use of other AWS products. If you need
additional technical information about a specific AWS product, you
can find the product's technical documentation at
`http://aws.amazon.com/documentation/`_.
"""
APIVersion = "2010-05-15"
DefaultRegionName = "us-east-1"
DefaultRegionEndpoint = "cloudformation.us-east-1.amazonaws.com"
ResponseError = JSONResponseError
_faults = {
"AlreadyExistsException": exceptions.AlreadyExistsException,
"InsufficientCapabilitiesException": exceptions.InsufficientCapabilitiesException,
"LimitExceededException": exceptions.LimitExceededException,
}
def __init__(self, **kwargs):
region = kwargs.pop('region', None)
if not region:
region = RegionInfo(self, self.DefaultRegionName,
self.DefaultRegionEndpoint)
if 'host' not in kwargs:
kwargs['host'] = region.endpoint
super(CloudFormationConnection, self).__init__(**kwargs)
self.region = region
def _required_auth_capability(self):
return ['hmac-v4']
def cancel_update_stack(self, stack_name):
"""
Cancels an update on the specified stack. If the call
completes successfully, the stack will roll back the update
and revert to the previous stack configuration.
Only stacks that are in the UPDATE_IN_PROGRESS state can be
canceled.
:type stack_name: string
:param stack_name: The name or the unique identifier associated with
the stack.
"""
params = {'StackName': stack_name, }
return self._make_request(
action='CancelUpdateStack',
verb='POST',
path='/', params=params)
def create_stack(self, stack_name, template_body=None, template_url=None,
parameters=None, disable_rollback=None,
timeout_in_minutes=None, notification_arns=None,
capabilities=None, on_failure=None,
stack_policy_body=None, stack_policy_url=None,
tags=None):
"""
Creates a stack as specified in the template. After the call
completes successfully, the stack creation starts. You can
check the status of the stack via the DescribeStacks API.
Currently, the limit for stacks is 20 stacks per account per
region.
:type stack_name: string
:param stack_name:
The name associated with the stack. The name must be unique within your
AWS account.
Must contain only alphanumeric characters (case sensitive) and start
with an alpha character. Maximum length of the name is 255
characters.
:type template_body: string
:param template_body: Structure containing the template body. (For more
information, go to `Template Anatomy`_ in the AWS CloudFormation
User Guide.)
Conditional: You must pass `TemplateBody` or `TemplateURL`. If both are
passed, only `TemplateBody` is used.
:type template_url: string
:param template_url: Location of file containing the template body. The
URL must point to a template (max size: 307,200 bytes) located in
an S3 bucket in the same region as the stack. For more information,
go to the `Template Anatomy`_ in the AWS CloudFormation User Guide.
Conditional: You must pass `TemplateURL` or `TemplateBody`. If both are
passed, only `TemplateBody` is used.
:type parameters: list
:param parameters: A list of `Parameter` structures that specify input
parameters for the stack.
:type disable_rollback: boolean
:param disable_rollback: Set to `True` to disable rollback of the stack
if stack creation failed. You can specify either `DisableRollback`
or `OnFailure`, but not both.
Default: `False`
:type timeout_in_minutes: integer
:param timeout_in_minutes: The amount of time that can pass before the
stack status becomes CREATE_FAILED; if `DisableRollback` is not set
or is set to `False`, the stack will be rolled back.
:type notification_arns: list
:param notification_arns: The Simple Notification Service (SNS) topic
ARNs to publish stack related events. You can find your SNS topic
ARNs using the `SNS console`_ or your Command Line Interface (CLI).
:type capabilities: list
:param capabilities: The list of capabilities that you want to allow in
the stack. If your template contains certain resources, you must
specify the CAPABILITY_IAM value for this parameter; otherwise,
this action returns an InsufficientCapabilities error. The
following resources require you to specify the capabilities
parameter: `AWS::CloudFormation::Stack`_, `AWS::IAM::AccessKey`_,
`AWS::IAM::Group`_, `AWS::IAM::InstanceProfile`_,
`AWS::IAM::Policy`_, `AWS::IAM::Role`_, `AWS::IAM::User`_, and
`AWS::IAM::UserToGroupAddition`_.
:type on_failure: string
:param on_failure: Determines what action will be taken if stack
creation fails. This must be one of: DO_NOTHING, ROLLBACK, or
DELETE. You can specify either `OnFailure` or `DisableRollback`,
but not both.
Default: `ROLLBACK`
:type stack_policy_body: string
:param stack_policy_body: Structure containing the stack policy body.
(For more information, go to ` Prevent Updates to Stack Resources`_
in the AWS CloudFormation User Guide.)
If you pass `StackPolicyBody` and `StackPolicyURL`, only
`StackPolicyBody` is used.
:type stack_policy_url: string
:param stack_policy_url: Location of a file containing the stack
policy. The URL must point to a policy (max size: 16KB) located in
an S3 bucket in the same region as the stack. If you pass
`StackPolicyBody` and `StackPolicyURL`, only `StackPolicyBody` is
used.
:type tags: list
:param tags: A set of user-defined `Tags` to associate with this stack,
represented by key/value pairs. Tags defined for the stack are
propagated to EC2 resources that are created as part of the stack.
A maximum number of 10 tags can be specified.
"""
params = {'StackName': stack_name, }
if template_body is not None:
params['TemplateBody'] = template_body
if template_url is not None:
params['TemplateURL'] = template_url
if parameters is not None:
self.build_complex_list_params(
params, parameters,
'Parameters.member',
('ParameterKey', 'ParameterValue'))
if disable_rollback is not None:
params['DisableRollback'] = str(
disable_rollback).lower()
if timeout_in_minutes is not None:
params['TimeoutInMinutes'] = timeout_in_minutes
if notification_arns is not None:
self.build_list_params(params,
notification_arns,
'NotificationARNs.member')
if capabilities is not None:
self.build_list_params(params,
capabilities,
'Capabilities.member')
if on_failure is not None:
params['OnFailure'] = on_failure
if stack_policy_body is not None:
params['StackPolicyBody'] = stack_policy_body
if stack_policy_url is not None:
params['StackPolicyURL'] = stack_policy_url
if tags is not None:
self.build_complex_list_params(
params, tags,
'Tags.member',
('Key', 'Value'))
return self._make_request(
action='CreateStack',
verb='POST',
path='/', params=params)
def delete_stack(self, stack_name):
"""
Deletes a specified stack. Once the call completes
successfully, stack deletion starts. Deleted stacks do not
show up in the DescribeStacks API if the deletion has been
completed successfully.
:type stack_name: string
:param stack_name: The name or the unique identifier associated with
the stack.
"""
params = {'StackName': stack_name, }
return self._make_request(
action='DeleteStack',
verb='POST',
path='/', params=params)
def describe_stack_events(self, stack_name=None, next_token=None):
"""
Returns all stack related events for a specified stack. For
more information about a stack's event history, go to
`Stacks`_ in the AWS CloudFormation User Guide.
Events are returned, even if the stack never existed or has
been successfully deleted.
:type stack_name: string
:param stack_name: The name or the unique identifier associated with
the stack.
Default: There is no default value.
:type next_token: string
:param next_token: String that identifies the start of the next list of
events, if there is one.
Default: There is no default value.
"""
params = {}
if stack_name is not None:
params['StackName'] = stack_name
if next_token is not None:
params['NextToken'] = next_token
return self._make_request(
action='DescribeStackEvents',
verb='POST',
path='/', params=params)
def describe_stack_resource(self, stack_name, logical_resource_id):
"""
Returns a description of the specified resource in the
specified stack.
For deleted stacks, DescribeStackResource returns resource
information for up to 90 days after the stack has been
deleted.
:type stack_name: string
:param stack_name: The name or the unique identifier associated with
the stack.
Default: There is no default value.
:type logical_resource_id: string
:param logical_resource_id: The logical name of the resource as
specified in the template.
Default: There is no default value.
"""
params = {
'StackName': stack_name,
'LogicalResourceId': logical_resource_id,
}
return self._make_request(
action='DescribeStackResource',
verb='POST',
path='/', params=params)
def describe_stack_resources(self, stack_name=None,
logical_resource_id=None,
physical_resource_id=None):
"""
Returns AWS resource descriptions for running and deleted
stacks. If `StackName` is specified, all the associated
resources that are part of the stack are returned. If
`PhysicalResourceId` is specified, the associated resources of
the stack that the resource belongs to are returned.
Only the first 100 resources will be returned. If your stack
has more resources than this, you should use
`ListStackResources` instead.
For deleted stacks, `DescribeStackResources` returns resource
information for up to 90 days after the stack has been
deleted.
You must specify either `StackName` or `PhysicalResourceId`,
but not both. In addition, you can specify `LogicalResourceId`
to filter the returned result. For more information about
resources, the `LogicalResourceId` and `PhysicalResourceId`,
go to the `AWS CloudFormation User Guide`_.
A `ValidationError` is returned if you specify both
`StackName` and `PhysicalResourceId` in the same request.
:type stack_name: string
:param stack_name: The name or the unique identifier associated with
the stack.
Required: Conditional. If you do not specify `StackName`, you must
specify `PhysicalResourceId`.
Default: There is no default value.
:type logical_resource_id: string
:param logical_resource_id: The logical name of the resource as
specified in the template.
Default: There is no default value.
:type physical_resource_id: string
:param physical_resource_id: The name or unique identifier that
corresponds to a physical instance ID of a resource supported by
AWS CloudFormation.
For example, for an Amazon Elastic Compute Cloud (EC2) instance,
`PhysicalResourceId` corresponds to the `InstanceId`. You can pass
the EC2 `InstanceId` to `DescribeStackResources` to find which
stack the instance belongs to and what other resources are part of
the stack.
Required: Conditional. If you do not specify `PhysicalResourceId`, you
must specify `StackName`.
Default: There is no default value.
"""
params = {}
if stack_name is not None:
params['StackName'] = stack_name
if logical_resource_id is not None:
params['LogicalResourceId'] = logical_resource_id
if physical_resource_id is not None:
params['PhysicalResourceId'] = physical_resource_id
return self._make_request(
action='DescribeStackResources',
verb='POST',
path='/', params=params)
def describe_stacks(self, stack_name=None, next_token=None):
"""
Returns the description for the specified stack; if no stack
name was specified, then it returns the description for all
the stacks created.
:type stack_name: string
:param stack_name: The name or the unique identifier associated with
the stack.
Default: There is no default value.
:type next_token: string
:param next_token: String that identifies the start of the next list of
stacks, if there is one.
"""
params = {}
if stack_name is not None:
params['StackName'] = stack_name
if next_token is not None:
params['NextToken'] = next_token
return self._make_request(
action='DescribeStacks',
verb='POST',
path='/', params=params)
def estimate_template_cost(self, template_body=None, template_url=None,
parameters=None):
"""
Returns the estimated monthly cost of a template. The return
value is an AWS Simple Monthly Calculator URL with a query
string that describes the resources required to run the
template.
:type template_body: string
:param template_body: Structure containing the template body. (For more
information, go to `Template Anatomy`_ in the AWS CloudFormation
User Guide.)
Conditional: You must pass `TemplateBody` or `TemplateURL`. If both are
passed, only `TemplateBody` is used.
:type template_url: string
:param template_url: Location of file containing the template body. The
URL must point to a template located in an S3 bucket in the same
region as the stack. For more information, go to `Template
Anatomy`_ in the AWS CloudFormation User Guide.
Conditional: You must pass `TemplateURL` or `TemplateBody`. If both are
passed, only `TemplateBody` is used.
:type parameters: list
:param parameters: A list of `Parameter` structures that specify input
parameters.
"""
params = {}
if template_body is not None:
params['TemplateBody'] = template_body
if template_url is not None:
params['TemplateURL'] = template_url
if parameters is not None:
self.build_complex_list_params(
params, parameters,
'Parameters.member',
('ParameterKey', 'ParameterValue'))
return self._make_request(
action='EstimateTemplateCost',
verb='POST',
path='/', params=params)
def get_stack_policy(self, stack_name):
"""
Returns the stack policy for a specified stack. If a stack
doesn't have a policy, a null value is returned.
:type stack_name: string
:param stack_name: The name or stack ID that is associated with the
stack whose policy you want to get.
"""
params = {'StackName': stack_name, }
return self._make_request(
action='GetStackPolicy',
verb='POST',
path='/', params=params)
def get_template(self, stack_name):
"""
Returns the template body for a specified stack. You can get
the template for running or deleted stacks.
For deleted stacks, GetTemplate returns the template for up to
90 days after the stack has been deleted.
If the template does not exist, a `ValidationError` is
returned.
:type stack_name: string
:param stack_name: The name or the unique identifier associated with
the stack, which are not always interchangeable:
+ Running stacks: You can specify either the stack's name or its unique
stack ID.
+ Deleted stacks: You must specify the unique stack ID.
Default: There is no default value.
"""
params = {'StackName': stack_name, }
return self._make_request(
action='GetTemplate',
verb='POST',
path='/', params=params)
def list_stack_resources(self, stack_name, next_token=None):
"""
Returns descriptions of all resources of the specified stack.
For deleted stacks, ListStackResources returns resource
information for up to 90 days after the stack has been
deleted.
:type stack_name: string
:param stack_name: The name or the unique identifier associated with
the stack, which are not always interchangeable:
+ Running stacks: You can specify either the stack's name or its unique
stack ID.
+ Deleted stacks: You must specify the unique stack ID.
Default: There is no default value.
:type next_token: string
:param next_token: String that identifies the start of the next list of
stack resource summaries, if there is one.
Default: There is no default value.
"""
params = {'StackName': stack_name, }
if next_token is not None:
params['NextToken'] = next_token
return self._make_request(
action='ListStackResources',
verb='POST',
path='/', params=params)
def list_stacks(self, next_token=None, stack_status_filter=None):
"""
Returns the summary information for stacks whose status
matches the specified StackStatusFilter. Summary information
for stacks that have been deleted is kept for 90 days after
the stack is deleted. If no StackStatusFilter is specified,
summary information for all stacks is returned (including
existing stacks and stacks that have been deleted).
:type next_token: string
:param next_token: String that identifies the start of the next list of
stacks, if there is one.
Default: There is no default value.
:type stack_status_filter: list
:param stack_status_filter: Stack status to use as a filter. Specify
one or more stack status codes to list only stacks with the
specified status codes. For a complete list of stack status codes,
see the `StackStatus` parameter of the Stack data type.
"""
params = {}
if next_token is not None:
params['NextToken'] = next_token
if stack_status_filter is not None:
self.build_list_params(params,
stack_status_filter,
'StackStatusFilter.member')
return self._make_request(
action='ListStacks',
verb='POST',
path='/', params=params)
def set_stack_policy(self, stack_name, stack_policy_body=None,
stack_policy_url=None):
"""
Sets a stack policy for a specified stack.
:type stack_name: string
:param stack_name: The name or stack ID that you want to associate a
policy with.
:type stack_policy_body: string
:param stack_policy_body: Structure containing the stack policy body.
(For more information, go to ` Prevent Updates to Stack Resources`_
in the AWS CloudFormation User Guide.)
You must pass `StackPolicyBody` or `StackPolicyURL`. If both are
passed, only `StackPolicyBody` is used.
:type stack_policy_url: string
:param stack_policy_url: Location of a file containing the stack
policy. The URL must point to a policy (max size: 16KB) located in
an S3 bucket in the same region as the stack. You must pass
`StackPolicyBody` or `StackPolicyURL`. If both are passed, only
`StackPolicyBody` is used.
"""
params = {'StackName': stack_name, }
if stack_policy_body is not None:
params['StackPolicyBody'] = stack_policy_body
if stack_policy_url is not None:
params['StackPolicyURL'] = stack_policy_url
return self._make_request(
action='SetStackPolicy',
verb='POST',
path='/', params=params)
def update_stack(self, stack_name, template_body=None, template_url=None,
stack_policy_during_update_body=None,
stack_policy_during_update_url=None, parameters=None,
capabilities=None, stack_policy_body=None,
stack_policy_url=None):
"""
Updates a stack as specified in the template. After the call
completes successfully, the stack update starts. You can check
the status of the stack via the DescribeStacks action.
**Note: **You cannot update `AWS::S3::Bucket`_ resources, for
example, to add or modify tags.
To get a copy of the template for an existing stack, you can
use the GetTemplate action.
Tags that were associated with this stack during creation time
will still be associated with the stack after an `UpdateStack`
operation.
For more information about creating an update template,
updating a stack, and monitoring the progress of the update,
see `Updating a Stack`_.
:type stack_name: string
:param stack_name:
The name or stack ID of the stack to update.
Must contain only alphanumeric characters (case sensitive) and start
with an alpha character. Maximum length of the name is 255
characters.
:type template_body: string
:param template_body: Structure containing the template body. (For more
information, go to `Template Anatomy`_ in the AWS CloudFormation
User Guide.)
Conditional: You must pass `TemplateBody` or `TemplateURL`. If both are
passed, only `TemplateBody` is used.
:type template_url: string
:param template_url: Location of file containing the template body. The
URL must point to a template located in an S3 bucket in the same
region as the stack. For more information, go to `Template
Anatomy`_ in the AWS CloudFormation User Guide.
Conditional: You must pass `TemplateURL` or `TemplateBody`. If both are
passed, only `TemplateBody` is used.
:type stack_policy_during_update_body: string
:param stack_policy_during_update_body: Structure containing the
temporary overriding stack policy body. If you pass
`StackPolicyDuringUpdateBody` and `StackPolicyDuringUpdateURL`,
only `StackPolicyDuringUpdateBody` is used.
If you want to update protected resources, specify a temporary
overriding stack policy during this update. If you do not specify a
stack policy, the current policy that associated with the stack
will be used.
:type stack_policy_during_update_url: string
:param stack_policy_during_update_url: Location of a file containing
the temporary overriding stack policy. The URL must point to a
policy (max size: 16KB) located in an S3 bucket in the same region
as the stack. If you pass `StackPolicyDuringUpdateBody` and
`StackPolicyDuringUpdateURL`, only `StackPolicyDuringUpdateBody` is
used.
If you want to update protected resources, specify a temporary
overriding stack policy during this update. If you do not specify a
stack policy, the current policy that is associated with the stack
will be used.
:type parameters: list
:param parameters: A list of `Parameter` structures that specify input
parameters for the stack.
:type capabilities: list
:param capabilities: The list of capabilities that you want to allow in
the stack. If your stack contains IAM resources, you must specify
the CAPABILITY_IAM value for this parameter; otherwise, this action
returns an InsufficientCapabilities error. IAM resources are the
following: `AWS::IAM::AccessKey`_, `AWS::IAM::Group`_,
`AWS::IAM::Policy`_, `AWS::IAM::User`_, and
`AWS::IAM::UserToGroupAddition`_.
:type stack_policy_body: string
:param stack_policy_body: Structure containing the updated stack policy
body. If you pass `StackPolicyBody` and `StackPolicyURL`, only
`StackPolicyBody` is used.
If you want to update a stack policy during a stack update, specify an
updated stack policy. For example, you can include an updated stack
policy to protect a new resource created in the stack update. If
you do not specify a stack policy, the current policy that is
associated with the stack is unchanged.
:type stack_policy_url: string
:param stack_policy_url: Location of a file containing the updated
stack policy. The URL must point to a policy (max size: 16KB)
located in an S3 bucket in the same region as the stack. If you
pass `StackPolicyBody` and `StackPolicyURL`, only `StackPolicyBody`
is used.
If you want to update a stack policy during a stack update, specify an
updated stack policy. For example, you can include an updated stack
policy to protect a new resource created in the stack update. If
you do not specify a stack policy, the current policy that is
associated with the stack is unchanged.
"""
params = {'StackName': stack_name, }
if template_body is not None:
params['TemplateBody'] = template_body
if template_url is not None:
params['TemplateURL'] = template_url
if stack_policy_during_update_body is not None:
params['StackPolicyDuringUpdateBody'] = stack_policy_during_update_body
if stack_policy_during_update_url is not None:
params['StackPolicyDuringUpdateURL'] = stack_policy_during_update_url
if parameters is not None:
self.build_complex_list_params(
params, parameters,
'Parameters.member',
('ParameterKey', 'ParameterValue'))
if capabilities is not None:
self.build_list_params(params,
capabilities,
'Capabilities.member')
if stack_policy_body is not None:
params['StackPolicyBody'] = stack_policy_body
if stack_policy_url is not None:
params['StackPolicyURL'] = stack_policy_url
return self._make_request(
action='UpdateStack',
verb='POST',
path='/', params=params)
def validate_template(self, template_body=None, template_url=None):
"""
Validates a specified template.
:type template_body: string
:param template_body: String containing the template body. (For more
information, go to `Template Anatomy`_ in the AWS CloudFormation
User Guide.)
Conditional: You must pass `TemplateURL` or `TemplateBody`. If both are
passed, only `TemplateBody` is used.
:type template_url: string
:param template_url: Location of file containing the template body. The
URL must point to a template (max size: 307,200 bytes) located in
an S3 bucket in the same region as the stack. For more information,
go to `Template Anatomy`_ in the AWS CloudFormation User Guide.
Conditional: You must pass `TemplateURL` or `TemplateBody`. If both are
passed, only `TemplateBody` is used.
"""
params = {}
if template_body is not None:
params['TemplateBody'] = template_body
if template_url is not None:
params['TemplateURL'] = template_url
return self._make_request(
action='ValidateTemplate',
verb='POST',
path='/', params=params)
def _make_request(self, action, verb, path, params):
params['ContentType'] = 'JSON'
response = self.make_request(action=action, verb='POST',
path='/', params=params)
body = response.read()
boto.log.debug(body)
if response.status == 200:
return json.loads(body)
else:
json_body = json.loads(body)
fault_name = json_body.get('Error', {}).get('Code', None)
exception_class = self._faults.get(fault_name, self.ResponseError)
raise exception_class(response.status, response.reason,
body=json_body)

View File

@@ -93,6 +93,12 @@ class DocumentServiceConnection(object):
self.documents_batch = [] self.documents_batch = []
self._sdf = None self._sdf = None
# Copy proxy settings from connection
if self.domain and self.domain.layer1 and self.domain.layer1.use_proxy:
self.proxy = {'http': self.domain.layer1.get_proxy_url_with_auth()}
else:
self.proxy = {}
def add(self, _id, fields): def add(self, _id, fields):
""" """
Add a document to be processed by the DocumentService Add a document to be processed by the DocumentService
@@ -184,6 +190,7 @@ class DocumentServiceConnection(object):
# Keep-alive is automatic in a post-1.0 requests world. # Keep-alive is automatic in a post-1.0 requests world.
session = requests.Session() session = requests.Session()
session.proxies = self.proxy
adapter = requests.adapters.HTTPAdapter( adapter = requests.adapters.HTTPAdapter(
pool_connections=20, pool_connections=20,
pool_maxsize=50, pool_maxsize=50,

View File

@@ -21,11 +21,11 @@
# #
import boto import boto
from boto.compat import json
from boto.connection import AWSQueryConnection from boto.connection import AWSQueryConnection
from boto.regioninfo import RegionInfo from boto.regioninfo import RegionInfo
from boto.exception import JSONResponseError from boto.exception import JSONResponseError
from boto.cloudsearch2 import exceptions from boto.cloudsearch2 import exceptions
from boto.compat import json
class CloudSearchConnection(AWSQueryConnection): class CloudSearchConnection(AWSQueryConnection):
@@ -56,6 +56,7 @@ class CloudSearchConnection(AWSQueryConnection):
"BaseException": exceptions.BaseException, "BaseException": exceptions.BaseException,
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
region = kwargs.pop('region', None) region = kwargs.pop('region', None)
if not region: if not region:
@@ -110,10 +111,10 @@ class CloudSearchConnection(AWSQueryConnection):
def define_analysis_scheme(self, domain_name, analysis_scheme): def define_analysis_scheme(self, domain_name, analysis_scheme):
""" """
Configures an analysis scheme for a domain. An analysis scheme Configures an analysis scheme that can be applied to a `text`
defines language-specific text processing options for a `text` or `text-array` field to define language-specific text
field. For more information, see `Configuring Analysis processing options. For more information, see `Configuring
Schemes`_ in the Amazon CloudSearch Developer Guide . Analysis Schemes`_ in the Amazon CloudSearch Developer Guide .
:type domain_name: string :type domain_name: string
:param domain_name: A string that represents the name of a domain. :param domain_name: A string that represents the name of a domain.
@@ -155,8 +156,8 @@ class CloudSearchConnection(AWSQueryConnection):
:type expression: dict :type expression: dict
:param expression: A named expression that can be evaluated at search :param expression: A named expression that can be evaluated at search
time. Can be used for sorting and filtering search results and time. Can be used to sort the search results, define other
constructing other expressions. expressions, or return computed information in the search results.
""" """
params = {'DomainName': domain_name, } params = {'DomainName': domain_name, }
@@ -425,9 +426,12 @@ class CloudSearchConnection(AWSQueryConnection):
""" """
Gets information about the search domains owned by this Gets information about the search domains owned by this
account. Can be limited to specific domains. Shows all domains account. Can be limited to specific domains. Shows all domains
by default. For more information, see `Getting Information by default. To get the number of searchable documents in a
about a Search Domain`_ in the Amazon CloudSearch Developer domain, use the console or submit a `matchall` request to your
Guide . domain's search endpoint:
`q=matchall&q.parser=structured&size=0`. For more information,
see `Getting Information about a Search Domain`_ in the Amazon
CloudSearch Developer Guide .
:type domain_names: list :type domain_names: list
:param domain_names: The names of the domains you want to include in :param domain_names: The names of the domains you want to include in
@@ -631,8 +635,6 @@ class CloudSearchConnection(AWSQueryConnection):
def list_domain_names(self): def list_domain_names(self):
""" """
Lists all search domains owned by an account. Lists all search domains owned by an account.
""" """
params = {} params = {}
return self._make_request( return self._make_request(

View File

@@ -131,7 +131,7 @@ class Query(object):
params['highlight.%s' % k] = v params['highlight.%s' % k] = v
if self.options: if self.options:
params['options'] = self.options params['q.options'] = self.options
if self.return_fields: if self.return_fields:
params['return'] = ','.join(self.return_fields) params['return'] = ','.join(self.return_fields)
@@ -152,6 +152,10 @@ class SearchConnection(object):
self.endpoint = endpoint self.endpoint = endpoint
self.session = requests.Session() self.session = requests.Session()
# Copy proxy settings from connection
if self.domain and self.domain.layer1 and self.domain.layer1.use_proxy:
self.session.proxies['http'] = self.domain.layer1.get_proxy_url_with_auth()
if not endpoint: if not endpoint:
self.endpoint = domain.search_service_endpoint self.endpoint = domain.search_service_endpoint

View File

@@ -24,6 +24,7 @@ class TrailAlreadyExistsException(BotoServerError):
""" """
pass pass
class InsufficientSnsTopicPolicyException(BotoServerError): class InsufficientSnsTopicPolicyException(BotoServerError):
""" """
Raised when the SNS topic does not allow Cloudtrail to post Raised when the SNS topic does not allow Cloudtrail to post
@@ -31,18 +32,21 @@ class InsufficientSnsTopicPolicyException(BotoServerError):
""" """
pass pass
class InvalidTrailNameException(BotoServerError): class InvalidTrailNameException(BotoServerError):
""" """
Raised when the trail name is invalid. Raised when the trail name is invalid.
""" """
pass pass
class InternalErrorException(BotoServerError): class InternalErrorException(BotoServerError):
""" """
Raised when there was an internal Cloudtrail error. Raised when there was an internal Cloudtrail error.
""" """
pass pass
class TrailNotFoundException(BotoServerError): class TrailNotFoundException(BotoServerError):
""" """
Raised when the given trail name is not found. Raised when the given trail name is not found.

View File

@@ -75,7 +75,6 @@ class CloudTrailConnection(AWSQueryConnection):
"InsufficientS3BucketPolicyException": exceptions.InsufficientS3BucketPolicyException, "InsufficientS3BucketPolicyException": exceptions.InsufficientS3BucketPolicyException,
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
region = kwargs.pop('region', None) region = kwargs.pop('region', None)
if not region: if not region:
@@ -351,4 +350,3 @@ class CloudTrailConnection(AWSQueryConnection):
exception_class = self._faults.get(fault_name, self.ResponseError) exception_class = self._faults.get(fault_name, self.ResponseError)
raise exception_class(response.status, response.reason, raise exception_class(response.status, response.reason,
body=json_body) body=json_body)

View File

@@ -0,0 +1,21 @@
# Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#

View File

@@ -0,0 +1,42 @@
# Copyright (c) 2014 Amazon.com, Inc. or its affiliates.
# All Rights Reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
from boto.regioninfo import RegionInfo, get_regions
def regions():
"""
Get all available regions for the Amazon Cognito Identity service.
:rtype: list
:return: A list of :class:`boto.regioninfo.RegionInfo`
"""
from boto.cognito.identity.layer1 import CognitoIdentityConnection
return get_regions('cognito-identity',
connection_cls=CognitoIdentityConnection)
def connect_to_region(region_name, **kw_params):
for region in regions():
if region.name == region_name:
return region.connect(**kw_params)
return None

View File

@@ -0,0 +1,40 @@
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
from boto.exception import BotoServerError
class LimitExceededException(BotoServerError):
pass
class ResourceConflictException(BotoServerError):
pass
class TooManyRequestsException(BotoServerError):
pass
class InvalidParameterException(BotoServerError):
pass
class ResourceNotFoundException(BotoServerError):
pass
class InternalErrorException(BotoServerError):
pass
class NotAuthorizedException(BotoServerError):
pass

View File

@@ -0,0 +1,303 @@
# Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
import boto
from boto.compat import json
from boto.connection import AWSQueryConnection
from boto.regioninfo import RegionInfo
from boto.exception import JSONResponseError
from boto.cognito.identity import exceptions
class CognitoIdentityConnection(AWSQueryConnection):
"""
Amazon Cognito
Amazon Cognito is a web service that facilitates the delivery of
scoped, temporary credentials to mobile devices or other untrusted
environments. Amazon Cognito uniquely identifies a device or user
and supplies the user with a consistent identity throughout the
lifetime of an application.
Amazon Cognito lets users authenticate with third-party identity
providers (Facebook, Google, or Login with Amazon). As a
developer, you decide which identity providers to trust. You can
also choose to support unauthenticated access from your
application. Your users are provided with Cognito tokens that
uniquely identify their device and any information provided about
third-party logins.
"""
APIVersion = "2014-06-30"
DefaultRegionName = "us-east-1"
DefaultRegionEndpoint = "cognito-identity.us-east-1.amazonaws.com"
ServiceName = "CognitoIdentity"
TargetPrefix = "AWSCognitoIdentityService"
ResponseError = JSONResponseError
_faults = {
"LimitExceededException": exceptions.LimitExceededException,
"ResourceConflictException": exceptions.ResourceConflictException,
"TooManyRequestsException": exceptions.TooManyRequestsException,
"InvalidParameterException": exceptions.InvalidParameterException,
"ResourceNotFoundException": exceptions.ResourceNotFoundException,
"InternalErrorException": exceptions.InternalErrorException,
"NotAuthorizedException": exceptions.NotAuthorizedException,
}
def __init__(self, **kwargs):
region = kwargs.pop('region', None)
if not region:
region = RegionInfo(self, self.DefaultRegionName,
self.DefaultRegionEndpoint)
if 'host' not in kwargs or kwargs['host'] is None:
kwargs['host'] = region.endpoint
super(CognitoIdentityConnection, self).__init__(**kwargs)
self.region = region
def _required_auth_capability(self):
return ['hmac-v4']
def create_identity_pool(self, identity_pool_name,
allow_unauthenticated_identities,
supported_login_providers=None):
"""
Creates a new identity pool. The identity pool is a store of
user identity information that is specific to your AWS
account.
:type identity_pool_name: string
:param identity_pool_name: A string that you provide.
:type allow_unauthenticated_identities: boolean
:param allow_unauthenticated_identities: TRUE if the identity pool
supports unauthenticated logins.
:type supported_login_providers: map
:param supported_login_providers: Optional key:value pairs mapping
provider names to provider app IDs.
"""
params = {
'IdentityPoolName': identity_pool_name,
'AllowUnauthenticatedIdentities': allow_unauthenticated_identities,
}
if supported_login_providers is not None:
params['SupportedLoginProviders'] = supported_login_providers
return self.make_request(action='CreateIdentityPool',
body=json.dumps(params))
def delete_identity_pool(self, identity_pool_id):
"""
Deletes a user pool. Once a pool is deleted, users will not be
able to authenticate with the pool.
:type identity_pool_id: string
:param identity_pool_id: An identity pool ID in the format REGION:GUID.
"""
params = {'IdentityPoolId': identity_pool_id, }
return self.make_request(action='DeleteIdentityPool',
body=json.dumps(params))
def describe_identity_pool(self, identity_pool_id):
"""
Gets details about a particular identity pool, including the
pool name, ID description, creation date, and current number
of users.
:type identity_pool_id: string
:param identity_pool_id: An identity pool ID in the format REGION:GUID.
"""
params = {'IdentityPoolId': identity_pool_id, }
return self.make_request(action='DescribeIdentityPool',
body=json.dumps(params))
def get_id(self, account_id, identity_pool_id, logins=None):
"""
Generates (or retrieves) a Cognito ID. Supplying multiple
logins will create an implicit linked account.
:type account_id: string
:param account_id: A standard AWS account ID (9+ digits).
:type identity_pool_id: string
:param identity_pool_id: An identity pool ID in the format REGION:GUID.
:type logins: map
:param logins: A set of optional name/value pairs that map provider
names to provider tokens.
"""
params = {
'AccountId': account_id,
'IdentityPoolId': identity_pool_id,
}
if logins is not None:
params['Logins'] = logins
return self.make_request(action='GetId',
body=json.dumps(params))
def get_open_id_token(self, identity_id, logins=None):
"""
Gets an OpenID token, using a known Cognito ID. This known
Cognito ID is returned from GetId. You can optionally add
additional logins for the identity. Supplying multiple logins
creates an implicit link.
:type identity_id: string
:param identity_id: A unique identifier in the format REGION:GUID.
:type logins: map
:param logins: A set of optional name/value pairs that map provider
names to provider tokens.
"""
params = {'IdentityId': identity_id, }
if logins is not None:
params['Logins'] = logins
return self.make_request(action='GetOpenIdToken',
body=json.dumps(params))
def list_identities(self, identity_pool_id, max_results, next_token=None):
"""
Lists the identities in a pool.
:type identity_pool_id: string
:param identity_pool_id: An identity pool ID in the format REGION:GUID.
:type max_results: integer
:param max_results: The maximum number of identities to return.
:type next_token: string
:param next_token: A pagination token.
"""
params = {
'IdentityPoolId': identity_pool_id,
'MaxResults': max_results,
}
if next_token is not None:
params['NextToken'] = next_token
return self.make_request(action='ListIdentities',
body=json.dumps(params))
def list_identity_pools(self, max_results, next_token=None):
"""
Lists all of the Cognito identity pools registered for your
account.
:type max_results: integer
:param max_results: The maximum number of identities to return.
:type next_token: string
:param next_token: A pagination token.
"""
params = {'MaxResults': max_results, }
if next_token is not None:
params['NextToken'] = next_token
return self.make_request(action='ListIdentityPools',
body=json.dumps(params))
def unlink_identity(self, identity_id, logins, logins_to_remove):
"""
Unlinks a federated identity from an existing account.
Unlinked logins will be considered new identities next time
they are seen. Removing the last linked login will make this
identity inaccessible.
:type identity_id: string
:param identity_id: A unique identifier in the format REGION:GUID.
:type logins: map
:param logins: A set of optional name/value pairs that map provider
names to provider tokens.
:type logins_to_remove: list
:param logins_to_remove: Provider names to unlink from this identity.
"""
params = {
'IdentityId': identity_id,
'Logins': logins,
'LoginsToRemove': logins_to_remove,
}
return self.make_request(action='UnlinkIdentity',
body=json.dumps(params))
def update_identity_pool(self, identity_pool_id, identity_pool_name,
allow_unauthenticated_identities,
supported_login_providers=None):
"""
Updates a user pool.
:type identity_pool_id: string
:param identity_pool_id: An identity pool ID in the format REGION:GUID.
:type identity_pool_name: string
:param identity_pool_name: A string that you provide.
:type allow_unauthenticated_identities: boolean
:param allow_unauthenticated_identities: TRUE if the identity pool
supports unauthenticated logins.
:type supported_login_providers: map
:param supported_login_providers: Optional key:value pairs mapping
provider names to provider app IDs.
"""
params = {
'IdentityPoolId': identity_pool_id,
'IdentityPoolName': identity_pool_name,
'AllowUnauthenticatedIdentities': allow_unauthenticated_identities,
}
if supported_login_providers is not None:
params['SupportedLoginProviders'] = supported_login_providers
return self.make_request(action='UpdateIdentityPool',
body=json.dumps(params))
def make_request(self, action, body):
headers = {
'X-Amz-Target': '%s.%s' % (self.TargetPrefix, action),
'Host': self.region.endpoint,
'Content-Type': 'application/x-amz-json-1.1',
'Content-Length': str(len(body)),
}
http_request = self.build_base_http_request(
method='POST', path='/', auth_path='/', params={},
headers=headers, data=body)
response = self._mexe(http_request, sender=None,
override_num_retries=10)
response_body = response.read().decode('utf-8')
boto.log.debug(response_body)
if response.status == 200:
if response_body:
return json.loads(response_body)
else:
json_body = json.loads(response_body)
fault_name = json_body.get('__type', None)
exception_class = self._faults.get(fault_name, self.ResponseError)
raise exception_class(response.status, response.reason,
body=json_body)

View File

@@ -0,0 +1,41 @@
# Copyright (c) 2014 Amazon.com, Inc. or its affiliates.
# All Rights Reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
from boto.regioninfo import RegionInfo, get_regions
def regions():
"""
Get all available regions for the Amazon Cognito Sync service.
:rtype: list
:return: A list of :class:`boto.regioninfo.RegionInfo`
"""
from boto.cognito.sync.layer1 import CognitoSyncConnection
return get_regions('cognito-sync', connection_cls=CognitoSyncConnection)
def connect_to_region(region_name, **kw_params):
for region in regions():
if region.name == region_name:
return region.connect(**kw_params)
return None

View File

@@ -0,0 +1,50 @@
# Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
from boto.exception import BotoServerError
class LimitExceededException(BotoServerError):
pass
class ResourceConflictException(BotoServerError):
pass
class TooManyRequestsException(BotoServerError):
pass
class InvalidParameterException(BotoServerError):
pass
class ResourceNotFoundException(BotoServerError):
pass
class InternalErrorException(BotoServerError):
pass
class NotAuthorizedException(BotoServerError):
pass

View File

@@ -0,0 +1,307 @@
# Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
from boto.compat import json
from boto.exception import JSONResponseError
from boto.connection import AWSAuthConnection
from boto.regioninfo import RegionInfo
from boto.cognito.sync import exceptions
class CognitoSyncConnection(AWSAuthConnection):
"""
Amazon Cognito Sync
Amazon Cognito Sync provides an AWS service and client library
that enable cross-device syncing of application-related user data.
High-level client libraries are available for both iOS and
Android. You can use these libraries to persist data locally so
that it's available even if the device is offline. Developer
credentials don't need to be stored on the mobile device to access
the service. You can use Amazon Cognito to obtain a normalized
user ID and credentials. User data is persisted in a dataset that
can store up to 1 MB of key-value pairs, and you can have up to 20
datasets per user identity.
"""
APIVersion = "2014-06-30"
DefaultRegionName = "us-east-1"
DefaultRegionEndpoint = "cognito-sync.us-east-1.amazonaws.com"
ResponseError = JSONResponseError
_faults = {
"LimitExceededException": exceptions.LimitExceededException,
"ResourceConflictException": exceptions.ResourceConflictException,
"TooManyRequestsException": exceptions.TooManyRequestsException,
"InvalidParameterException": exceptions.InvalidParameterException,
"ResourceNotFoundException": exceptions.ResourceNotFoundException,
"InternalErrorException": exceptions.InternalErrorException,
"NotAuthorizedException": exceptions.NotAuthorizedException,
}
def __init__(self, **kwargs):
region = kwargs.get('region')
if not region:
region = RegionInfo(self, self.DefaultRegionName,
self.DefaultRegionEndpoint)
else:
del kwargs['region']
kwargs['host'] = region.endpoint
super(CognitoSyncConnection, self).__init__(**kwargs)
self.region = region
def _required_auth_capability(self):
return ['hmac-v4']
def delete_dataset(self, identity_pool_id, identity_id, dataset_name):
"""
Deletes the specific dataset. The dataset will be deleted
permanently, and the action can't be undone. Datasets that
this dataset was merged with will no longer report the merge.
Any consequent operation on this dataset will result in a
ResourceNotFoundException.
:type identity_pool_id: string
:param identity_pool_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type identity_id: string
:param identity_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type dataset_name: string
:param dataset_name: A string of up to 128 characters. Allowed
characters are a-z, A-Z, 0-9, '_' (underscore), '-' (dash), and '.'
(dot).
"""
uri = '/identitypools/{0}/identities/{1}/datasets/{2}'.format(
identity_pool_id, identity_id, dataset_name)
return self.make_request('DELETE', uri, expected_status=200)
def describe_dataset(self, identity_pool_id, identity_id, dataset_name):
"""
Gets metadata about a dataset by identity and dataset name.
:type identity_pool_id: string
:param identity_pool_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type identity_id: string
:param identity_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type dataset_name: string
:param dataset_name: A string of up to 128 characters. Allowed
characters are a-z, A-Z, 0-9, '_' (underscore), '-' (dash), and '.'
(dot).
"""
uri = '/identitypools/{0}/identities/{1}/datasets/{2}'.format(
identity_pool_id, identity_id, dataset_name)
return self.make_request('GET', uri, expected_status=200)
def describe_identity_pool_usage(self, identity_pool_id):
"""
Gets usage details (for example, data storage) about a
particular identity pool.
:type identity_pool_id: string
:param identity_pool_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
"""
uri = '/identitypools/{0}'.format(identity_pool_id)
return self.make_request('GET', uri, expected_status=200)
def describe_identity_usage(self, identity_pool_id, identity_id):
"""
Gets usage information for an identity, including number of
datasets and data usage.
:type identity_pool_id: string
:param identity_pool_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type identity_id: string
:param identity_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
"""
uri = '/identitypools/{0}/identities/{1}'.format(
identity_pool_id, identity_id)
return self.make_request('GET', uri, expected_status=200)
def list_datasets(self, identity_pool_id, identity_id, next_token=None,
max_results=None):
"""
Lists datasets for an identity.
:type identity_pool_id: string
:param identity_pool_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type identity_id: string
:param identity_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type next_token: string
:param next_token: A pagination token for obtaining the next page of
results.
:type max_results: integer
:param max_results: The maximum number of results to be returned.
"""
uri = '/identitypools/{0}/identities/{1}/datasets'.format(
identity_pool_id, identity_id)
params = {}
headers = {}
return self.make_request('GET', uri, expected_status=200,
data=json.dumps(params), headers=headers)
def list_identity_pool_usage(self, next_token=None, max_results=None):
"""
Gets a list of identity pools registered with Cognito.
:type next_token: string
:param next_token: A pagination token for obtaining the next page of
results.
:type max_results: integer
:param max_results: The maximum number of results to be returned.
"""
uri = '/identitypools'
params = {}
headers = {}
return self.make_request('GET', uri, expected_status=200,
data=json.dumps(params), headers=headers)
def list_records(self, identity_pool_id, identity_id, dataset_name,
last_sync_count=None, next_token=None, max_results=None,
sync_session_token=None):
"""
Gets paginated records, optionally changed after a particular
sync count for a dataset and identity.
:type identity_pool_id: string
:param identity_pool_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type identity_id: string
:param identity_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type dataset_name: string
:param dataset_name: A string of up to 128 characters. Allowed
characters are a-z, A-Z, 0-9, '_' (underscore), '-' (dash), and '.'
(dot).
:type last_sync_count: long
:param last_sync_count: The last server sync count for this record.
:type next_token: string
:param next_token: A pagination token for obtaining the next page of
results.
:type max_results: integer
:param max_results: The maximum number of results to be returned.
:type sync_session_token: string
:param sync_session_token: A token containing a session ID, identity
ID, and expiration.
"""
uri = '/identitypools/{0}/identities/{1}/datasets/{2}/records'.format(
identity_pool_id, identity_id, dataset_name)
params = {}
headers = {}
return self.make_request('GET', uri, expected_status=200,
data=json.dumps(params), headers=headers)
def update_records(self, identity_pool_id, identity_id, dataset_name,
sync_session_token, record_patches=None,
client_context=None):
"""
Posts updates to records and add and delete records for a
dataset and user.
:type identity_pool_id: string
:param identity_pool_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type identity_id: string
:param identity_id: A name-spaced GUID (for example, us-
east-1:23EC4050-6AEA-7089-A2DD-08002EXAMPLE) created by Amazon
Cognito. GUID generation is unique within a region.
:type dataset_name: string
:param dataset_name: A string of up to 128 characters. Allowed
characters are a-z, A-Z, 0-9, '_' (underscore), '-' (dash), and '.'
(dot).
:type record_patches: list
:param record_patches:
:type sync_session_token: string
:param sync_session_token: The SyncSessionToken returned by a previous
call to ListRecords for this dataset and identity.
:type client_context: string
:param client_context:
"""
uri = '/identitypools/{0}/identities/{1}/datasets/{2}'.format(
identity_pool_id, identity_id, dataset_name)
params = {'SyncSessionToken': sync_session_token, }
headers = {}
if record_patches is not None:
params['RecordPatches'] = record_patches
if client_context is not None:
headers['x-amz-Client-Context'] = client_context
return self.make_request('POST', uri, expected_status=200,
data=json.dumps(params), headers=headers)
def make_request(self, verb, resource, headers=None, data='',
expected_status=None, params=None):
if headers is None:
headers = {}
response = AWSAuthConnection.make_request(
self, verb, resource, headers=headers, data=data, params=params)
body = json.loads(response.read().decode('utf-8'))
if response.status == expected_status:
return body
else:
error_type = response.getheader('x-amzn-ErrorType').split(':')[0]
error_class = self._faults.get(error_type, self.ResponseError)
raise error_class(response.status, response.reason, body)

View File

@@ -90,7 +90,7 @@ ON_APP_ENGINE = all(key in os.environ for key in (
PORTS_BY_SECURITY = {True: 443, PORTS_BY_SECURITY = {True: 443,
False: 80} False: 80}
DEFAULT_CA_CERTS_FILE = os.path.join(os.path.dirname(os.path.abspath(boto.cacerts.__file__ )), "cacerts.txt") DEFAULT_CA_CERTS_FILE = os.path.join(os.path.dirname(os.path.abspath(boto.cacerts.__file__)), "cacerts.txt")
class HostConnectionPool(object): class HostConnectionPool(object):
@@ -372,9 +372,10 @@ class HTTPRequest(object):
self.headers[key] = quote(val.encode('utf-8'), safe) self.headers[key] = quote(val.encode('utf-8'), safe)
setattr(self, '_headers_quoted', True) setattr(self, '_headers_quoted', True)
self.headers['User-Agent'] = UserAgent
connection._auth_handler.add_auth(self, **kwargs) connection._auth_handler.add_auth(self, **kwargs)
self.headers['User-Agent'] = UserAgent
# I'm not sure if this is still needed, now that add_auth is # I'm not sure if this is still needed, now that add_auth is
# setting the content-length for POST requests. # setting the content-length for POST requests.
if 'Content-Length' not in self.headers: if 'Content-Length' not in self.headers:
@@ -485,13 +486,13 @@ class AWSAuthConnection(object):
validate_certs) validate_certs)
if self.https_validate_certificates and not HAVE_HTTPS_CONNECTION: if self.https_validate_certificates and not HAVE_HTTPS_CONNECTION:
raise BotoClientError( raise BotoClientError(
"SSL server certificate validation is enabled in boto " "SSL server certificate validation is enabled in boto "
"configuration, but Python dependencies required to " "configuration, but Python dependencies required to "
"support this feature are not available. Certificate " "support this feature are not available. Certificate "
"validation is only supported when running under Python " "validation is only supported when running under Python "
"2.6 or later.") "2.6 or later.")
certs_file = config.get_value( certs_file = config.get_value(
'Boto', 'ca_certificates_file', DEFAULT_CA_CERTS_FILE) 'Boto', 'ca_certificates_file', DEFAULT_CA_CERTS_FILE)
if certs_file == 'system': if certs_file == 'system':
certs_file = None certs_file = None
self.ca_certificates_file = certs_file self.ca_certificates_file = certs_file
@@ -508,7 +509,7 @@ class AWSAuthConnection(object):
self.http_unretryable_exceptions = [] self.http_unretryable_exceptions = []
if HAVE_HTTPS_CONNECTION: if HAVE_HTTPS_CONNECTION:
self.http_unretryable_exceptions.append( self.http_unretryable_exceptions.append(
https_connection.InvalidCertificateException) https_connection.InvalidCertificateException)
# define values in socket exceptions we don't want to catch # define values in socket exceptions we don't want to catch
self.socket_exception_values = (errno.EINTR,) self.socket_exception_values = (errno.EINTR,)
@@ -565,7 +566,7 @@ class AWSAuthConnection(object):
self._connection = (self.host, self.port, self.is_secure) self._connection = (self.host, self.port, self.is_secure)
self._last_rs = None self._last_rs = None
self._auth_handler = auth.get_auth_handler( self._auth_handler = auth.get_auth_handler(
host, config, self.provider, self._required_auth_capability()) host, config, self.provider, self._required_auth_capability())
if getattr(self, 'AuthServiceName', None) is not None: if getattr(self, 'AuthServiceName', None) is not None:
self.auth_service_name = self.AuthServiceName self.auth_service_name = self.AuthServiceName
self.request_hook = None self.request_hook = None
@@ -667,9 +668,9 @@ class AWSAuthConnection(object):
self.proxy_pass = proxy_pass self.proxy_pass = proxy_pass
if 'http_proxy' in os.environ and not self.proxy: if 'http_proxy' in os.environ and not self.proxy:
pattern = re.compile( pattern = re.compile(
'(?:http://)?' \ '(?:http://)?'
'(?:(?P<user>[\w\-\.]+):(?P<pass>.*)@)?' \ '(?:(?P<user>[\w\-\.]+):(?P<pass>.*)@)?'
'(?P<host>[\w\-\.]+)' \ '(?P<host>[\w\-\.]+)'
'(?::(?P<port>\d+))?' '(?::(?P<port>\d+))?'
) )
match = pattern.match(os.environ['http_proxy']) match = pattern.match(os.environ['http_proxy'])
@@ -689,8 +690,8 @@ class AWSAuthConnection(object):
self.proxy_pass = config.get_value('Boto', 'proxy_pass', None) self.proxy_pass = config.get_value('Boto', 'proxy_pass', None)
if not self.proxy_port and self.proxy: if not self.proxy_port and self.proxy:
print("http_proxy environment variable does not specify " \ print("http_proxy environment variable does not specify "
"a port, using default") "a port, using default")
self.proxy_port = self.port self.proxy_port = self.port
self.no_proxy = os.environ.get('no_proxy', '') or os.environ.get('NO_PROXY', '') self.no_proxy = os.environ.get('no_proxy', '') or os.environ.get('NO_PROXY', '')
@@ -740,30 +741,30 @@ class AWSAuthConnection(object):
if is_secure: if is_secure:
boto.log.debug( boto.log.debug(
'establishing HTTPS connection: host=%s, kwargs=%s', 'establishing HTTPS connection: host=%s, kwargs=%s',
host, http_connection_kwargs) host, http_connection_kwargs)
if self.use_proxy and not self.skip_proxy(host): if self.use_proxy and not self.skip_proxy(host):
connection = self.proxy_ssl(host, is_secure and 443 or 80) connection = self.proxy_ssl(host, is_secure and 443 or 80)
elif self.https_connection_factory: elif self.https_connection_factory:
connection = self.https_connection_factory(host) connection = self.https_connection_factory(host)
elif self.https_validate_certificates and HAVE_HTTPS_CONNECTION: elif self.https_validate_certificates and HAVE_HTTPS_CONNECTION:
connection = https_connection.CertValidatingHTTPSConnection( connection = https_connection.CertValidatingHTTPSConnection(
host, ca_certs=self.ca_certificates_file, host, ca_certs=self.ca_certificates_file,
**http_connection_kwargs) **http_connection_kwargs)
else: else:
connection = http_client.HTTPSConnection(host, connection = http_client.HTTPSConnection(
**http_connection_kwargs) host, **http_connection_kwargs)
else: else:
boto.log.debug('establishing HTTP connection: kwargs=%s' % boto.log.debug('establishing HTTP connection: kwargs=%s' %
http_connection_kwargs) http_connection_kwargs)
if self.https_connection_factory: if self.https_connection_factory:
# even though the factory says https, this is too handy # even though the factory says https, this is too handy
# to not be able to allow overriding for http also. # to not be able to allow overriding for http also.
connection = self.https_connection_factory(host, connection = self.https_connection_factory(
**http_connection_kwargs) host, **http_connection_kwargs)
else: else:
connection = http_client.HTTPConnection(host, connection = http_client.HTTPConnection(
**http_connection_kwargs) host, **http_connection_kwargs)
if self.debug > 1: if self.debug > 1:
connection.set_debuglevel(self.debug) connection.set_debuglevel(self.debug)
# self.connection must be maintained for backwards-compatibility # self.connection must be maintained for backwards-compatibility
@@ -822,7 +823,7 @@ class AWSAuthConnection(object):
if self.https_validate_certificates and HAVE_HTTPS_CONNECTION: if self.https_validate_certificates and HAVE_HTTPS_CONNECTION:
msg = "wrapping ssl socket for proxied connection; " msg = "wrapping ssl socket for proxied connection; "
if self.ca_certificates_file: if self.ca_certificates_file:
msg += "CA certificate file=%s" %self.ca_certificates_file msg += "CA certificate file=%s" % self.ca_certificates_file
else: else:
msg += "using system provided SSL certs" msg += "using system provided SSL certs"
boto.log.debug(msg) boto.log.debug(msg)
@@ -836,7 +837,7 @@ class AWSAuthConnection(object):
hostname = self.host.split(':', 0)[0] hostname = self.host.split(':', 0)[0]
if not https_connection.ValidateCertificateHostname(cert, hostname): if not https_connection.ValidateCertificateHostname(cert, hostname):
raise https_connection.InvalidCertificateException( raise https_connection.InvalidCertificateException(
hostname, cert, 'hostname mismatch') hostname, cert, 'hostname mismatch')
else: else:
# Fallback for old Python without ssl.wrap_socket # Fallback for old Python without ssl.wrap_socket
if hasattr(http_client, 'ssl'): if hasattr(http_client, 'ssl'):
@@ -857,6 +858,21 @@ class AWSAuthConnection(object):
auth = encodebytes(self.proxy_user + ':' + self.proxy_pass) auth = encodebytes(self.proxy_user + ':' + self.proxy_pass)
return {'Proxy-Authorization': 'Basic %s' % auth} return {'Proxy-Authorization': 'Basic %s' % auth}
# For passing proxy information to other connection libraries, e.g. cloudsearch2
def get_proxy_url_with_auth(self):
if not self.use_proxy:
return None
if self.proxy_user or self.proxy_pass:
if self.proxy_pass:
login_info = '%s:%s@' % (self.proxy_user, self.proxy_pass)
else:
login_info = '%s@' % self.proxy_user
else:
login_info = ''
return 'http://%s%s:%s' % (login_info, self.proxy, str(self.proxy_port or self.port))
def set_host_header(self, request): def set_host_header(self, request):
try: try:
request.headers['Host'] = \ request.headers['Host'] = \
@@ -993,8 +1009,8 @@ class AWSAuthConnection(object):
'encountered unretryable %s exception, re-raising' % 'encountered unretryable %s exception, re-raising' %
e.__class__.__name__) e.__class__.__name__)
raise raise
boto.log.debug('encountered %s exception, reconnecting' % \ boto.log.debug('encountered %s exception, reconnecting' %
e.__class__.__name__) e.__class__.__name__)
connection = self.new_http_connection(request.host, request.port, connection = self.new_http_connection(request.host, request.port,
self.is_secure) self.is_secure)
time.sleep(next_sleep) time.sleep(next_sleep)
@@ -1026,8 +1042,7 @@ class AWSAuthConnection(object):
headers = {} headers = {}
else: else:
headers = headers.copy() headers = headers.copy()
if (self.host_header and if self.host_header and not boto.utils.find_matching_headers('host', headers):
not boto.utils.find_matching_headers('host', headers)):
headers['host'] = self.host_header headers['host'] = self.host_header
host = host or self.host host = host or self.host
if self.use_proxy: if self.use_proxy:
@@ -1070,14 +1085,15 @@ class AWSQueryConnection(AWSAuthConnection):
proxy_user=None, proxy_pass=None, host=None, debug=0, proxy_user=None, proxy_pass=None, host=None, debug=0,
https_connection_factory=None, path='/', security_token=None, https_connection_factory=None, path='/', security_token=None,
validate_certs=True, profile_name=None): validate_certs=True, profile_name=None):
super(AWSQueryConnection, self).__init__(host, aws_access_key_id, super(AWSQueryConnection, self).__init__(
aws_secret_access_key, host, aws_access_key_id,
is_secure, port, proxy, aws_secret_access_key,
proxy_port, proxy_user, proxy_pass, is_secure, port, proxy,
debug, https_connection_factory, path, proxy_port, proxy_user, proxy_pass,
security_token=security_token, debug, https_connection_factory, path,
validate_certs=validate_certs, security_token=security_token,
profile_name=profile_name) validate_certs=validate_certs,
profile_name=profile_name)
def _required_auth_capability(self): def _required_auth_capability(self):
return [] return []

View File

@@ -14,9 +14,8 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
# #

View File

@@ -28,6 +28,7 @@ This module requires the yaml module.
from boto.sqs.message import Message from boto.sqs.message import Message
import yaml import yaml
class YAMLMessage(Message): class YAMLMessage(Message):
""" """
The YAMLMessage class provides a YAML compatible message. Encoding and The YAMLMessage class provides a YAML compatible message. Encoding and

View File

@@ -83,7 +83,6 @@ class DataPipelineConnection(AWSQueryConnection):
"InternalServiceError": exceptions.InternalServiceError, "InternalServiceError": exceptions.InternalServiceError,
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
region = kwargs.pop('region', None) region = kwargs.pop('region', None)
if not region: if not region:
@@ -638,4 +637,3 @@ class DataPipelineConnection(AWSQueryConnection):
exception_class = self._faults.get(fault_name, self.ResponseError) exception_class = self._faults.get(fault_name, self.ResponseError)
raise exception_class(response.status, response.reason, raise exception_class(response.status, response.reason,
body=json_body) body=json_body)

View File

@@ -20,6 +20,7 @@
# IN THE SOFTWARE. # IN THE SOFTWARE.
# #
class DirectConnectClientException(Exception): class DirectConnectClientException(Exception):
pass pass

View File

@@ -65,7 +65,6 @@ class DirectConnectConnection(AWSQueryConnection):
"DirectConnectServerException": exceptions.DirectConnectServerException, "DirectConnectServerException": exceptions.DirectConnectServerException,
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
region = kwargs.pop('region', None) region = kwargs.pop('region', None)
if not region: if not region:
@@ -626,4 +625,3 @@ class DirectConnectConnection(AWSQueryConnection):
exception_class = self._faults.get(fault_name, self.ResponseError) exception_class = self._faults.get(fault_name, self.ResponseError)
raise exception_class(response.status, response.reason, raise exception_class(response.status, response.reason,
body=json_body) body=json_body)

File diff suppressed because it is too large Load Diff

View File

@@ -467,6 +467,8 @@ class Table(object):
should be fetched) should be fetched)
Returns an ``Item`` instance containing all the data for that record. Returns an ``Item`` instance containing all the data for that record.
Raises an ``ItemNotFound`` exception if the item is not found.
Example:: Example::

View File

@@ -22,6 +22,7 @@
from boto.ec2.ec2object import EC2Object from boto.ec2.ec2object import EC2Object
class Address(EC2Object): class Address(EC2Object):
""" """
Represents an EC2 Elastic IP Address Represents an EC2 Elastic IP Address
@@ -78,33 +79,36 @@ class Address(EC2Object):
""" """
if self.allocation_id: if self.allocation_id:
return self.connection.release_address( return self.connection.release_address(
None, allocation_id=self.allocation_id,
self.allocation_id,
dry_run=dry_run) dry_run=dry_run)
else: else:
return self.connection.release_address( return self.connection.release_address(
self.public_ip, public_ip=self.public_ip,
dry_run=dry_run dry_run=dry_run
) )
delete = release delete = release
def associate(self, instance_id, allow_reassociation=False, dry_run=False): def associate(self, instance_id=None, network_interface_id=None, private_ip_address=None, allow_reassociation=False, dry_run=False):
""" """
Associate this Elastic IP address with a currently running instance. Associate this Elastic IP address with a currently running instance.
:see: :meth:`boto.ec2.connection.EC2Connection.associate_address` :see: :meth:`boto.ec2.connection.EC2Connection.associate_address`
""" """
if self.allocation_id: if self.allocation_id:
return self.connection.associate_address( return self.connection.associate_address(
instance_id, instance_id=instance_id,
self.public_ip, public_ip=self.public_ip,
allocation_id=self.allocation_id, allocation_id=self.allocation_id,
network_interface_id=network_interface_id,
private_ip_address=private_ip_address,
allow_reassociation=allow_reassociation, allow_reassociation=allow_reassociation,
dry_run=dry_run dry_run=dry_run
) )
return self.connection.associate_address( return self.connection.associate_address(
instance_id, instance_id=instance_id,
self.public_ip, public_ip=self.public_ip,
network_interface_id=network_interface_id,
private_ip_address=private_ip_address,
allow_reassociation=allow_reassociation, allow_reassociation=allow_reassociation,
dry_run=dry_run dry_run=dry_run
) )
@@ -116,14 +120,11 @@ class Address(EC2Object):
""" """
if self.association_id: if self.association_id:
return self.connection.disassociate_address( return self.connection.disassociate_address(
None, association_id=self.association_id,
self.association_id,
dry_run=dry_run dry_run=dry_run
) )
else: else:
return self.connection.disassociate_address( return self.connection.disassociate_address(
self.public_ip, public_ip=self.public_ip,
dry_run=dry_run dry_run=dry_run
) )

View File

@@ -105,14 +105,14 @@ class AutoScaleConnection(AWSQueryConnection):
self.region = region self.region = region
self.use_block_device_types = use_block_device_types self.use_block_device_types = use_block_device_types
super(AutoScaleConnection, self).__init__(aws_access_key_id, super(AutoScaleConnection, self).__init__(aws_access_key_id,
aws_secret_access_key, aws_secret_access_key,
is_secure, port, proxy, proxy_port, is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass, proxy_user, proxy_pass,
self.region.endpoint, debug, self.region.endpoint, debug,
https_connection_factory, path=path, https_connection_factory, path=path,
security_token=security_token, security_token=security_token,
validate_certs=validate_certs, validate_certs=validate_certs,
profile_name=profile_name) profile_name=profile_name)
def _required_auth_capability(self): def _required_auth_capability(self):
return ['hmac-v4'] return ['hmac-v4']
@@ -222,7 +222,10 @@ class AutoScaleConnection(AWSQueryConnection):
if launch_config.key_name: if launch_config.key_name:
params['KeyName'] = launch_config.key_name params['KeyName'] = launch_config.key_name
if launch_config.user_data: if launch_config.user_data:
params['UserData'] = base64.b64encode(launch_config.user_data).decode('utf-8') user_data = launch_config.user_data
if isinstance(user_data, six.text_type):
user_data = user_data.encode('utf-8')
params['UserData'] = base64.b64encode(user_data).decode('utf-8')
if launch_config.kernel_id: if launch_config.kernel_id:
params['KernelId'] = launch_config.kernel_id params['KernelId'] = launch_config.kernel_id
if launch_config.ramdisk_id: if launch_config.ramdisk_id:

View File

@@ -71,4 +71,3 @@ class Activity(object):
self.status_code = value self.status_code = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -57,4 +57,3 @@ class Instance(object):
self.group_name = value self.group_name = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -20,7 +20,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
from datetime import datetime
from boto.ec2.elb.listelement import ListElement from boto.ec2.elb.listelement import ListElement
# Namespacing issue with deprecated local class # Namespacing issue with deprecated local class
from boto.ec2.blockdevicemapping import BlockDeviceMapping as BDM from boto.ec2.blockdevicemapping import BlockDeviceMapping as BDM
@@ -129,8 +128,8 @@ class LaunchConfiguration(object):
:type instance_type: str :type instance_type: str
:param instance_type: The instance type :param instance_type: The instance type
:type kern_id: str :type kernel_id: str
:param kern_id: Kernel id for instance :param kernel_id: Kernel id for instance
:type ramdisk_id: str :type ramdisk_id: str
:param ramdisk_id: RAM disk id for instance :param ramdisk_id: RAM disk id for instance

View File

@@ -19,6 +19,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
class AccountLimits(object): class AccountLimits(object):
def __init__(self, connection=None): def __init__(self, connection=None):
self.connection = connection self.connection = connection
@@ -27,7 +28,7 @@ class AccountLimits(object):
def __repr__(self): def __repr__(self):
return 'AccountLimits: [%s, %s]' % (self.max_autoscaling_groups, return 'AccountLimits: [%s, %s]' % (self.max_autoscaling_groups,
self.max_launch_configurations) self.max_launch_configurations)
def startElement(self, name, attrs, connection): def startElement(self, name, attrs, connection):
return None return None
@@ -41,4 +42,3 @@ class AccountLimits(object):
self.max_launch_configurations = int(value) self.max_launch_configurations = int(value)
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -23,6 +23,7 @@
from boto.resultset import ResultSet from boto.resultset import ResultSet
from boto.ec2.elb.listelement import ListElement from boto.ec2.elb.listelement import ListElement
class Alarm(object): class Alarm(object):
def __init__(self, connection=None): def __init__(self, connection=None):
self.connection = connection self.connection = connection
@@ -64,18 +65,24 @@ class AdjustmentType(object):
class MetricCollectionTypes(object): class MetricCollectionTypes(object):
class BaseType(object): class BaseType(object):
arg = '' arg = ''
def __init__(self, connection): def __init__(self, connection):
self.connection = connection self.connection = connection
self.val = None self.val = None
def __repr__(self): def __repr__(self):
return '%s:%s' % (self.arg, self.val) return '%s:%s' % (self.arg, self.val)
def startElement(self, name, attrs, connection): def startElement(self, name, attrs, connection):
return return
def endElement(self, name, value, connection): def endElement(self, name, value, connection):
if name == self.arg: if name == self.arg:
self.val = value self.val = value
class Metric(BaseType): class Metric(BaseType):
arg = 'Metric' arg = 'Metric'
class Granularity(BaseType): class Granularity(BaseType):
arg = 'Granularity' arg = 'Granularity'

View File

@@ -19,6 +19,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
class Request(object): class Request(object):
def __init__(self, connection=None): def __init__(self, connection=None):
self.connection = connection self.connection = connection
@@ -35,4 +36,3 @@ class Request(object):
self.request_id = value self.request_id = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -75,4 +75,3 @@ class ScheduledUpdateGroupAction(object):
self.end_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ') self.end_time = datetime.strptime(value, '%Y-%m-%dT%H:%M:%SZ')
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -38,7 +38,8 @@ class BlockDeviceType(object):
delete_on_termination=False, delete_on_termination=False,
size=None, size=None,
volume_type=None, volume_type=None,
iops=None): iops=None,
encrypted=None):
self.connection = connection self.connection = connection
self.ephemeral_name = ephemeral_name self.ephemeral_name = ephemeral_name
self.no_device = no_device self.no_device = no_device
@@ -50,6 +51,7 @@ class BlockDeviceType(object):
self.size = size self.size = size
self.volume_type = volume_type self.volume_type = volume_type
self.iops = iops self.iops = iops
self.encrypted = encrypted
def startElement(self, name, attrs, connection): def startElement(self, name, attrs, connection):
pass pass
@@ -76,6 +78,8 @@ class BlockDeviceType(object):
self.volume_type = value self.volume_type = value
elif lname == 'iops': elif lname == 'iops':
self.iops = int(value) self.iops = int(value)
elif lname == 'encrypted':
self.encrypted = (value == 'true')
else: else:
setattr(self, name, value) setattr(self, name, value)
@@ -150,4 +154,12 @@ class BlockDeviceMapping(dict):
params['%s.Ebs.VolumeType' % pre] = block_dev.volume_type params['%s.Ebs.VolumeType' % pre] = block_dev.volume_type
if block_dev.iops is not None: if block_dev.iops is not None:
params['%s.Ebs.Iops' % pre] = block_dev.iops params['%s.Ebs.Iops' % pre] = block_dev.iops
# The encrypted flag (even if False) cannot be specified for the root EBS
# volume.
if block_dev.encrypted is not None:
if block_dev.encrypted:
params['%s.Ebs.Encrypted' % pre] = 'true'
else:
params['%s.Ebs.Encrypted' % pre] = 'false'
i += 1 i += 1

View File

@@ -25,6 +25,7 @@ Represents an EC2 Bundle Task
from boto.ec2.ec2object import EC2Object from boto.ec2.ec2object import EC2Object
class BundleInstanceTask(EC2Object): class BundleInstanceTask(EC2Object):
def __init__(self, connection=None): def __init__(self, connection=None):
@@ -75,4 +76,3 @@ class BundleInstanceTask(EC2Object):
self.message = value self.message = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -14,19 +14,21 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
import boto.ec2 import boto.ec2
from boto.sdb.db.property import StringProperty, IntegerProperty from boto.sdb.db.property import StringProperty, IntegerProperty
from boto.manage import propget from boto.manage import propget
from boto.compat import six
InstanceTypes = ['m1.small', 'm1.large', 'm1.xlarge', InstanceTypes = ['m1.small', 'm1.large', 'm1.xlarge',
'c1.medium', 'c1.xlarge', 'm2.xlarge', 'c1.medium', 'c1.xlarge', 'm2.xlarge',
'm2.2xlarge', 'm2.4xlarge', 'cc1.4xlarge', 'm2.2xlarge', 'm2.4xlarge', 'cc1.4xlarge',
't1.micro'] 't1.micro']
class BuyReservation(object): class BuyReservation(object):
def get_region(self, params): def get_region(self, params):
@@ -51,7 +53,7 @@ class BuyReservation(object):
prop = StringProperty(name='zone', verbose_name='EC2 Availability Zone', prop = StringProperty(name='zone', verbose_name='EC2 Availability Zone',
choices=self.ec2.get_all_zones) choices=self.ec2.get_all_zones)
params['zone'] = propget.get(prop) params['zone'] = propget.get(prop)
def get(self, params): def get(self, params):
self.get_region(params) self.get_region(params)
self.ec2 = params['region'].connect() self.ec2 = params['region'].connect()
@@ -76,7 +78,7 @@ if __name__ == "__main__":
unit_price = float(offering.fixed_price) unit_price = float(offering.fixed_price)
total_price = unit_price * params['quantity'] total_price = unit_price * params['quantity']
print('!!! You are about to purchase %d of these offerings for a total of $%.2f !!!' % (params['quantity'], total_price)) print('!!! You are about to purchase %d of these offerings for a total of $%.2f !!!' % (params['quantity'], total_price))
answer = raw_input('Are you sure you want to do this? If so, enter YES: ') answer = six.moves.input('Are you sure you want to do this? If so, enter YES: ')
if answer.strip().lower() == 'yes': if answer.strip().lower() == 'yes':
offering.purchase(params['quantity']) offering.purchase(params['quantity'])
else: else:

View File

@@ -92,14 +92,14 @@ class CloudWatchConnection(AWSQueryConnection):
validate_certs = False validate_certs = False
super(CloudWatchConnection, self).__init__(aws_access_key_id, super(CloudWatchConnection, self).__init__(aws_access_key_id,
aws_secret_access_key, aws_secret_access_key,
is_secure, port, proxy, proxy_port, is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass, proxy_user, proxy_pass,
self.region.endpoint, debug, self.region.endpoint, debug,
https_connection_factory, path, https_connection_factory, path,
security_token, security_token,
validate_certs=validate_certs, validate_certs=validate_certs,
profile_name=profile_name) profile_name=profile_name)
def _required_auth_capability(self): def _required_auth_capability(self):
return ['hmac-v4'] return ['hmac-v4']
@@ -113,11 +113,11 @@ class CloudWatchConnection(AWSQueryConnection):
if isinstance(dim_value, six.string_types): if isinstance(dim_value, six.string_types):
dim_value = [dim_value] dim_value = [dim_value]
for value in dim_value: for value in dim_value:
params['%s.%d.Name' % (prefix, i+1)] = dim_name params['%s.%d.Name' % (prefix, i + 1)] = dim_name
params['%s.%d.Value' % (prefix, i+1)] = value params['%s.%d.Value' % (prefix, i + 1)] = value
i += 1 i += 1
else: else:
params['%s.%d.Name' % (prefix, i+1)] = dim_name params['%s.%d.Name' % (prefix, i + 1)] = dim_name
i += 1 i += 1
def build_list_params(self, params, items, label): def build_list_params(self, params, items, label):
@@ -134,7 +134,7 @@ class CloudWatchConnection(AWSQueryConnection):
params[label % i] = item params[label % i] = item
def build_put_params(self, params, name, value=None, timestamp=None, def build_put_params(self, params, name, value=None, timestamp=None,
unit=None, dimensions=None, statistics=None): unit=None, dimensions=None, statistics=None):
args = (name, value, unit, dimensions, statistics, timestamp) args = (name, value, unit, dimensions, statistics, timestamp)
length = max(map(lambda a: len(a) if isinstance(a, list) else 1, args)) length = max(map(lambda a: len(a) if isinstance(a, list) else 1, args))
@@ -329,7 +329,7 @@ class CloudWatchConnection(AWSQueryConnection):
""" """
params = {'Namespace': namespace} params = {'Namespace': namespace}
self.build_put_params(params, name, value=value, timestamp=timestamp, self.build_put_params(params, name, value=value, timestamp=timestamp,
unit=unit, dimensions=dimensions, statistics=statistics) unit=unit, dimensions=dimensions, statistics=statistics)
return self.get_status('PutMetricData', params, verb="POST") return self.get_status('PutMetricData', params, verb="POST")
@@ -498,15 +498,15 @@ class CloudWatchConnection(AWSQueryConnection):
:param alarm: MetricAlarm object. :param alarm: MetricAlarm object.
""" """
params = { params = {
'AlarmName': alarm.name, 'AlarmName': alarm.name,
'MetricName': alarm.metric, 'MetricName': alarm.metric,
'Namespace': alarm.namespace, 'Namespace': alarm.namespace,
'Statistic': alarm.statistic, 'Statistic': alarm.statistic,
'ComparisonOperator': alarm.comparison, 'ComparisonOperator': alarm.comparison,
'Threshold': alarm.threshold, 'Threshold': alarm.threshold,
'EvaluationPeriods': alarm.evaluation_periods, 'EvaluationPeriods': alarm.evaluation_periods,
'Period': alarm.period, 'Period': alarm.period,
} }
if alarm.actions_enabled is not None: if alarm.actions_enabled is not None:
params['ActionsEnabled'] = alarm.actions_enabled params['ActionsEnabled'] = alarm.actions_enabled
if alarm.alarm_actions: if alarm.alarm_actions:

View File

@@ -52,11 +52,11 @@ class MetricAlarm(object):
INSUFFICIENT_DATA = 'INSUFFICIENT_DATA' INSUFFICIENT_DATA = 'INSUFFICIENT_DATA'
_cmp_map = { _cmp_map = {
'>=': 'GreaterThanOrEqualToThreshold', '>=': 'GreaterThanOrEqualToThreshold',
'>': 'GreaterThanThreshold', '>': 'GreaterThanThreshold',
'<': 'LessThanThreshold', '<': 'LessThanThreshold',
'<=': 'LessThanOrEqualToThreshold', '<=': 'LessThanOrEqualToThreshold',
} }
_rev_cmp_map = dict((v, k) for (k, v) in six.iteritems(_cmp_map)) _rev_cmp_map = dict((v, k) for (k, v) in six.iteritems(_cmp_map))
def __init__(self, connection=None, name=None, metric=None, def __init__(self, connection=None, name=None, metric=None,
@@ -122,15 +122,15 @@ class MetricAlarm(object):
'InstanceId': ['i-0123456', 'i-0123457'], 'InstanceId': ['i-0123456', 'i-0123457'],
'LoadBalancerName': 'test-lb' 'LoadBalancerName': 'test-lb'
} }
:type alarm_actions: list of strs :type alarm_actions: list of strs
:param alarm_actions: A list of the ARNs of the actions to take in :param alarm_actions: A list of the ARNs of the actions to take in
ALARM state ALARM state
:type insufficient_data_actions: list of strs :type insufficient_data_actions: list of strs
:param insufficient_data_actions: A list of the ARNs of the actions to :param insufficient_data_actions: A list of the ARNs of the actions to
take in INSUFFICIENT_DATA state take in INSUFFICIENT_DATA state
:type ok_actions: list of strs :type ok_actions: list of strs
:param ok_actions: A list of the ARNs of the actions to take in OK state :param ok_actions: A list of the ARNs of the actions to take in OK state
""" """
@@ -295,6 +295,7 @@ class MetricAlarm(object):
def delete(self): def delete(self):
self.connection.delete_alarms([self.name]) self.connection.delete_alarms([self.name])
class AlarmHistoryItem(object): class AlarmHistoryItem(object):
def __init__(self, connection=None): def __init__(self, connection=None):
self.connection = connection self.connection = connection

View File

@@ -21,6 +21,7 @@
# #
from datetime import datetime from datetime import datetime
class Datapoint(dict): class Datapoint(dict):
def __init__(self, connection=None): def __init__(self, connection=None):

View File

@@ -20,6 +20,7 @@
# IN THE SOFTWARE. # IN THE SOFTWARE.
# #
class Dimension(dict): class Dimension(dict):
def startElement(self, name, attrs, connection): def startElement(self, name, attrs, connection):

View File

@@ -14,11 +14,12 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
class ListElement(list): class ListElement(list):
def startElement(self, name, attrs, connection): def startElement(self, name, attrs, connection):

View File

@@ -93,14 +93,14 @@ class EC2Connection(AWSQueryConnection):
self.DefaultRegionEndpoint) self.DefaultRegionEndpoint)
self.region = region self.region = region
super(EC2Connection, self).__init__(aws_access_key_id, super(EC2Connection, self).__init__(aws_access_key_id,
aws_secret_access_key, aws_secret_access_key,
is_secure, port, proxy, proxy_port, is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass, proxy_user, proxy_pass,
self.region.endpoint, debug, self.region.endpoint, debug,
https_connection_factory, path, https_connection_factory, path,
security_token, security_token,
validate_certs=validate_certs, validate_certs=validate_certs,
profile_name=profile_name) profile_name=profile_name)
if api_version: if api_version:
self.APIVersion = api_version self.APIVersion = api_version
@@ -270,7 +270,7 @@ class EC2Connection(AWSQueryConnection):
root_device_name=None, block_device_map=None, root_device_name=None, block_device_map=None,
dry_run=False, virtualization_type=None, dry_run=False, virtualization_type=None,
sriov_net_support=None, sriov_net_support=None,
snapshot_id=None, snapshot_id=None,
delete_root_volume_on_termination=False): delete_root_volume_on_termination=False):
""" """
Register an image. Register an image.
@@ -323,7 +323,7 @@ class EC2Connection(AWSQueryConnection):
:type delete_root_volume_on_termination: bool :type delete_root_volume_on_termination: bool
:param delete_root_volume_on_termination: Whether to delete the root :param delete_root_volume_on_termination: Whether to delete the root
volume of the image after instance termination. Only applies when volume of the image after instance termination. Only applies when
creating image from snapshot_id. Defaults to False. Note that creating image from snapshot_id. Defaults to False. Note that
leaving volumes behind after instance termination is not free. leaving volumes behind after instance termination is not free.
:rtype: string :rtype: string
@@ -345,8 +345,8 @@ class EC2Connection(AWSQueryConnection):
if root_device_name: if root_device_name:
params['RootDeviceName'] = root_device_name params['RootDeviceName'] = root_device_name
if snapshot_id: if snapshot_id:
root_vol = BlockDeviceType(snapshot_id=snapshot_id, root_vol = BlockDeviceType(snapshot_id=snapshot_id,
delete_on_termination=delete_root_volume_on_termination) delete_on_termination=delete_root_volume_on_termination)
block_device_map = BlockDeviceMapping() block_device_map = BlockDeviceMapping()
block_device_map[root_device_name] = root_vol block_device_map[root_device_name] = root_vol
if block_device_map: if block_device_map:
@@ -358,7 +358,6 @@ class EC2Connection(AWSQueryConnection):
if sriov_net_support: if sriov_net_support:
params['SriovNetSupport'] = sriov_net_support params['SriovNetSupport'] = sriov_net_support
rs = self.get_object('RegisterImage', params, ResultSet, verb='POST') rs = self.get_object('RegisterImage', params, ResultSet, verb='POST')
image_id = getattr(rs, 'imageId', None) image_id = getattr(rs, 'imageId', None)
return image_id return image_id
@@ -1881,9 +1880,9 @@ class EC2Connection(AWSQueryConnection):
return self.get_status('AssignPrivateIpAddresses', params, verb='POST') return self.get_status('AssignPrivateIpAddresses', params, verb='POST')
def _associate_address(self, status, instance_id=None, public_ip=None, def _associate_address(self, status, instance_id=None, public_ip=None,
allocation_id=None, network_interface_id=None, allocation_id=None, network_interface_id=None,
private_ip_address=None, allow_reassociation=False, private_ip_address=None, allow_reassociation=False,
dry_run=False): dry_run=False):
params = {} params = {}
if instance_id is not None: if instance_id is not None:
params['InstanceId'] = instance_id params['InstanceId'] = instance_id
@@ -1960,9 +1959,9 @@ class EC2Connection(AWSQueryConnection):
allow_reassociation=allow_reassociation, dry_run=dry_run) allow_reassociation=allow_reassociation, dry_run=dry_run)
def associate_address_object(self, instance_id=None, public_ip=None, def associate_address_object(self, instance_id=None, public_ip=None,
allocation_id=None, network_interface_id=None, allocation_id=None, network_interface_id=None,
private_ip_address=None, allow_reassociation=False, private_ip_address=None, allow_reassociation=False,
dry_run=False): dry_run=False):
""" """
Associate an Elastic IP address with a currently running instance. Associate an Elastic IP address with a currently running instance.
This requires one of ``public_ip`` or ``allocation_id`` depending This requires one of ``public_ip`` or ``allocation_id`` depending
@@ -2589,7 +2588,7 @@ class EC2Connection(AWSQueryConnection):
now = datetime.utcnow() now = datetime.utcnow()
last_hour = datetime(now.year, now.month, now.day, now.hour) last_hour = datetime(now.year, now.month, now.day, now.hour)
last_midnight = datetime(now.year, now.month, now.day) last_midnight = datetime(now.year, now.month, now.day)
last_sunday = datetime(now.year, now.month, now.day) - timedelta(days = (now.weekday() + 1) % 7) last_sunday = datetime(now.year, now.month, now.day) - timedelta(days=(now.weekday() + 1) % 7)
start_of_month = datetime(now.year, now.month, 1) start_of_month = datetime(now.year, now.month, 1)
target_backup_times = [] target_backup_times = []
@@ -2598,15 +2597,15 @@ class EC2Connection(AWSQueryConnection):
oldest_snapshot_date = datetime(2007, 1, 1) oldest_snapshot_date = datetime(2007, 1, 1)
for hour in range(0, hourly_backups): for hour in range(0, hourly_backups):
target_backup_times.append(last_hour - timedelta(hours = hour)) target_backup_times.append(last_hour - timedelta(hours=hour))
for day in range(0, daily_backups): for day in range(0, daily_backups):
target_backup_times.append(last_midnight - timedelta(days = day)) target_backup_times.append(last_midnight - timedelta(days=day))
for week in range(0, weekly_backups): for week in range(0, weekly_backups):
target_backup_times.append(last_sunday - timedelta(weeks = week)) target_backup_times.append(last_sunday - timedelta(weeks=week))
one_day = timedelta(days = 1) one_day = timedelta(days=1)
monthly_snapshots_added = 0 monthly_snapshots_added = 0
while (start_of_month > oldest_snapshot_date and while (start_of_month > oldest_snapshot_date and
(monthly_backups is True or (monthly_backups is True or
@@ -3080,7 +3079,7 @@ class EC2Connection(AWSQueryConnection):
:rtype: bool :rtype: bool
:return: True if successful. :return: True if successful.
""" """
params = {'GroupName':group_name} params = {'GroupName': group_name}
if src_security_group_name: if src_security_group_name:
params['SourceSecurityGroupName'] = src_security_group_name params['SourceSecurityGroupName'] = src_security_group_name
if src_security_group_owner_id: if src_security_group_owner_id:
@@ -3184,7 +3183,7 @@ class EC2Connection(AWSQueryConnection):
if not isinstance(cidr_ip, list): if not isinstance(cidr_ip, list):
cidr_ip = [cidr_ip] cidr_ip = [cidr_ip]
for i, single_cidr_ip in enumerate(cidr_ip): for i, single_cidr_ip in enumerate(cidr_ip):
params['IpPermissions.1.IpRanges.%d.CidrIp' % (i+1)] = \ params['IpPermissions.1.IpRanges.%d.CidrIp' % (i + 1)] = \
single_cidr_ip single_cidr_ip
if dry_run: if dry_run:
params['DryRun'] = 'true' params['DryRun'] = 'true'
@@ -3278,7 +3277,7 @@ class EC2Connection(AWSQueryConnection):
:rtype: bool :rtype: bool
:return: True if successful. :return: True if successful.
""" """
params = {'GroupName':group_name} params = {'GroupName': group_name}
if src_security_group_name: if src_security_group_name:
params['SourceSecurityGroupName'] = src_security_group_name params['SourceSecurityGroupName'] = src_security_group_name
if src_security_group_owner_id: if src_security_group_owner_id:
@@ -3476,7 +3475,7 @@ class EC2Connection(AWSQueryConnection):
if dry_run: if dry_run:
params['DryRun'] = 'true' params['DryRun'] = 'true'
regions = self.get_list('DescribeRegions', params, regions = self.get_list('DescribeRegions', params,
[('item', RegionInfo)], verb='POST') [('item', RegionInfo)], verb='POST')
for region in regions: for region in regions:
region.connection_cls = EC2Connection region.connection_cls = EC2Connection
return regions return regions
@@ -4110,7 +4109,7 @@ class EC2Connection(AWSQueryConnection):
:rtype: bool :rtype: bool
:return: True if successful :return: True if successful
""" """
params = {'GroupName':name, 'Strategy':strategy} params = {'GroupName': name, 'Strategy': strategy}
if dry_run: if dry_run:
params['DryRun'] = 'true' params['DryRun'] = 'true'
group = self.get_status('CreatePlacementGroup', params, verb='POST') group = self.get_status('CreatePlacementGroup', params, verb='POST')
@@ -4127,7 +4126,7 @@ class EC2Connection(AWSQueryConnection):
:param dry_run: Set to True if the operation should not actually run. :param dry_run: Set to True if the operation should not actually run.
""" """
params = {'GroupName':name} params = {'GroupName': name}
if dry_run: if dry_run:
params['DryRun'] = 'true' params['DryRun'] = 'true'
return self.get_status('DeletePlacementGroup', params, verb='POST') return self.get_status('DeletePlacementGroup', params, verb='POST')
@@ -4139,9 +4138,9 @@ class EC2Connection(AWSQueryConnection):
i = 1 i = 1
for key in keys: for key in keys:
value = tags[key] value = tags[key]
params['Tag.%d.Key'%i] = key params['Tag.%d.Key' % i] = key
if value is not None: if value is not None:
params['Tag.%d.Value'%i] = value params['Tag.%d.Value' % i] = value
i += 1 i += 1
def get_all_tags(self, filters=None, dry_run=False, max_results=None): def get_all_tags(self, filters=None, dry_run=False, max_results=None):
@@ -4240,7 +4239,7 @@ class EC2Connection(AWSQueryConnection):
:type network_interface_ids: list :type network_interface_ids: list
:param network_interface_ids: a list of strings representing ENI IDs :param network_interface_ids: a list of strings representing ENI IDs
:type filters: dict :type filters: dict
:param filters: Optional filters that can be used to limit :param filters: Optional filters that can be used to limit
the results returned. Filters are provided the results returned. Filters are provided
@@ -4409,7 +4408,7 @@ class EC2Connection(AWSQueryConnection):
if dry_run: if dry_run:
params['DryRun'] = 'true' params['DryRun'] = 'true'
return self.get_object('CopyImage', params, CopyImage, return self.get_object('CopyImage', params, CopyImage,
verb='POST') verb='POST')
def describe_account_attributes(self, attribute_names=None, dry_run=False): def describe_account_attributes(self, attribute_names=None, dry_run=False):
""" """

View File

@@ -25,6 +25,7 @@ Represents an EC2 Object
""" """
from boto.ec2.tag import TagSet from boto.ec2.tag import TagSet
class EC2Object(object): class EC2Object(object):
def __init__(self, connection=None): def __init__(self, connection=None):
@@ -64,7 +65,7 @@ class TaggedEC2Object(EC2Object):
def add_tag(self, key, value='', dry_run=False): def add_tag(self, key, value='', dry_run=False):
""" """
Add a tag to this object. Tag's are stored by AWS and can be used Add a tag to this object. Tags are stored by AWS and can be used
to organize and filter resources. Adding a tag involves a round-trip to organize and filter resources. Adding a tag involves a round-trip
to the EC2 service. to the EC2 service.
@@ -76,14 +77,7 @@ class TaggedEC2Object(EC2Object):
If you want only the tag name and no value, the If you want only the tag name and no value, the
value should be the empty string. value should be the empty string.
""" """
status = self.connection.create_tags( self.add_tags({key: value}, dry_run)
[self.id],
{key : value},
dry_run=dry_run
)
if self.tags is None:
self.tags = TagSet()
self.tags[key] = value
def add_tags(self, tags, dry_run=False): def add_tags(self, tags, dry_run=False):
""" """
@@ -116,21 +110,35 @@ class TaggedEC2Object(EC2Object):
:type value: str :type value: str
:param value: An optional value that can be stored with the tag. :param value: An optional value that can be stored with the tag.
If a value is provided, it must match the value If a value is provided, it must match the value currently
currently stored in EC2. If not, the tag will not stored in EC2. If not, the tag will not be removed. If
be removed. If a value of None is provided, all a value of None is provided, the tag will be
tags with the specified name will be deleted. unconditionally deleted.
NOTE: There is an important distinction between NOTE: There is an important distinction between a value
a value of '' and a value of None. of '' and a value of None.
"""
self.remove_tags({key: value}, dry_run)
def remove_tags(self, tags, dry_run=False):
"""
Removes tags from this object. Removing tags involves a round-trip
to the EC2 service.
:type tags: dict
:param tags: A dictionary of key-value pairs for the tags being removed.
For each key, the provided value must match the value
currently stored in EC2. If not, that particular tag will
not be removed. However, if a value of None is provided,
the tag will be unconditionally deleted.
NOTE: There is an important distinction between a value of
'' and a value of None.
""" """
if value is not None:
tags = {key : value}
else:
tags = [key]
status = self.connection.delete_tags( status = self.connection.delete_tags(
[self.id], [self.id],
tags, tags,
dry_run=dry_run dry_run=dry_run
) )
if key in self.tags: for key, value in tags.items():
del self.tags[key] if key in self.tags:
if value is None or value == self.tags[key]:
del self.tags[key]

View File

@@ -400,6 +400,7 @@ class ELBConnection(AWSQueryConnection):
:param attribute: The attribute you wish to change. :param attribute: The attribute you wish to change.
* crossZoneLoadBalancing - Boolean (true) * crossZoneLoadBalancing - Boolean (true)
* connectingSettings - :py:class:`ConnectionSettingAttribute` instance
* accessLog - :py:class:`AccessLogAttribute` instance * accessLog - :py:class:`AccessLogAttribute` instance
* connectionDraining - :py:class:`ConnectionDrainingAttribute` instance * connectionDraining - :py:class:`ConnectionDrainingAttribute` instance
@@ -436,6 +437,9 @@ class ELBConnection(AWSQueryConnection):
value.enabled and 'true' or 'false' value.enabled and 'true' or 'false'
params['LoadBalancerAttributes.ConnectionDraining.Timeout'] = \ params['LoadBalancerAttributes.ConnectionDraining.Timeout'] = \
value.timeout value.timeout
elif attribute.lower() == 'connectingsettings':
params['LoadBalancerAttributes.ConnectionSettings.IdleTimeout'] = \
value.idle_timeout
else: else:
raise ValueError('InvalidAttribute', attribute) raise ValueError('InvalidAttribute', attribute)
return self.get_status('ModifyLoadBalancerAttributes', params, return self.get_status('ModifyLoadBalancerAttributes', params,
@@ -468,6 +472,7 @@ class ELBConnection(AWSQueryConnection):
* accessLog - :py:class:`AccessLogAttribute` instance * accessLog - :py:class:`AccessLogAttribute` instance
* crossZoneLoadBalancing - Boolean * crossZoneLoadBalancing - Boolean
* connectingSettings - :py:class:`ConnectionSettingAttribute` instance
* connectionDraining - :py:class:`ConnectionDrainingAttribute` * connectionDraining - :py:class:`ConnectionDrainingAttribute`
instance instance
@@ -481,6 +486,8 @@ class ELBConnection(AWSQueryConnection):
return attributes.cross_zone_load_balancing.enabled return attributes.cross_zone_load_balancing.enabled
if attribute.lower() == 'connectiondraining': if attribute.lower() == 'connectiondraining':
return attributes.connection_draining return attributes.connection_draining
if attribute.lower() == 'connectingsettings':
return attributes.connecting_settings
return None return None
def register_instances(self, load_balancer_name, instances): def register_instances(self, load_balancer_name, instances):

View File

@@ -19,6 +19,24 @@
# #
# Created by Chris Huegle for TellApart, Inc. # Created by Chris Huegle for TellApart, Inc.
class ConnectionSettingAttribute(object):
"""
Represents the ConnectionSetting segment of ELB Attributes.
"""
def __init__(self, connection=None):
self.idle_timeout = None
def __repr__(self):
return 'ConnectionSettingAttribute(%s)' % (
self.idle_timeout)
def startElement(self, name, attrs, connection):
pass
def endElement(self, name, value, connection):
if name == 'IdleTimeout':
self.idle_timeout = int(value)
class CrossZoneLoadBalancingAttribute(object): class CrossZoneLoadBalancingAttribute(object):
""" """
Represents the CrossZoneLoadBalancing segement of ELB Attributes. Represents the CrossZoneLoadBalancing segement of ELB Attributes.
@@ -40,6 +58,7 @@ class CrossZoneLoadBalancingAttribute(object):
else: else:
self.enabled = False self.enabled = False
class AccessLogAttribute(object): class AccessLogAttribute(object):
""" """
Represents the AccessLog segment of ELB attributes. Represents the AccessLog segment of ELB attributes.
@@ -74,6 +93,7 @@ class AccessLogAttribute(object):
elif name == 'EmitInterval': elif name == 'EmitInterval':
self.emit_interval = int(value) self.emit_interval = int(value)
class ConnectionDrainingAttribute(object): class ConnectionDrainingAttribute(object):
""" """
Represents the ConnectionDraining segment of ELB attributes. Represents the ConnectionDraining segment of ELB attributes.
@@ -100,6 +120,7 @@ class ConnectionDrainingAttribute(object):
elif name == 'Timeout': elif name == 'Timeout':
self.timeout = int(value) self.timeout = int(value)
class LbAttributes(object): class LbAttributes(object):
""" """
Represents the Attributes of an Elastic Load Balancer. Represents the Attributes of an Elastic Load Balancer.
@@ -107,15 +128,17 @@ class LbAttributes(object):
def __init__(self, connection=None): def __init__(self, connection=None):
self.connection = connection self.connection = connection
self.cross_zone_load_balancing = CrossZoneLoadBalancingAttribute( self.cross_zone_load_balancing = CrossZoneLoadBalancingAttribute(
self.connection) self.connection)
self.access_log = AccessLogAttribute(self.connection) self.access_log = AccessLogAttribute(self.connection)
self.connection_draining = ConnectionDrainingAttribute(self.connection) self.connection_draining = ConnectionDrainingAttribute(self.connection)
self.connecting_settings = ConnectionSettingAttribute(self.connection)
def __repr__(self): def __repr__(self):
return 'LbAttributes(%s, %s, %s)' % ( return 'LbAttributes(%s, %s, %s, %s)' % (
repr(self.cross_zone_load_balancing), repr(self.cross_zone_load_balancing),
repr(self.access_log), repr(self.access_log),
repr(self.connection_draining)) repr(self.connection_draining),
repr(self.connecting_settings))
def startElement(self, name, attrs, connection): def startElement(self, name, attrs, connection):
if name == 'CrossZoneLoadBalancing': if name == 'CrossZoneLoadBalancing':
@@ -124,6 +147,8 @@ class LbAttributes(object):
return self.access_log return self.access_log
if name == 'ConnectionDraining': if name == 'ConnectionDraining':
return self.connection_draining return self.connection_draining
if name == 'ConnectionSettings':
return self.connecting_settings
def endElement(self, name, value, connection): def endElement(self, name, value, connection):
pass pass

View File

@@ -19,6 +19,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
class InstanceState(object): class InstanceState(object):
""" """
Represents the state of an EC2 Load Balancer Instance Represents the state of an EC2 Load Balancer Instance

View File

@@ -80,6 +80,8 @@ class Listener(object):
return self.instance_port return self.instance_port
if key == 2: if key == 2:
return self.protocol return self.protocol
if key == 4: if key == 3:
return self.instance_protocol return self.instance_protocol
if key == 4:
return self.ssl_certificate_id
raise KeyError raise KeyError

View File

@@ -106,4 +106,3 @@ class Policies(object):
def endElement(self, name, value, connection): def endElement(self, name, value, connection):
return return

View File

@@ -19,6 +19,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
class SecurityGroup(object): class SecurityGroup(object):
def __init__(self, connection=None): def __init__(self, connection=None):
self.name = None self.name = None
@@ -35,4 +36,3 @@ class SecurityGroup(object):
self.name = value self.name = value
elif name == 'OwnerAlias': elif name == 'OwnerAlias':
self.owner_alias = value self.owner_alias = value

View File

@@ -20,6 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
class Group(object): class Group(object):
def __init__(self, parent=None): def __init__(self, parent=None):
self.id = None self.id = None
@@ -35,4 +36,3 @@ class Group(object):
self.name = value self.name = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -41,6 +41,7 @@ class BillingProducts(list):
if name == 'billingProduct': if name == 'billingProduct':
self.append(value) self.append(value)
class Image(TaggedEC2Object): class Image(TaggedEC2Object):
""" """
Represents an EC2 Image Represents an EC2 Image
@@ -106,7 +107,7 @@ class Image(TaggedEC2Object):
self.is_public = True self.is_public = True
else: else:
raise Exception( raise Exception(
'Unexpected value of isPublic %s for image %s'%( 'Unexpected value of isPublic %s for image %s' % (
value, value,
self.id self.id
) )
@@ -368,7 +369,7 @@ class Image(TaggedEC2Object):
) )
def get_kernel(self, dry_run=False): def get_kernel(self, dry_run=False):
img_attrs =self.connection.get_image_attribute( img_attrs = self.connection.get_image_attribute(
self.id, self.id,
'kernel', 'kernel',
dry_run=dry_run dry_run=dry_run

View File

@@ -14,16 +14,17 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
class InstanceInfo(object): class InstanceInfo(object):
""" """
Represents an EC2 Instance status response from CloudWatch Represents an EC2 Instance status response from CloudWatch
""" """
def __init__(self, connection=None, id=None, state=None): def __init__(self, connection=None, id=None, state=None):
""" """
:ivar str id: The instance's EC2 ID. :ivar str id: The instance's EC2 ID.
@@ -46,6 +47,3 @@ class InstanceInfo(object):
self.state = value self.state = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -44,7 +44,7 @@ class InstanceType(EC2Object):
def __repr__(self): def __repr__(self):
return 'InstanceType:%s-%s,%s,%s' % (self.name, self.cores, return 'InstanceType:%s-%s,%s,%s' % (self.name, self.cores,
self.memory, self.disk) self.memory, self.disk)
def endElement(self, name, value, connection): def endElement(self, name, value, connection):
if name == 'name': if name == 'name':

View File

@@ -27,6 +27,7 @@ import os
from boto.ec2.ec2object import EC2Object from boto.ec2.ec2object import EC2Object
from boto.exception import BotoClientError from boto.exception import BotoClientError
class KeyPair(EC2Object): class KeyPair(EC2Object):
def __init__(self, connection=None): def __init__(self, connection=None):
@@ -108,6 +109,3 @@ class KeyPair(EC2Object):
rconn = region.connect(**conn_params) rconn = region.connect(**conn_params)
kp = rconn.create_key_pair(self.name, dry_run=dry_run) kp = rconn.create_key_pair(self.name, dry_run=dry_run)
return kp return kp

View File

@@ -119,8 +119,7 @@ class NetworkInterface(TaggedEC2Object):
return 'NetworkInterface:%s' % self.id return 'NetworkInterface:%s' % self.id
def startElement(self, name, attrs, connection): def startElement(self, name, attrs, connection):
retval = super(NetworkInterface, self).startElement(name, attrs, retval = super(NetworkInterface, self).startElement(name, attrs, connection)
connection)
if retval is not None: if retval is not None:
return retval return retval
if name == 'groupSet': if name == 'groupSet':
@@ -313,10 +312,10 @@ class NetworkInterfaceCollection(list):
if spec.associate_public_ip_address is not None: if spec.associate_public_ip_address is not None:
if not params[full_prefix + 'DeviceIndex'] in (0, '0'): if not params[full_prefix + 'DeviceIndex'] in (0, '0'):
raise BotoClientError( raise BotoClientError(
"Only the interface with device index of 0 can " + \ "Only the interface with device index of 0 can " + \
"be provided when using " + \ "be provided when using " + \
"'associate_public_ip_address'." "'associate_public_ip_address'."
) )
if len(self) > 1: if len(self) > 1:
raise BotoClientError( raise BotoClientError(

View File

@@ -24,6 +24,7 @@ Represents an EC2 Placement Group
from boto.ec2.ec2object import EC2Object from boto.ec2.ec2object import EC2Object
from boto.exception import BotoClientError from boto.exception import BotoClientError
class PlacementGroup(EC2Object): class PlacementGroup(EC2Object):
def __init__(self, connection=None, name=None, strategy=None, state=None): def __init__(self, connection=None, name=None, strategy=None, state=None):
@@ -50,5 +51,3 @@ class PlacementGroup(EC2Object):
self.name, self.name,
dry_run=dry_run dry_run=dry_run
) )

View File

@@ -23,6 +23,7 @@
from boto.regioninfo import RegionInfo from boto.regioninfo import RegionInfo
class EC2RegionInfo(RegionInfo): class EC2RegionInfo(RegionInfo):
""" """
Represents an EC2 Region Represents an EC2 Region

View File

@@ -128,9 +128,9 @@ class ReservedInstance(ReservedInstancesOffering):
usage_price=None, description=None, usage_price=None, description=None,
instance_count=None, state=None): instance_count=None, state=None):
super(ReservedInstance, self).__init__(connection, id, instance_type, super(ReservedInstance, self).__init__(connection, id, instance_type,
availability_zone, duration, availability_zone, duration,
fixed_price, usage_price, fixed_price, usage_price,
description) description)
self.instance_count = instance_count self.instance_count = instance_count
self.state = state self.state = state
self.start = None self.start = None

View File

@@ -44,8 +44,7 @@ class SecurityGroup(TaggedEC2Object):
return 'SecurityGroup:%s' % self.name return 'SecurityGroup:%s' % self.name
def startElement(self, name, attrs, connection): def startElement(self, name, attrs, connection):
retval = super(SecurityGroup, self).startElement(name, attrs, retval = super(SecurityGroup, self).startElement(name, attrs, connection)
connection)
if retval is not None: if retval is not None:
return retval return retval
if name == 'ipPermissions': if name == 'ipPermissions':

View File

@@ -185,6 +185,3 @@ class SnapshotAttribute(object):
self.snapshot_id = value self.snapshot_id = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -25,10 +25,11 @@ Represents an EC2 Spot Instance Datafeed Subscription
from boto.ec2.ec2object import EC2Object from boto.ec2.ec2object import EC2Object
from boto.ec2.spotinstancerequest import SpotInstanceStateFault from boto.ec2.spotinstancerequest import SpotInstanceStateFault
class SpotDatafeedSubscription(EC2Object): class SpotDatafeedSubscription(EC2Object):
def __init__(self, connection=None, owner_id=None, def __init__(self, connection=None, owner_id=None,
bucket=None, prefix=None, state=None,fault=None): bucket=None, prefix=None, state=None, fault=None):
super(SpotDatafeedSubscription, self).__init__(connection) super(SpotDatafeedSubscription, self).__init__(connection)
self.owner_id = owner_id self.owner_id = owner_id
self.bucket = bucket self.bucket = bucket
@@ -62,4 +63,3 @@ class SpotDatafeedSubscription(EC2Object):
return self.connection.delete_spot_datafeed_subscription( return self.connection.delete_spot_datafeed_subscription(
dry_run=dry_run dry_run=dry_run
) )

View File

@@ -25,6 +25,7 @@ Represents an EC2 Spot Instance Request
from boto.ec2.ec2object import EC2Object from boto.ec2.ec2object import EC2Object
class SpotPriceHistory(EC2Object): class SpotPriceHistory(EC2Object):
def __init__(self, connection=None): def __init__(self, connection=None):
@@ -51,5 +52,3 @@ class SpotPriceHistory(EC2Object):
self.availability_zone = value self.availability_zone = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -1,59 +0,0 @@
# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
from boto.ec2.ec2object import EC2Object
class VmType(EC2Object):
"""
Represents an EC2 VM Type
:ivar name: The name of the vm type
:ivar cores: The number of cpu cores for this vm type
:ivar memory: The amount of memory in megabytes for this vm type
:ivar disk: The amount of disk space in gigabytes for this vm type
"""
def __init__(self, connection=None, name=None, cores=None,
memory=None, disk=None):
super(VmType, self).__init__(connection)
self.connection = connection
self.name = name
self.cores = cores
self.memory = memory
self.disk = disk
def __repr__(self):
return 'VmType:%s-%s,%s,%s' % (self.name, self.cores,
self.memory, self.disk)
def endElement(self, name, value, connection):
if name == 'euca:name':
self.name = value
elif name == 'euca:cpu':
self.cores = value
elif name == 'euca:disk':
self.disk = value
elif name == 'euca:memory':
self.memory = value
else:
setattr(self, name, value)

View File

@@ -24,6 +24,7 @@ Represents an EC2 Availability Zone
""" """
from boto.ec2.ec2object import EC2Object from boto.ec2.ec2object import EC2Object
class MessageSet(list): class MessageSet(list):
""" """
A list object that contains messages associated with A list object that contains messages associated with
@@ -39,6 +40,7 @@ class MessageSet(list):
else: else:
setattr(self, name, value) setattr(self, name, value)
class Zone(EC2Object): class Zone(EC2Object):
""" """
Represents an Availability Zone. Represents an Availability Zone.
@@ -74,7 +76,3 @@ class Zone(EC2Object):
self.region_name = value self.region_name = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -58,7 +58,6 @@ class ElastiCacheConnection(AWSQueryConnection):
super(ElastiCacheConnection, self).__init__(**kwargs) super(ElastiCacheConnection, self).__init__(**kwargs)
self.region = region self.region = region
def _required_auth_capability(self): def _required_auth_capability(self):
return ['hmac-v4'] return ['hmac-v4']

View File

@@ -922,7 +922,7 @@ class ElasticTranscoderConnection(AWSAuthConnection):
if headers is None: if headers is None:
headers = {} headers = {}
response = super(ElasticTranscoderConnection, self).make_request( response = super(ElasticTranscoderConnection, self).make_request(
verb, resource, headers=headers, data=data) verb, resource, headers=headers, data=data, params=params)
body = json.loads(response.read().decode('utf-8')) body = json.loads(response.read().decode('utf-8'))
if response.status == expected_status: if response.status == expected_status:
return body return body

View File

@@ -9,7 +9,8 @@
"us-east-1": "autoscaling.us-east-1.amazonaws.com", "us-east-1": "autoscaling.us-east-1.amazonaws.com",
"us-gov-west-1": "autoscaling.us-gov-west-1.amazonaws.com", "us-gov-west-1": "autoscaling.us-gov-west-1.amazonaws.com",
"us-west-1": "autoscaling.us-west-1.amazonaws.com", "us-west-1": "autoscaling.us-west-1.amazonaws.com",
"us-west-2": "autoscaling.us-west-2.amazonaws.com" "us-west-2": "autoscaling.us-west-2.amazonaws.com",
"eu-central-1": "autoscaling.eu-central-1.amazonaws.com"
}, },
"cloudformation": { "cloudformation": {
"ap-northeast-1": "cloudformation.ap-northeast-1.amazonaws.com", "ap-northeast-1": "cloudformation.ap-northeast-1.amazonaws.com",
@@ -19,8 +20,10 @@
"eu-west-1": "cloudformation.eu-west-1.amazonaws.com", "eu-west-1": "cloudformation.eu-west-1.amazonaws.com",
"sa-east-1": "cloudformation.sa-east-1.amazonaws.com", "sa-east-1": "cloudformation.sa-east-1.amazonaws.com",
"us-east-1": "cloudformation.us-east-1.amazonaws.com", "us-east-1": "cloudformation.us-east-1.amazonaws.com",
"us-gov-west-1": "cloudformation.us-gov-west-1.amazonaws.com",
"us-west-1": "cloudformation.us-west-1.amazonaws.com", "us-west-1": "cloudformation.us-west-1.amazonaws.com",
"us-west-2": "cloudformation.us-west-2.amazonaws.com" "us-west-2": "cloudformation.us-west-2.amazonaws.com",
"eu-central-1": "cloudformation.eu-central-1.amazonaws.com"
}, },
"cloudfront": { "cloudfront": {
"ap-northeast-1": "cloudfront.amazonaws.com", "ap-northeast-1": "cloudfront.amazonaws.com",
@@ -30,18 +33,30 @@
"sa-east-1": "cloudfront.amazonaws.com", "sa-east-1": "cloudfront.amazonaws.com",
"us-east-1": "cloudfront.amazonaws.com", "us-east-1": "cloudfront.amazonaws.com",
"us-west-1": "cloudfront.amazonaws.com", "us-west-1": "cloudfront.amazonaws.com",
"us-west-2": "cloudfront.amazonaws.com" "us-west-2": "cloudfront.amazonaws.com",
"eu-central-1": "cloudfront.amazonaws.com"
}, },
"cloudsearch": { "cloudsearch": {
"ap-southeast-1": "cloudsearch.ap-southeast-1.amazonaws.com", "ap-southeast-1": "cloudsearch.ap-southeast-1.amazonaws.com",
"ap-southeast-2": "cloudsearch.ap-southeast-2.amazonaws.com",
"ap-northeast-1": "cloudsearch.ap-northeast-1.amazonaws.com",
"sa-east-1": "cloudsearch.sa-east-1.amazonaws.com",
"eu-west-1": "cloudsearch.eu-west-1.amazonaws.com", "eu-west-1": "cloudsearch.eu-west-1.amazonaws.com",
"us-east-1": "cloudsearch.us-east-1.amazonaws.com", "us-east-1": "cloudsearch.us-east-1.amazonaws.com",
"us-west-1": "cloudsearch.us-west-1.amazonaws.com", "us-west-1": "cloudsearch.us-west-1.amazonaws.com",
"us-west-2": "cloudsearch.us-west-2.amazonaws.com" "us-west-2": "cloudsearch.us-west-2.amazonaws.com",
"eu-central-1": "cloudsearch.eu-central-1.amazonaws.com"
}, },
"cloudtrail": { "cloudtrail": {
"ap-northeast-1": "cloudtrail.ap-northeast-1.amazonaws.com",
"ap-southeast-1": "cloudtrail.ap-southeast-1.amazonaws.com",
"ap-southeast-2": "cloudtrail.ap-southeast-2.amazonaws.com",
"eu-west-1": "cloudtrail.eu-west-1.amazonaws.com",
"sa-east-1": "cloudtrail.sa-east-1.amazonaws.com",
"us-east-1": "cloudtrail.us-east-1.amazonaws.com", "us-east-1": "cloudtrail.us-east-1.amazonaws.com",
"us-west-2": "cloudtrail.us-west-2.amazonaws.com" "us-west-1": "cloudtrail.us-west-1.amazonaws.com",
"us-west-2": "cloudtrail.us-west-2.amazonaws.com",
"eu-central-1": "cloudtrail.eu-central-1.amazonaws.com"
}, },
"cloudwatch": { "cloudwatch": {
"ap-northeast-1": "monitoring.ap-northeast-1.amazonaws.com", "ap-northeast-1": "monitoring.ap-northeast-1.amazonaws.com",
@@ -53,14 +68,22 @@
"us-east-1": "monitoring.us-east-1.amazonaws.com", "us-east-1": "monitoring.us-east-1.amazonaws.com",
"us-gov-west-1": "monitoring.us-gov-west-1.amazonaws.com", "us-gov-west-1": "monitoring.us-gov-west-1.amazonaws.com",
"us-west-1": "monitoring.us-west-1.amazonaws.com", "us-west-1": "monitoring.us-west-1.amazonaws.com",
"us-west-2": "monitoring.us-west-2.amazonaws.com" "us-west-2": "monitoring.us-west-2.amazonaws.com",
"eu-central-1": "monitoring.eu-central-1.amazonaws.com"
},
"cognito-identity": {
"us-east-1": "cognito-identity.us-east-1.amazonaws.com"
},
"cognito-sync": {
"us-east-1": "cognito-sync.us-east-1.amazonaws.com"
}, },
"datapipeline": { "datapipeline": {
"us-east-1": "datapipeline.us-east-1.amazonaws.com", "us-east-1": "datapipeline.us-east-1.amazonaws.com",
"us-west-2": "datapipeline.us-west-2.amazonaws.com", "us-west-2": "datapipeline.us-west-2.amazonaws.com",
"eu-west-1": "datapipeline.eu-west-1.amazonaws.com", "eu-west-1": "datapipeline.eu-west-1.amazonaws.com",
"ap-southeast-2": "datapipeline.ap-southeast-2.amazonaws.com", "ap-southeast-2": "datapipeline.ap-southeast-2.amazonaws.com",
"ap-northeast-1": "datapipeline.ap-northeast-1.amazonaws.com" "ap-northeast-1": "datapipeline.ap-northeast-1.amazonaws.com",
"eu-central-1": "datapipeline.eu-central-1.amazonaws.com"
}, },
"directconnect": { "directconnect": {
"ap-northeast-1": "directconnect.ap-northeast-1.amazonaws.com", "ap-northeast-1": "directconnect.ap-northeast-1.amazonaws.com",
@@ -70,7 +93,8 @@
"sa-east-1": "directconnect.sa-east-1.amazonaws.com", "sa-east-1": "directconnect.sa-east-1.amazonaws.com",
"us-east-1": "directconnect.us-east-1.amazonaws.com", "us-east-1": "directconnect.us-east-1.amazonaws.com",
"us-west-1": "directconnect.us-west-1.amazonaws.com", "us-west-1": "directconnect.us-west-1.amazonaws.com",
"us-west-2": "directconnect.us-west-2.amazonaws.com" "us-west-2": "directconnect.us-west-2.amazonaws.com",
"eu-central-1": "directconnect.eu-central-1.amazonaws.com"
}, },
"dynamodb": { "dynamodb": {
"ap-northeast-1": "dynamodb.ap-northeast-1.amazonaws.com", "ap-northeast-1": "dynamodb.ap-northeast-1.amazonaws.com",
@@ -82,7 +106,8 @@
"us-east-1": "dynamodb.us-east-1.amazonaws.com", "us-east-1": "dynamodb.us-east-1.amazonaws.com",
"us-gov-west-1": "dynamodb.us-gov-west-1.amazonaws.com", "us-gov-west-1": "dynamodb.us-gov-west-1.amazonaws.com",
"us-west-1": "dynamodb.us-west-1.amazonaws.com", "us-west-1": "dynamodb.us-west-1.amazonaws.com",
"us-west-2": "dynamodb.us-west-2.amazonaws.com" "us-west-2": "dynamodb.us-west-2.amazonaws.com",
"eu-central-1": "dynamodb.eu-central-1.amazonaws.com"
}, },
"ec2": { "ec2": {
"ap-northeast-1": "ec2.ap-northeast-1.amazonaws.com", "ap-northeast-1": "ec2.ap-northeast-1.amazonaws.com",
@@ -94,7 +119,8 @@
"us-east-1": "ec2.us-east-1.amazonaws.com", "us-east-1": "ec2.us-east-1.amazonaws.com",
"us-gov-west-1": "ec2.us-gov-west-1.amazonaws.com", "us-gov-west-1": "ec2.us-gov-west-1.amazonaws.com",
"us-west-1": "ec2.us-west-1.amazonaws.com", "us-west-1": "ec2.us-west-1.amazonaws.com",
"us-west-2": "ec2.us-west-2.amazonaws.com" "us-west-2": "ec2.us-west-2.amazonaws.com",
"eu-central-1": "ec2.eu-central-1.amazonaws.com"
}, },
"elasticache": { "elasticache": {
"ap-northeast-1": "elasticache.ap-northeast-1.amazonaws.com", "ap-northeast-1": "elasticache.ap-northeast-1.amazonaws.com",
@@ -105,7 +131,8 @@
"sa-east-1": "elasticache.sa-east-1.amazonaws.com", "sa-east-1": "elasticache.sa-east-1.amazonaws.com",
"us-east-1": "elasticache.us-east-1.amazonaws.com", "us-east-1": "elasticache.us-east-1.amazonaws.com",
"us-west-1": "elasticache.us-west-1.amazonaws.com", "us-west-1": "elasticache.us-west-1.amazonaws.com",
"us-west-2": "elasticache.us-west-2.amazonaws.com" "us-west-2": "elasticache.us-west-2.amazonaws.com",
"eu-central-1": "elasticache.eu-central-1.amazonaws.com"
}, },
"elasticbeanstalk": { "elasticbeanstalk": {
"ap-northeast-1": "elasticbeanstalk.ap-northeast-1.amazonaws.com", "ap-northeast-1": "elasticbeanstalk.ap-northeast-1.amazonaws.com",
@@ -115,7 +142,8 @@
"sa-east-1": "elasticbeanstalk.sa-east-1.amazonaws.com", "sa-east-1": "elasticbeanstalk.sa-east-1.amazonaws.com",
"us-east-1": "elasticbeanstalk.us-east-1.amazonaws.com", "us-east-1": "elasticbeanstalk.us-east-1.amazonaws.com",
"us-west-1": "elasticbeanstalk.us-west-1.amazonaws.com", "us-west-1": "elasticbeanstalk.us-west-1.amazonaws.com",
"us-west-2": "elasticbeanstalk.us-west-2.amazonaws.com" "us-west-2": "elasticbeanstalk.us-west-2.amazonaws.com",
"eu-central-1": "elasticbeanstalk.eu-central-1.amazonaws.com"
}, },
"elasticloadbalancing": { "elasticloadbalancing": {
"ap-northeast-1": "elasticloadbalancing.ap-northeast-1.amazonaws.com", "ap-northeast-1": "elasticloadbalancing.ap-northeast-1.amazonaws.com",
@@ -127,7 +155,8 @@
"us-east-1": "elasticloadbalancing.us-east-1.amazonaws.com", "us-east-1": "elasticloadbalancing.us-east-1.amazonaws.com",
"us-gov-west-1": "elasticloadbalancing.us-gov-west-1.amazonaws.com", "us-gov-west-1": "elasticloadbalancing.us-gov-west-1.amazonaws.com",
"us-west-1": "elasticloadbalancing.us-west-1.amazonaws.com", "us-west-1": "elasticloadbalancing.us-west-1.amazonaws.com",
"us-west-2": "elasticloadbalancing.us-west-2.amazonaws.com" "us-west-2": "elasticloadbalancing.us-west-2.amazonaws.com",
"eu-central-1": "elasticloadbalancing.eu-central-1.amazonaws.com"
}, },
"elasticmapreduce": { "elasticmapreduce": {
"ap-northeast-1": "ap-northeast-1.elasticmapreduce.amazonaws.com", "ap-northeast-1": "ap-northeast-1.elasticmapreduce.amazonaws.com",
@@ -139,7 +168,8 @@
"us-east-1": "elasticmapreduce.us-east-1.amazonaws.com", "us-east-1": "elasticmapreduce.us-east-1.amazonaws.com",
"us-gov-west-1": "us-gov-west-1.elasticmapreduce.amazonaws.com", "us-gov-west-1": "us-gov-west-1.elasticmapreduce.amazonaws.com",
"us-west-1": "us-west-1.elasticmapreduce.amazonaws.com", "us-west-1": "us-west-1.elasticmapreduce.amazonaws.com",
"us-west-2": "us-west-2.elasticmapreduce.amazonaws.com" "us-west-2": "us-west-2.elasticmapreduce.amazonaws.com",
"eu-central-1": "eu-central-1.elasticmapreduce.amazonaws.com"
}, },
"elastictranscoder": { "elastictranscoder": {
"ap-northeast-1": "elastictranscoder.ap-northeast-1.amazonaws.com", "ap-northeast-1": "elastictranscoder.ap-northeast-1.amazonaws.com",
@@ -147,7 +177,8 @@
"eu-west-1": "elastictranscoder.eu-west-1.amazonaws.com", "eu-west-1": "elastictranscoder.eu-west-1.amazonaws.com",
"us-east-1": "elastictranscoder.us-east-1.amazonaws.com", "us-east-1": "elastictranscoder.us-east-1.amazonaws.com",
"us-west-1": "elastictranscoder.us-west-1.amazonaws.com", "us-west-1": "elastictranscoder.us-west-1.amazonaws.com",
"us-west-2": "elastictranscoder.us-west-2.amazonaws.com" "us-west-2": "elastictranscoder.us-west-2.amazonaws.com",
"eu-central-1": "elastictranscoder.eu-central-1.amazonaws.com"
}, },
"glacier": { "glacier": {
"ap-northeast-1": "glacier.ap-northeast-1.amazonaws.com", "ap-northeast-1": "glacier.ap-northeast-1.amazonaws.com",
@@ -156,7 +187,8 @@
"eu-west-1": "glacier.eu-west-1.amazonaws.com", "eu-west-1": "glacier.eu-west-1.amazonaws.com",
"us-east-1": "glacier.us-east-1.amazonaws.com", "us-east-1": "glacier.us-east-1.amazonaws.com",
"us-west-1": "glacier.us-west-1.amazonaws.com", "us-west-1": "glacier.us-west-1.amazonaws.com",
"us-west-2": "glacier.us-west-2.amazonaws.com" "us-west-2": "glacier.us-west-2.amazonaws.com",
"eu-central-1": "glacier.eu-central-1.amazonaws.com"
}, },
"iam": { "iam": {
"ap-northeast-1": "iam.amazonaws.com", "ap-northeast-1": "iam.amazonaws.com",
@@ -181,10 +213,23 @@
"us-west-2": "importexport.amazonaws.com" "us-west-2": "importexport.amazonaws.com"
}, },
"kinesis": { "kinesis": {
"us-east-1": "kinesis.us-east-1.amazonaws.com" "us-east-1": "kinesis.us-east-1.amazonaws.com",
"us-west-2": "kinesis.us-west-2.amazonaws.com",
"eu-west-1": "kinesis.eu-west-1.amazonaws.com",
"ap-southeast-1": "kinesis.ap-southeast-1.amazonaws.com",
"ap-southeast-2": "kinesis.ap-southeast-2.amazonaws.com",
"ap-northeast-1": "kinesis.ap-northeast-1.amazonaws.com",
"eu-central-1": "kinesis.eu-central-1.amazonaws.com"
},
"logs": {
"us-east-1": "logs.us-east-1.amazonaws.com",
"us-west-2": "logs.us-west-2.amazonaws.com",
"eu-west-1": "logs.eu-west-1.amazonaws.com",
"eu-central-1": "logs.eu-central-1.amazonaws.com"
}, },
"opsworks": { "opsworks": {
"us-east-1": "opsworks.us-east-1.amazonaws.com" "us-east-1": "opsworks.us-east-1.amazonaws.com",
"eu-central-1": "opsworks.eu-central-1.amazonaws.com"
}, },
"rds": { "rds": {
"ap-northeast-1": "rds.ap-northeast-1.amazonaws.com", "ap-northeast-1": "rds.ap-northeast-1.amazonaws.com",
@@ -196,7 +241,8 @@
"us-east-1": "rds.amazonaws.com", "us-east-1": "rds.amazonaws.com",
"us-gov-west-1": "rds.us-gov-west-1.amazonaws.com", "us-gov-west-1": "rds.us-gov-west-1.amazonaws.com",
"us-west-1": "rds.us-west-1.amazonaws.com", "us-west-1": "rds.us-west-1.amazonaws.com",
"us-west-2": "rds.us-west-2.amazonaws.com" "us-west-2": "rds.us-west-2.amazonaws.com",
"eu-central-1": "rds.eu-central-1.amazonaws.com"
}, },
"redshift": { "redshift": {
"ap-northeast-1": "redshift.ap-northeast-1.amazonaws.com", "ap-northeast-1": "redshift.ap-northeast-1.amazonaws.com",
@@ -204,7 +250,8 @@
"ap-southeast-2": "redshift.ap-southeast-2.amazonaws.com", "ap-southeast-2": "redshift.ap-southeast-2.amazonaws.com",
"eu-west-1": "redshift.eu-west-1.amazonaws.com", "eu-west-1": "redshift.eu-west-1.amazonaws.com",
"us-east-1": "redshift.us-east-1.amazonaws.com", "us-east-1": "redshift.us-east-1.amazonaws.com",
"us-west-2": "redshift.us-west-2.amazonaws.com" "us-west-2": "redshift.us-west-2.amazonaws.com",
"eu-central-1": "redshift.eu-central-1.amazonaws.com"
}, },
"route53": { "route53": {
"ap-northeast-1": "route53.amazonaws.com", "ap-northeast-1": "route53.amazonaws.com",
@@ -216,6 +263,9 @@
"us-west-1": "route53.amazonaws.com", "us-west-1": "route53.amazonaws.com",
"us-west-2": "route53.amazonaws.com" "us-west-2": "route53.amazonaws.com"
}, },
"route53domains": {
"us-east-1": "route53domains.us-east-1.amazonaws.com"
},
"s3": { "s3": {
"ap-northeast-1": "s3-ap-northeast-1.amazonaws.com", "ap-northeast-1": "s3-ap-northeast-1.amazonaws.com",
"ap-southeast-1": "s3-ap-southeast-1.amazonaws.com", "ap-southeast-1": "s3-ap-southeast-1.amazonaws.com",
@@ -226,7 +276,8 @@
"us-east-1": "s3.amazonaws.com", "us-east-1": "s3.amazonaws.com",
"us-gov-west-1": "s3-us-gov-west-1.amazonaws.com", "us-gov-west-1": "s3-us-gov-west-1.amazonaws.com",
"us-west-1": "s3-us-west-1.amazonaws.com", "us-west-1": "s3-us-west-1.amazonaws.com",
"us-west-2": "s3-us-west-2.amazonaws.com" "us-west-2": "s3-us-west-2.amazonaws.com",
"eu-central-1": "s3.eu-central-1.amazonaws.com"
}, },
"sdb": { "sdb": {
"ap-northeast-1": "sdb.ap-northeast-1.amazonaws.com", "ap-northeast-1": "sdb.ap-northeast-1.amazonaws.com",
@@ -236,12 +287,14 @@
"sa-east-1": "sdb.sa-east-1.amazonaws.com", "sa-east-1": "sdb.sa-east-1.amazonaws.com",
"us-east-1": "sdb.amazonaws.com", "us-east-1": "sdb.amazonaws.com",
"us-west-1": "sdb.us-west-1.amazonaws.com", "us-west-1": "sdb.us-west-1.amazonaws.com",
"us-west-2": "sdb.us-west-2.amazonaws.com" "us-west-2": "sdb.us-west-2.amazonaws.com",
"eu-central-1": "sdb.eu-central-1.amazonaws.com"
}, },
"ses": { "ses": {
"eu-west-1": "email.eu-west-1.amazonaws.com", "eu-west-1": "email.eu-west-1.amazonaws.com",
"us-east-1": "email.us-east-1.amazonaws.com", "us-east-1": "email.us-east-1.amazonaws.com",
"us-west-2": "email.us-west-2.amazonaws.com" "us-west-2": "email.us-west-2.amazonaws.com",
"eu-central-1": "email.eu-central-1.amazonaws.com"
}, },
"sns": { "sns": {
"ap-northeast-1": "sns.ap-northeast-1.amazonaws.com", "ap-northeast-1": "sns.ap-northeast-1.amazonaws.com",
@@ -253,7 +306,8 @@
"us-east-1": "sns.us-east-1.amazonaws.com", "us-east-1": "sns.us-east-1.amazonaws.com",
"us-gov-west-1": "sns.us-gov-west-1.amazonaws.com", "us-gov-west-1": "sns.us-gov-west-1.amazonaws.com",
"us-west-1": "sns.us-west-1.amazonaws.com", "us-west-1": "sns.us-west-1.amazonaws.com",
"us-west-2": "sns.us-west-2.amazonaws.com" "us-west-2": "sns.us-west-2.amazonaws.com",
"eu-central-1": "sns.eu-central-1.amazonaws.com"
}, },
"sqs": { "sqs": {
"ap-northeast-1": "ap-northeast-1.queue.amazonaws.com", "ap-northeast-1": "ap-northeast-1.queue.amazonaws.com",
@@ -265,7 +319,8 @@
"us-east-1": "queue.amazonaws.com", "us-east-1": "queue.amazonaws.com",
"us-gov-west-1": "us-gov-west-1.queue.amazonaws.com", "us-gov-west-1": "us-gov-west-1.queue.amazonaws.com",
"us-west-1": "us-west-1.queue.amazonaws.com", "us-west-1": "us-west-1.queue.amazonaws.com",
"us-west-2": "us-west-2.queue.amazonaws.com" "us-west-2": "us-west-2.queue.amazonaws.com",
"eu-central-1": "eu-central-1.queue.amazonaws.com"
}, },
"storagegateway": { "storagegateway": {
"ap-northeast-1": "storagegateway.ap-northeast-1.amazonaws.com", "ap-northeast-1": "storagegateway.ap-northeast-1.amazonaws.com",
@@ -275,7 +330,8 @@
"sa-east-1": "storagegateway.sa-east-1.amazonaws.com", "sa-east-1": "storagegateway.sa-east-1.amazonaws.com",
"us-east-1": "storagegateway.us-east-1.amazonaws.com", "us-east-1": "storagegateway.us-east-1.amazonaws.com",
"us-west-1": "storagegateway.us-west-1.amazonaws.com", "us-west-1": "storagegateway.us-west-1.amazonaws.com",
"us-west-2": "storagegateway.us-west-2.amazonaws.com" "us-west-2": "storagegateway.us-west-2.amazonaws.com",
"eu-central-1": "storagegateway.eu-central-1.amazonaws.com"
}, },
"sts": { "sts": {
"ap-northeast-1": "sts.amazonaws.com", "ap-northeast-1": "sts.amazonaws.com",
@@ -290,7 +346,8 @@
"us-west-2": "sts.amazonaws.com" "us-west-2": "sts.amazonaws.com"
}, },
"support": { "support": {
"us-east-1": "support.us-east-1.amazonaws.com" "us-east-1": "support.us-east-1.amazonaws.com",
"eu-central-1": "support.eu-central-1.amazonaws.com"
}, },
"swf": { "swf": {
"ap-northeast-1": "swf.ap-northeast-1.amazonaws.com", "ap-northeast-1": "swf.ap-northeast-1.amazonaws.com",
@@ -302,6 +359,7 @@
"us-east-1": "swf.us-east-1.amazonaws.com", "us-east-1": "swf.us-east-1.amazonaws.com",
"us-gov-west-1": "swf.us-gov-west-1.amazonaws.com", "us-gov-west-1": "swf.us-gov-west-1.amazonaws.com",
"us-west-1": "swf.us-west-1.amazonaws.com", "us-west-1": "swf.us-west-1.amazonaws.com",
"us-west-2": "swf.us-west-2.amazonaws.com" "us-west-2": "swf.us-west-2.amazonaws.com",
"eu-central-1": "swf.eu-central-1.amazonaws.com"
} }
} }

View File

@@ -30,9 +30,10 @@ import xml.sax
import boto import boto
from boto import handler from boto import handler
from boto.compat import json, six, StandardError from boto.compat import json, StandardError
from boto.resultset import ResultSet from boto.resultset import ResultSet
class BotoClientError(StandardError): class BotoClientError(StandardError):
""" """
General Boto Client error (error accessing AWS) General Boto Client error (error accessing AWS)
@@ -112,7 +113,7 @@ class BotoServerError(StandardError):
try: try:
h = handler.XmlHandlerWrapper(self, self) h = handler.XmlHandlerWrapper(self, self)
h.parseString(self.body) h.parseString(self.body)
except (TypeError, xml.sax.SAXParseException) as pe: except (TypeError, xml.sax.SAXParseException):
# What if it's JSON? Let's try that. # What if it's JSON? Let's try that.
try: try:
parsed = json.loads(self.body) parsed = json.loads(self.body)
@@ -209,6 +210,7 @@ class StorageCreateError(BotoServerError):
else: else:
return super(StorageCreateError, self).endElement(name, value, connection) return super(StorageCreateError, self).endElement(name, value, connection)
class S3CreateError(StorageCreateError): class S3CreateError(StorageCreateError):
""" """
Error creating a bucket or key on S3. Error creating a bucket or key on S3.
@@ -294,15 +296,15 @@ class StorageResponseError(BotoServerError):
super(StorageResponseError, self).__init__(status, reason, body) super(StorageResponseError, self).__init__(status, reason, body)
def startElement(self, name, attrs, connection): def startElement(self, name, attrs, connection):
return super(StorageResponseError, self).startElement(name, attrs, return super(StorageResponseError, self).startElement(
connection) name, attrs, connection)
def endElement(self, name, value, connection): def endElement(self, name, value, connection):
if name == 'Resource': if name == 'Resource':
self.resource = value self.resource = value
else: else:
return super(StorageResponseError, self).endElement(name, value, return super(StorageResponseError, self).endElement(
connection) name, value, connection)
def _cleanupParsedProperties(self): def _cleanupParsedProperties(self):
super(StorageResponseError, self)._cleanupParsedProperties() super(StorageResponseError, self)._cleanupParsedProperties()
@@ -332,8 +334,8 @@ class EC2ResponseError(BotoServerError):
self.errors = None self.errors = None
self._errorResultSet = [] self._errorResultSet = []
super(EC2ResponseError, self).__init__(status, reason, body) super(EC2ResponseError, self).__init__(status, reason, body)
self.errors = [ (e.error_code, e.error_message) \ self.errors = [
for e in self._errorResultSet ] (e.error_code, e.error_message) for e in self._errorResultSet]
if len(self.errors): if len(self.errors):
self.error_code, self.error_message = self.errors[0] self.error_code, self.error_message = self.errors[0]
@@ -348,7 +350,7 @@ class EC2ResponseError(BotoServerError):
if name == 'RequestID': if name == 'RequestID':
self.request_id = value self.request_id = value
else: else:
return None # don't call subclass here return None # don't call subclass here
def _cleanupParsedProperties(self): def _cleanupParsedProperties(self):
super(EC2ResponseError, self)._cleanupParsedProperties() super(EC2ResponseError, self)._cleanupParsedProperties()
@@ -420,30 +422,35 @@ class SDBResponseError(BotoServerError):
""" """
pass pass
class AWSConnectionError(BotoClientError): class AWSConnectionError(BotoClientError):
""" """
General error connecting to Amazon Web Services. General error connecting to Amazon Web Services.
""" """
pass pass
class StorageDataError(BotoClientError): class StorageDataError(BotoClientError):
""" """
Error receiving data from a storage service. Error receiving data from a storage service.
""" """
pass pass
class S3DataError(StorageDataError): class S3DataError(StorageDataError):
""" """
Error receiving data from S3. Error receiving data from S3.
""" """
pass pass
class GSDataError(StorageDataError): class GSDataError(StorageDataError):
""" """
Error receiving data from GS. Error receiving data from GS.
""" """
pass pass
class InvalidUriError(Exception): class InvalidUriError(Exception):
"""Exception raised when URI is invalid.""" """Exception raised when URI is invalid."""
@@ -451,6 +458,7 @@ class InvalidUriError(Exception):
super(InvalidUriError, self).__init__(message) super(InvalidUriError, self).__init__(message)
self.message = message self.message = message
class InvalidAclError(Exception): class InvalidAclError(Exception):
"""Exception raised when ACL XML is invalid.""" """Exception raised when ACL XML is invalid."""
@@ -458,6 +466,7 @@ class InvalidAclError(Exception):
super(InvalidAclError, self).__init__(message) super(InvalidAclError, self).__init__(message)
self.message = message self.message = message
class InvalidCorsError(Exception): class InvalidCorsError(Exception):
"""Exception raised when CORS XML is invalid.""" """Exception raised when CORS XML is invalid."""
@@ -465,10 +474,12 @@ class InvalidCorsError(Exception):
super(InvalidCorsError, self).__init__(message) super(InvalidCorsError, self).__init__(message)
self.message = message self.message = message
class NoAuthHandlerFound(Exception): class NoAuthHandlerFound(Exception):
"""Is raised when no auth handlers were found ready to authenticate.""" """Is raised when no auth handlers were found ready to authenticate."""
pass pass
class InvalidLifecycleConfigError(Exception): class InvalidLifecycleConfigError(Exception):
"""Exception raised when GCS lifecycle configuration XML is invalid.""" """Exception raised when GCS lifecycle configuration XML is invalid."""
@@ -476,6 +487,7 @@ class InvalidLifecycleConfigError(Exception):
super(InvalidLifecycleConfigError, self).__init__(message) super(InvalidLifecycleConfigError, self).__init__(message)
self.message = message self.message = message
# Enum class for resumable upload failure disposition. # Enum class for resumable upload failure disposition.
class ResumableTransferDisposition(object): class ResumableTransferDisposition(object):
# START_OVER means an attempt to resume an existing transfer failed, # START_OVER means an attempt to resume an existing transfer failed,
@@ -500,6 +512,7 @@ class ResumableTransferDisposition(object):
# upload ID. # upload ID.
ABORT = 'ABORT' ABORT = 'ABORT'
class ResumableUploadException(Exception): class ResumableUploadException(Exception):
""" """
Exception raised for various resumable upload problems. Exception raised for various resumable upload problems.
@@ -516,6 +529,7 @@ class ResumableUploadException(Exception):
return 'ResumableUploadException("%s", %s)' % ( return 'ResumableUploadException("%s", %s)' % (
self.message, self.disposition) self.message, self.disposition)
class ResumableDownloadException(Exception): class ResumableDownloadException(Exception):
""" """
Exception raised for various resumable download problems. Exception raised for various resumable download problems.
@@ -532,6 +546,7 @@ class ResumableDownloadException(Exception):
return 'ResumableDownloadException("%s", %s)' % ( return 'ResumableDownloadException("%s", %s)' % (
self.message, self.disposition) self.message, self.disposition)
class TooManyRecordsException(Exception): class TooManyRecordsException(Exception):
""" """
Exception raised when a search of Route53 records returns more Exception raised when a search of Route53 records returns more

View File

@@ -14,7 +14,7 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
@@ -23,6 +23,7 @@ import xml.sax
from boto.compat import StringIO from boto.compat import StringIO
class XmlHandler(xml.sax.ContentHandler): class XmlHandler(xml.sax.ContentHandler):
def __init__(self, root_node, connection): def __init__(self, root_node, connection):

View File

@@ -27,111 +27,112 @@ import boto
from boto.compat import six, http_client from boto.compat import six, http_client
class InvalidCertificateException(http_client.HTTPException): class InvalidCertificateException(http_client.HTTPException):
"""Raised when a certificate is provided with an invalid hostname.""" """Raised when a certificate is provided with an invalid hostname."""
def __init__(self, host, cert, reason): def __init__(self, host, cert, reason):
"""Constructor. """Constructor.
Args: Args:
host: The hostname the connection was made to. host: The hostname the connection was made to.
cert: The SSL certificate (as a dictionary) the host returned. cert: The SSL certificate (as a dictionary) the host returned.
""" """
http_client.HTTPException.__init__(self) http_client.HTTPException.__init__(self)
self.host = host self.host = host
self.cert = cert self.cert = cert
self.reason = reason self.reason = reason
def __str__(self):
return ('Host %s returned an invalid certificate (%s): %s' %
(self.host, self.reason, self.cert))
def __str__(self):
return ('Host %s returned an invalid certificate (%s): %s' %
(self.host, self.reason, self.cert))
def GetValidHostsForCert(cert): def GetValidHostsForCert(cert):
"""Returns a list of valid host globs for an SSL certificate. """Returns a list of valid host globs for an SSL certificate.
Args:
cert: A dictionary representing an SSL certificate.
Returns:
list: A list of valid host globs.
"""
if 'subjectAltName' in cert:
return [x[1] for x in cert['subjectAltName'] if x[0].lower() == 'dns']
else:
return [x[0][1] for x in cert['subject']
if x[0][0].lower() == 'commonname']
Args:
cert: A dictionary representing an SSL certificate.
Returns:
list: A list of valid host globs.
"""
if 'subjectAltName' in cert:
return [x[1] for x in cert['subjectAltName'] if x[0].lower() == 'dns']
else:
return [x[0][1] for x in cert['subject']
if x[0][0].lower() == 'commonname']
def ValidateCertificateHostname(cert, hostname): def ValidateCertificateHostname(cert, hostname):
"""Validates that a given hostname is valid for an SSL certificate. """Validates that a given hostname is valid for an SSL certificate.
Args: Args:
cert: A dictionary representing an SSL certificate. cert: A dictionary representing an SSL certificate.
hostname: The hostname to test. hostname: The hostname to test.
Returns: Returns:
bool: Whether or not the hostname is valid for this certificate. bool: Whether or not the hostname is valid for this certificate.
""" """
hosts = GetValidHostsForCert(cert) hosts = GetValidHostsForCert(cert)
boto.log.debug( boto.log.debug(
"validating server certificate: hostname=%s, certificate hosts=%s", "validating server certificate: hostname=%s, certificate hosts=%s",
hostname, hosts) hostname, hosts)
for host in hosts: for host in hosts:
host_re = host.replace('.', '\.').replace('*', '[^.]*') host_re = host.replace('.', '\.').replace('*', '[^.]*')
if re.search('^%s$' % (host_re,), hostname, re.I): if re.search('^%s$' % (host_re,), hostname, re.I):
return True return True
return False return False
class CertValidatingHTTPSConnection(http_client.HTTPConnection): class CertValidatingHTTPSConnection(http_client.HTTPConnection):
"""An HTTPConnection that connects over SSL and validates certificates.""" """An HTTPConnection that connects over SSL and validates certificates."""
default_port = http_client.HTTPS_PORT default_port = http_client.HTTPS_PORT
def __init__(self, host, port=default_port, key_file=None, cert_file=None, def __init__(self, host, port=default_port, key_file=None, cert_file=None,
ca_certs=None, strict=None, **kwargs): ca_certs=None, strict=None, **kwargs):
"""Constructor. """Constructor.
Args: Args:
host: The hostname. Can be in 'host:port' form. host: The hostname. Can be in 'host:port' form.
port: The port. Defaults to 443. port: The port. Defaults to 443.
key_file: A file containing the client's private key key_file: A file containing the client's private key
cert_file: A file containing the client's certificates cert_file: A file containing the client's certificates
ca_certs: A file contianing a set of concatenated certificate authority ca_certs: A file contianing a set of concatenated certificate authority
certs for validating the server against. certs for validating the server against.
strict: When true, causes BadStatusLine to be raised if the status line strict: When true, causes BadStatusLine to be raised if the status line
can't be parsed as a valid HTTP/1.0 or 1.1 status line. can't be parsed as a valid HTTP/1.0 or 1.1 status line.
""" """
if six.PY2: if six.PY2:
# Python 3.2 and newer have deprecated and removed the strict # Python 3.2 and newer have deprecated and removed the strict
# parameter. Since the params are supported as keyword arguments # parameter. Since the params are supported as keyword arguments
# we conditionally add it here. # we conditionally add it here.
kwargs['strict'] = strict kwargs['strict'] = strict
http_client.HTTPConnection.__init__(self, host=host, port=port, **kwargs)
self.key_file = key_file
self.cert_file = cert_file
self.ca_certs = ca_certs
def connect(self):
"Connect to a host on a given (SSL) port."
if hasattr(self, "timeout"):
sock = socket.create_connection((self.host, self.port), self.timeout)
else:
sock = socket.create_connection((self.host, self.port))
msg = "wrapping ssl socket; "
if self.ca_certs:
msg += "CA certificate file=%s" %self.ca_certs
else:
msg += "using system provided SSL certs"
boto.log.debug(msg)
self.sock = ssl.wrap_socket(sock, keyfile=self.key_file,
certfile=self.cert_file,
cert_reqs=ssl.CERT_REQUIRED,
ca_certs=self.ca_certs)
cert = self.sock.getpeercert()
hostname = self.host.split(':', 0)[0]
if not ValidateCertificateHostname(cert, hostname):
raise InvalidCertificateException(hostname,
cert,
'remote hostname "%s" does not match '\
'certificate' % hostname)
http_client.HTTPConnection.__init__(self, host=host, port=port, **kwargs)
self.key_file = key_file
self.cert_file = cert_file
self.ca_certs = ca_certs
def connect(self):
"Connect to a host on a given (SSL) port."
if hasattr(self, "timeout"):
sock = socket.create_connection((self.host, self.port), self.timeout)
else:
sock = socket.create_connection((self.host, self.port))
msg = "wrapping ssl socket; "
if self.ca_certs:
msg += "CA certificate file=%s" % self.ca_certs
else:
msg += "using system provided SSL certs"
boto.log.debug(msg)
self.sock = ssl.wrap_socket(sock, keyfile=self.key_file,
certfile=self.cert_file,
cert_reqs=ssl.CERT_REQUIRED,
ca_certs=self.ca_certs)
cert = self.sock.getpeercert()
hostname = self.host.split(':', 0)[0]
if not ValidateCertificateHostname(cert, hostname):
raise InvalidCertificateException(hostname,
cert,
'remote hostname "%s" does not match '
'certificate' % hostname)

View File

@@ -64,13 +64,13 @@ class IAMConnection(AWSQueryConnection):
debug=0, https_connection_factory=None, path='/', debug=0, https_connection_factory=None, path='/',
security_token=None, validate_certs=True, profile_name=None): security_token=None, validate_certs=True, profile_name=None):
super(IAMConnection, self).__init__(aws_access_key_id, super(IAMConnection, self).__init__(aws_access_key_id,
aws_secret_access_key, aws_secret_access_key,
is_secure, port, proxy, is_secure, port, proxy,
proxy_port, proxy_user, proxy_pass, proxy_port, proxy_user, proxy_pass,
host, debug, https_connection_factory, host, debug, https_connection_factory,
path, security_token, path, security_token,
validate_certs=validate_certs, validate_certs=validate_certs,
profile_name=profile_name) profile_name=profile_name)
def _required_auth_capability(self): def _required_auth_capability(self):
return ['hmac-v4'] return ['hmac-v4']
@@ -700,7 +700,7 @@ class IAMConnection(AWSQueryConnection):
# #
def list_server_certs(self, path_prefix='/', def list_server_certs(self, path_prefix='/',
marker=None, max_items=None): marker=None, max_items=None):
""" """
Lists the server certificates that have the specified path prefix. Lists the server certificates that have the specified path prefix.
If none exist, the action returns an empty list. If none exist, the action returns an empty list.
@@ -1199,8 +1199,8 @@ class IAMConnection(AWSQueryConnection):
:param instance_profile_name: Name of the instance profile to get :param instance_profile_name: Name of the instance profile to get
information about. information about.
""" """
return self.get_response('GetInstanceProfile', {'InstanceProfileName': return self.get_response('GetInstanceProfile',
instance_profile_name}) {'InstanceProfileName': instance_profile_name})
def get_role(self, role_name): def get_role(self, role_name):
""" """
@@ -1453,7 +1453,7 @@ class IAMConnection(AWSQueryConnection):
provider to get information about. provider to get information about.
""" """
params = {'SAMLProviderArn': saml_provider_arn } params = {'SAMLProviderArn': saml_provider_arn}
return self.get_response('GetSAMLProvider', params) return self.get_response('GetSAMLProvider', params)
def update_saml_provider(self, saml_provider_arn, saml_metadata_document): def update_saml_provider(self, saml_provider_arn, saml_metadata_document):
@@ -1496,5 +1496,51 @@ class IAMConnection(AWSQueryConnection):
provider to delete. provider to delete.
""" """
params = {'SAMLProviderArn': saml_provider_arn } params = {'SAMLProviderArn': saml_provider_arn}
return self.get_response('DeleteSAMLProvider', params) return self.get_response('DeleteSAMLProvider', params)
#
# IAM Reports
#
def generate_credential_report(self):
"""
Generates a credential report for an account
A new credential report can only be generated every 4 hours. If one
hasn't been generated in the last 4 hours then get_credential_report
will error when called
"""
params = {}
return self.get_response('GenerateCredentialReport', params)
def get_credential_report(self):
"""
Retrieves a credential report for an account
A report must have been generated in the last 4 hours to succeed.
The report is returned as a base64 encoded blob within the response.
"""
params = {}
return self.get_response('GetCredentialReport', params)
def create_virtual_mfa_device(self, path, device_name):
"""
Creates a new virtual MFA device for the AWS account.
After creating the virtual MFA, use enable-mfa-device to
attach the MFA device to an IAM user.
:type path: string
:param path: The path for the virtual MFA device.
:type device_name: string
:param device_name: The name of the virtual MFA device.
Used with path to uniquely identify a virtual MFA device.
"""
params = {
'Path': path,
'VirtualMFADeviceName': device_name
}
return self.get_response('CreateVirtualMFADevice', params)

View File

@@ -20,6 +20,7 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
class SummaryMap(dict): class SummaryMap(dict):
def __init__(self, parent=None): def __init__(self, parent=None):
@@ -39,4 +40,3 @@ class SummaryMap(dict):
self[self._name] = value self[self._name] = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -23,6 +23,7 @@
import xml.sax import xml.sax
from boto import utils from boto import utils
class XmlHandler(xml.sax.ContentHandler): class XmlHandler(xml.sax.ContentHandler):
def __init__(self, root_node, connection): def __init__(self, root_node, connection):
@@ -52,7 +53,8 @@ class XmlHandler(xml.sax.ContentHandler):
if not isinstance(s, bytes): if not isinstance(s, bytes):
s = s.encode('utf-8') s = s.encode('utf-8')
xml.sax.parseString(s, self) xml.sax.parseString(s, self)
class Element(dict): class Element(dict):
def __init__(self, connection=None, element_name=None, def __init__(self, connection=None, element_name=None,
@@ -116,6 +118,7 @@ class Element(dict):
elif isinstance(self.parent, ListElement): elif isinstance(self.parent, ListElement):
self.parent.append(value) self.parent.append(value)
class ListElement(list): class ListElement(list):
def __init__(self, connection=None, element_name=None, def __init__(self, connection=None, element_name=None,

View File

@@ -95,7 +95,6 @@ class CloudWatchLogsConnection(AWSQueryConnection):
"InvalidSequenceTokenException": exceptions.InvalidSequenceTokenException, "InvalidSequenceTokenException": exceptions.InvalidSequenceTokenException,
} }
def __init__(self, **kwargs): def __init__(self, **kwargs):
region = kwargs.pop('region', None) region = kwargs.pop('region', None)
if not region: if not region:

View File

@@ -440,6 +440,21 @@ class MTurkConnection(AWSQueryConnection):
params['RequesterFeedback'] = feedback params['RequesterFeedback'] = feedback
return self._process_request('ApproveRejectedAssignment', params) return self._process_request('ApproveRejectedAssignment', params)
def get_file_upload_url(self, assignment_id, question_identifier):
"""
Generates and returns a temporary URL to an uploaded file. The
temporary URL is used to retrieve the file as an answer to a
FileUploadAnswer question, it is valid for 60 seconds.
Will have a FileUploadURL attribute as per the API Reference.
"""
params = {'AssignmentId': assignment_id,
'QuestionIdentifier': question_identifier}
return self._process_request('GetFileUploadURL', params,
[('FileUploadURL', FileUploadURL)])
def get_hit(self, hit_id, response_groups=None): def get_hit(self, hit_id, response_groups=None):
""" """
""" """
@@ -915,6 +930,14 @@ class HIT(BaseAutoResultElement):
expired = property(_has_expired) expired = property(_has_expired)
class FileUploadURL(BaseAutoResultElement):
"""
Class to extract an FileUploadURL structure from a response
"""
pass
class HITTypeId(BaseAutoResultElement): class HITTypeId(BaseAutoResultElement):
""" """
Class to extract an HITTypeId structure from a response Class to extract an HITTypeId structure from a response

View File

@@ -309,7 +309,7 @@ class MWSConnection(AWSQueryConnection):
try: try:
response = self._mexe(request, override_num_retries=None) response = self._mexe(request, override_num_retries=None)
except BotoServerError as bs: except BotoServerError as bs:
raise self._response_error_factor(bs.status, bs.reason, bs.body) raise self._response_error_factory(bs.status, bs.reason, bs.body)
body = response.read() body = response.read()
boto.log.debug(body) boto.log.debug(body)
if not body: if not body:

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved # Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved
# #
# Permission is hereby granted, free of charge, to any person obtaining a # Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the # copy of this software and associated documentation files (the
@@ -20,13 +20,12 @@
# IN THE SOFTWARE. # IN THE SOFTWARE.
# #
import boto import boto
from boto.compat import json
from boto.connection import AWSQueryConnection from boto.connection import AWSQueryConnection
from boto.regioninfo import RegionInfo from boto.regioninfo import RegionInfo
from boto.exception import JSONResponseError from boto.exception import JSONResponseError
from boto.opsworks import exceptions from boto.opsworks import exceptions
from boto.compat import json
class OpsWorksConnection(AWSQueryConnection): class OpsWorksConnection(AWSQueryConnection):
@@ -70,9 +69,8 @@ class OpsWorksConnection(AWSQueryConnection):
When you call CreateStack, CloneStack, or UpdateStack we recommend When you call CreateStack, CloneStack, or UpdateStack we recommend
you use the `ConfigurationManager` parameter to specify the Chef you use the `ConfigurationManager` parameter to specify the Chef
version, 0.9 or 11.4. The default value is currently 0.9. However, version, 0.9, 11.4, or 11.10. The default value is currently 11.4.
we expect to change the default value to 11.4 in October 2013. For For more information, see `Chef Versions`_.
more information, see `Using AWS OpsWorks with Chef 11`_.
""" """
APIVersion = "2013-02-18" APIVersion = "2013-02-18"
DefaultRegionName = "us-east-1" DefaultRegionName = "us-east-1"
@@ -92,7 +90,10 @@ class OpsWorksConnection(AWSQueryConnection):
if not region: if not region:
region = RegionInfo(self, self.DefaultRegionName, region = RegionInfo(self, self.DefaultRegionName,
self.DefaultRegionEndpoint) self.DefaultRegionEndpoint)
kwargs['host'] = region.endpoint
if 'host' not in kwargs or kwargs['host'] is None:
kwargs['host'] = region.endpoint
super(OpsWorksConnection, self).__init__(**kwargs) super(OpsWorksConnection, self).__init__(**kwargs)
self.region = region self.region = region
@@ -155,12 +156,8 @@ class OpsWorksConnection(AWSQueryConnection):
layer_id): layer_id):
""" """
Attaches an Elastic Load Balancing load balancer to a Attaches an Elastic Load Balancing load balancer to a
specified layer. specified layer. For more information, see `Elastic Load
Balancing`_.
You must create the Elastic Load Balancing instance
separately, by using the Elastic Load Balancing console, API,
or CLI. For more information, see ` Elastic Load Balancing
Developer Guide`_.
**Required Permissions**: To use this action, an IAM user must **Required Permissions**: To use this action, an IAM user must
have a Manage permissions level for the stack, or an attached have a Manage permissions level for the stack, or an attached
@@ -189,7 +186,9 @@ class OpsWorksConnection(AWSQueryConnection):
default_instance_profile_arn=None, default_os=None, default_instance_profile_arn=None, default_os=None,
hostname_theme=None, default_availability_zone=None, hostname_theme=None, default_availability_zone=None,
default_subnet_id=None, custom_json=None, default_subnet_id=None, custom_json=None,
configuration_manager=None, use_custom_cookbooks=None, configuration_manager=None, chef_configuration=None,
use_custom_cookbooks=None,
use_opsworks_security_groups=None,
custom_cookbooks_source=None, default_ssh_key_name=None, custom_cookbooks_source=None, default_ssh_key_name=None,
clone_permissions=None, clone_app_ids=None, clone_permissions=None, clone_app_ids=None,
default_root_device_type=None): default_root_device_type=None):
@@ -247,19 +246,14 @@ class OpsWorksConnection(AWSQueryConnection):
pairs to be added to the cloned stack. pairs to be added to the cloned stack.
:type service_role_arn: string :type service_role_arn: string
:param service_role_arn: :param service_role_arn: The stack AWS Identity and Access Management
The stack AWS Identity and Access Management (IAM) role, which allows (IAM) role, which allows AWS OpsWorks to work with AWS resources on
AWS OpsWorks to work with AWS resources on your behalf. You must your behalf. You must set this parameter to the Amazon Resource
set this parameter to the Amazon Resource Name (ARN) for an Name (ARN) for an existing IAM role. If you create a stack by using
existing IAM role. If you create a stack by using the AWS OpsWorks the AWS OpsWorks console, it creates the role for you. You can
console, it creates the role for you. You can obtain an existing obtain an existing stack's IAM ARN programmatically by calling
stack's IAM ARN programmatically by calling DescribePermissions. DescribePermissions. For more information about IAM ARNs, see
For more information about IAM ARNs, see `Using Identifiers`_. `Using Identifiers`_.
You must set this parameter to a valid service role ARN or the action
will fail; there is no default value. You can specify the source
stack's service role ARN, if you prefer, but you must do so
explicitly.
:type default_instance_profile_arn: string :type default_instance_profile_arn: string
:param default_instance_profile_arn: The ARN of an IAM profile that is :param default_instance_profile_arn: The ARN of an IAM profile that is
@@ -313,21 +307,50 @@ class OpsWorksConnection(AWSQueryConnection):
:param custom_json: A string that contains user-defined, custom JSON. :param custom_json: A string that contains user-defined, custom JSON.
It is used to override the corresponding default stack It is used to override the corresponding default stack
configuration JSON values. The string should be in the following configuration JSON values. The string should be in the following
format and must escape characters such as '"'.: `"{\"key1\": format and must escape characters such as '"'.:
\"value1\", \"key2\": \"value2\",...}"` `"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
For more information on custom JSON, see `Use Custom JSON to Modify the For more information on custom JSON, see `Use Custom JSON to Modify the
Stack Configuration JSON`_ Stack Configuration JSON`_
:type configuration_manager: dict :type configuration_manager: dict
:param configuration_manager: The configuration manager. When you clone :param configuration_manager: The configuration manager. When you clone
a stack we recommend that you use the configuration manager to a stack we recommend that you use the configuration manager to
specify the Chef version, 0.9 or 11.4. The default value is specify the Chef version, 0.9, 11.4, or 11.10. The default value is
currently 0.9. However, we expect to change the default value to currently 11.4.
11.4 in September 2013.
:type chef_configuration: dict
:param chef_configuration: A `ChefConfiguration` object that specifies
whether to enable Berkshelf and the Berkshelf version on Chef 11.10
stacks. For more information, see `Create a New Stack`_.
:type use_custom_cookbooks: boolean :type use_custom_cookbooks: boolean
:param use_custom_cookbooks: Whether to use custom cookbooks. :param use_custom_cookbooks: Whether to use custom cookbooks.
:type use_opsworks_security_groups: boolean
:param use_opsworks_security_groups: Whether to associate the AWS
OpsWorks built-in security groups with the stack's layers.
AWS OpsWorks provides a standard set of built-in security groups, one
for each layer, which are associated with layers by default. With
`UseOpsworksSecurityGroups` you can instead provide your own custom
security groups. `UseOpsworksSecurityGroups` has the following
settings:
+ True - AWS OpsWorks automatically associates the appropriate built-in
security group with each layer (default setting). You can associate
additional security groups with a layer after you create it but you
cannot delete the built-in security group.
+ False - AWS OpsWorks does not associate built-in security groups with
layers. You must create appropriate EC2 security groups and
associate a security group with each layer that you create.
However, you can still manually associate a built-in security group
with a layer on creation; custom security groups are required only
for those layers that need custom settings.
For more information, see `Create a New Stack`_.
:type custom_cookbooks_source: dict :type custom_cookbooks_source: dict
:param custom_cookbooks_source: Contains the information required to :param custom_cookbooks_source: Contains the information required to
retrieve an app or cookbook from a repository. For more retrieve an app or cookbook from a repository. For more
@@ -379,8 +402,12 @@ class OpsWorksConnection(AWSQueryConnection):
params['CustomJson'] = custom_json params['CustomJson'] = custom_json
if configuration_manager is not None: if configuration_manager is not None:
params['ConfigurationManager'] = configuration_manager params['ConfigurationManager'] = configuration_manager
if chef_configuration is not None:
params['ChefConfiguration'] = chef_configuration
if use_custom_cookbooks is not None: if use_custom_cookbooks is not None:
params['UseCustomCookbooks'] = use_custom_cookbooks params['UseCustomCookbooks'] = use_custom_cookbooks
if use_opsworks_security_groups is not None:
params['UseOpsworksSecurityGroups'] = use_opsworks_security_groups
if custom_cookbooks_source is not None: if custom_cookbooks_source is not None:
params['CustomCookbooksSource'] = custom_cookbooks_source params['CustomCookbooksSource'] = custom_cookbooks_source
if default_ssh_key_name is not None: if default_ssh_key_name is not None:
@@ -395,8 +422,9 @@ class OpsWorksConnection(AWSQueryConnection):
body=json.dumps(params)) body=json.dumps(params))
def create_app(self, stack_id, name, type, shortname=None, def create_app(self, stack_id, name, type, shortname=None,
description=None, app_source=None, domains=None, description=None, data_sources=None, app_source=None,
enable_ssl=None, ssl_configuration=None, attributes=None): domains=None, enable_ssl=None, ssl_configuration=None,
attributes=None):
""" """
Creates an app for a specified stack. For more information, Creates an app for a specified stack. For more information,
see `Creating Apps`_. see `Creating Apps`_.
@@ -419,6 +447,9 @@ class OpsWorksConnection(AWSQueryConnection):
:type description: string :type description: string
:param description: A description of the app. :param description: A description of the app.
:type data_sources: list
:param data_sources: The app's data source.
:type type: string :type type: string
:param type: The app type. Each supported type is associated with a :param type: The app type. Each supported type is associated with a
particular layer. For example, PHP applications are associated with particular layer. For example, PHP applications are associated with
@@ -441,7 +472,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map :type attributes: map
:param attributes: One or more user-defined key/value pairs to be added :param attributes: One or more user-defined key/value pairs to be added
to the stack attributes bag. to the stack attributes.
""" """
params = {'StackId': stack_id, 'Name': name, 'Type': type, } params = {'StackId': stack_id, 'Name': name, 'Type': type, }
@@ -449,6 +480,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['Shortname'] = shortname params['Shortname'] = shortname
if description is not None: if description is not None:
params['Description'] = description params['Description'] = description
if data_sources is not None:
params['DataSources'] = data_sources
if app_source is not None: if app_source is not None:
params['AppSource'] = app_source params['AppSource'] = app_source
if domains is not None: if domains is not None:
@@ -505,8 +538,9 @@ class OpsWorksConnection(AWSQueryConnection):
:param custom_json: A string that contains user-defined, custom JSON. :param custom_json: A string that contains user-defined, custom JSON.
It is used to override the corresponding default stack It is used to override the corresponding default stack
configuration JSON values. The string should be in the following configuration JSON values. The string should be in the following
format and must escape characters such as '"'.: `"{\"key1\": format and must escape characters such as '"'.:
\"value1\", \"key2\": \"value2\",...}"` `"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
For more information on custom JSON, see `Use Custom JSON to Modify the For more information on custom JSON, see `Use Custom JSON to Modify the
Stack Configuration JSON`_. Stack Configuration JSON`_.
@@ -526,9 +560,10 @@ class OpsWorksConnection(AWSQueryConnection):
def create_instance(self, stack_id, layer_ids, instance_type, def create_instance(self, stack_id, layer_ids, instance_type,
auto_scaling_type=None, hostname=None, os=None, auto_scaling_type=None, hostname=None, os=None,
ami_id=None, ssh_key_name=None, ami_id=None, ssh_key_name=None,
availability_zone=None, subnet_id=None, availability_zone=None, virtualization_type=None,
architecture=None, root_device_type=None, subnet_id=None, architecture=None,
install_updates_on_boot=None): root_device_type=None, install_updates_on_boot=None,
ebs_optimized=None):
""" """
Creates an instance in a specified stack. For more Creates an instance in a specified stack. For more
information, see `Adding an Instance to a Layer`_. information, see `Adding an Instance to a Layer`_.
@@ -598,6 +633,10 @@ class OpsWorksConnection(AWSQueryConnection):
:param availability_zone: The instance Availability Zone. For more :param availability_zone: The instance Availability Zone. For more
information, see `Regions and Endpoints`_. information, see `Regions and Endpoints`_.
:type virtualization_type: string
:param virtualization_type: The instance's virtualization type,
`paravirtual` or `hvm`.
:type subnet_id: string :type subnet_id: string
:param subnet_id: The ID of the instance's subnet. If the stack is :param subnet_id: The ID of the instance's subnet. If the stack is
running in a VPC, you can use this parameter to override the running in a VPC, you can use this parameter to override the
@@ -605,26 +644,28 @@ class OpsWorksConnection(AWSQueryConnection):
the instance in a different subnet. the instance in a different subnet.
:type architecture: string :type architecture: string
:param architecture: The instance architecture. Instance types do not :param architecture: The instance architecture. The default option is
necessarily support both architectures. For a list of the `x86_64`. Instance types do not necessarily support both
architectures that are supported by the different instance types, architectures. For a list of the architectures that are supported
see `Instance Families and Types`_. by the different instance types, see `Instance Families and
Types`_.
:type root_device_type: string :type root_device_type: string
:param root_device_type: The instance root device type. For more :param root_device_type: The instance root device type. For more
information, see `Storage for the Root Device`_. information, see `Storage for the Root Device`_.
:type install_updates_on_boot: boolean :type install_updates_on_boot: boolean
:param install_updates_on_boot: :param install_updates_on_boot: Whether to install operating system and
Whether to install operating system and package updates when the package updates when the instance boots. The default value is
instance boots. The default value is `True`. To control when `True`. To control when updates are installed, set this value to
updates are installed, set this value to `False`. You must then `False`. You must then update your instances manually by using
update your instances manually by using CreateDeployment to run the CreateDeployment to run the `update_dependencies` stack command or
`update_dependencies` stack command or manually running `yum` manually running `yum` (Amazon Linux) or `apt-get` (Ubuntu) on the
(Amazon Linux) or `apt-get` (Ubuntu) on the instances. instances.
We strongly recommend using the default value of `True`, to ensure that :type ebs_optimized: boolean
your instances have the latest security updates. :param ebs_optimized: Whether to create an Amazon EBS-optimized
instance.
""" """
params = { params = {
@@ -644,6 +685,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['SshKeyName'] = ssh_key_name params['SshKeyName'] = ssh_key_name
if availability_zone is not None: if availability_zone is not None:
params['AvailabilityZone'] = availability_zone params['AvailabilityZone'] = availability_zone
if virtualization_type is not None:
params['VirtualizationType'] = virtualization_type
if subnet_id is not None: if subnet_id is not None:
params['SubnetId'] = subnet_id params['SubnetId'] = subnet_id
if architecture is not None: if architecture is not None:
@@ -652,6 +695,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['RootDeviceType'] = root_device_type params['RootDeviceType'] = root_device_type
if install_updates_on_boot is not None: if install_updates_on_boot is not None:
params['InstallUpdatesOnBoot'] = install_updates_on_boot params['InstallUpdatesOnBoot'] = install_updates_on_boot
if ebs_optimized is not None:
params['EbsOptimized'] = ebs_optimized
return self.make_request(action='CreateInstance', return self.make_request(action='CreateInstance',
body=json.dumps(params)) body=json.dumps(params))
@@ -661,19 +706,12 @@ class OpsWorksConnection(AWSQueryConnection):
volume_configurations=None, enable_auto_healing=None, volume_configurations=None, enable_auto_healing=None,
auto_assign_elastic_ips=None, auto_assign_elastic_ips=None,
auto_assign_public_ips=None, custom_recipes=None, auto_assign_public_ips=None, custom_recipes=None,
install_updates_on_boot=None): install_updates_on_boot=None,
use_ebs_optimized_instances=None):
""" """
Creates a layer. For more information, see `How to Create a Creates a layer. For more information, see `How to Create a
Layer`_. Layer`_.
You should use **CreateLayer** for noncustom layer types such
as PHP App Server only if the stack does not have an existing
layer of that type. A stack can have at most one instance of
each noncustom layer; if you attempt to create a second
instance, **CreateLayer** fails. A stack can have an arbitrary
number of custom layers, so you can call **CreateLayer** as
many times as you like for that layer type.
**Required Permissions**: To use this action, an IAM user must **Required Permissions**: To use this action, an IAM user must
have a Manage permissions level for the stack, or an attached have a Manage permissions level for the stack, or an attached
policy that explicitly grants permissions. For more policy that explicitly grants permissions. For more
@@ -685,19 +723,21 @@ class OpsWorksConnection(AWSQueryConnection):
:type type: string :type type: string
:param type: :param type:
The layer type. A stack cannot have more than one layer of the same The layer type. A stack cannot have more than one built-in layer of the
type. This parameter must be set to one of the following: same type. It can have any number of custom layers. This parameter
must be set to one of the following:
+ lb: An HAProxy layer
+ web: A Static Web Server layer
+ rails-app: A Rails App Server layer
+ php-app: A PHP App Server layer
+ nodejs-app: A Node.js App Server layer
+ memcached: A Memcached layer
+ db-master: A MySQL layer
+ monitoring-master: A Ganglia layer
+ custom: A custom layer + custom: A custom layer
+ db-master: A MySQL layer
+ java-app: A Java App Server layer
+ rails-app: A Rails App Server layer
+ lb: An HAProxy layer
+ memcached: A Memcached layer
+ monitoring-master: A Ganglia layer
+ nodejs-app: A Node.js App Server layer
+ php-app: A PHP App Server layer
+ web: A Static Web Server layer
:type name: string :type name: string
:param name: The layer name, which is used by the console. :param name: The layer name, which is used by the console.
@@ -711,7 +751,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map :type attributes: map
:param attributes: One or more user-defined key/value pairs to be added :param attributes: One or more user-defined key/value pairs to be added
to the stack attributes bag. to the stack attributes.
:type custom_instance_profile_arn: string :type custom_instance_profile_arn: string
:param custom_instance_profile_arn: The ARN of an IAM profile that to :param custom_instance_profile_arn: The ARN of an IAM profile that to
@@ -728,7 +768,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type volume_configurations: list :type volume_configurations: list
:param volume_configurations: A `VolumeConfigurations` object that :param volume_configurations: A `VolumeConfigurations` object that
describes the layer Amazon EBS volumes. describes the layer's Amazon EBS volumes.
:type enable_auto_healing: boolean :type enable_auto_healing: boolean
:param enable_auto_healing: Whether to disable auto healing for the :param enable_auto_healing: Whether to disable auto healing for the
@@ -749,16 +789,17 @@ class OpsWorksConnection(AWSQueryConnection):
layer custom recipes. layer custom recipes.
:type install_updates_on_boot: boolean :type install_updates_on_boot: boolean
:param install_updates_on_boot: :param install_updates_on_boot: Whether to install operating system and
Whether to install operating system and package updates when the package updates when the instance boots. The default value is
instance boots. The default value is `True`. To control when `True`. To control when updates are installed, set this value to
updates are installed, set this value to `False`. You must then `False`. You must then update your instances manually by using
update your instances manually by using CreateDeployment to run the CreateDeployment to run the `update_dependencies` stack command or
`update_dependencies` stack command or manually running `yum` manually running `yum` (Amazon Linux) or `apt-get` (Ubuntu) on the
(Amazon Linux) or `apt-get` (Ubuntu) on the instances. instances.
We strongly recommend using the default value of `True`, to ensure that :type use_ebs_optimized_instances: boolean
your instances have the latest security updates. :param use_ebs_optimized_instances: Whether to use Amazon EBS-optimized
instances.
""" """
params = { params = {
@@ -787,6 +828,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['CustomRecipes'] = custom_recipes params['CustomRecipes'] = custom_recipes
if install_updates_on_boot is not None: if install_updates_on_boot is not None:
params['InstallUpdatesOnBoot'] = install_updates_on_boot params['InstallUpdatesOnBoot'] = install_updates_on_boot
if use_ebs_optimized_instances is not None:
params['UseEbsOptimizedInstances'] = use_ebs_optimized_instances
return self.make_request(action='CreateLayer', return self.make_request(action='CreateLayer',
body=json.dumps(params)) body=json.dumps(params))
@@ -795,8 +838,9 @@ class OpsWorksConnection(AWSQueryConnection):
attributes=None, default_os=None, hostname_theme=None, attributes=None, default_os=None, hostname_theme=None,
default_availability_zone=None, default_subnet_id=None, default_availability_zone=None, default_subnet_id=None,
custom_json=None, configuration_manager=None, custom_json=None, configuration_manager=None,
use_custom_cookbooks=None, custom_cookbooks_source=None, chef_configuration=None, use_custom_cookbooks=None,
default_ssh_key_name=None, use_opsworks_security_groups=None,
custom_cookbooks_source=None, default_ssh_key_name=None,
default_root_device_type=None): default_root_device_type=None):
""" """
Creates a new stack. For more information, see `Create a New Creates a new stack. For more information, see `Create a New
@@ -846,7 +890,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map :type attributes: map
:param attributes: One or more user-defined key/value pairs to be added :param attributes: One or more user-defined key/value pairs to be added
to the stack attributes bag. to the stack attributes.
:type service_role_arn: string :type service_role_arn: string
:param service_role_arn: The stack AWS Identity and Access Management :param service_role_arn: The stack AWS Identity and Access Management
@@ -907,21 +951,50 @@ class OpsWorksConnection(AWSQueryConnection):
:param custom_json: A string that contains user-defined, custom JSON. :param custom_json: A string that contains user-defined, custom JSON.
It is used to override the corresponding default stack It is used to override the corresponding default stack
configuration JSON values. The string should be in the following configuration JSON values. The string should be in the following
format and must escape characters such as '"'.: `"{\"key1\": format and must escape characters such as '"'.:
\"value1\", \"key2\": \"value2\",...}"` `"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
For more information on custom JSON, see `Use Custom JSON to Modify the For more information on custom JSON, see `Use Custom JSON to Modify the
Stack Configuration JSON`_. Stack Configuration JSON`_.
:type configuration_manager: dict :type configuration_manager: dict
:param configuration_manager: The configuration manager. When you :param configuration_manager: The configuration manager. When you clone
create a stack we recommend that you use the configuration manager a stack we recommend that you use the configuration manager to
to specify the Chef version, 0.9 or 11.4. The default value is specify the Chef version, 0.9, 11.4, or 11.10. The default value is
currently 0.9. However, we expect to change the default value to currently 11.4.
11.4 in September 2013.
:type chef_configuration: dict
:param chef_configuration: A `ChefConfiguration` object that specifies
whether to enable Berkshelf and the Berkshelf version on Chef 11.10
stacks. For more information, see `Create a New Stack`_.
:type use_custom_cookbooks: boolean :type use_custom_cookbooks: boolean
:param use_custom_cookbooks: Whether the stack uses custom cookbooks. :param use_custom_cookbooks: Whether the stack uses custom cookbooks.
:type use_opsworks_security_groups: boolean
:param use_opsworks_security_groups: Whether to associate the AWS
OpsWorks built-in security groups with the stack's layers.
AWS OpsWorks provides a standard set of built-in security groups, one
for each layer, which are associated with layers by default. With
`UseOpsworksSecurityGroups` you can instead provide your own custom
security groups. `UseOpsworksSecurityGroups` has the following
settings:
+ True - AWS OpsWorks automatically associates the appropriate built-in
security group with each layer (default setting). You can associate
additional security groups with a layer after you create it but you
cannot delete the built-in security group.
+ False - AWS OpsWorks does not associate built-in security groups with
layers. You must create appropriate EC2 security groups and
associate a security group with each layer that you create.
However, you can still manually associate a built-in security group
with a layer on creation; custom security groups are required only
for those layers that need custom settings.
For more information, see `Create a New Stack`_.
:type custom_cookbooks_source: dict :type custom_cookbooks_source: dict
:param custom_cookbooks_source: Contains the information required to :param custom_cookbooks_source: Contains the information required to
retrieve an app or cookbook from a repository. For more retrieve an app or cookbook from a repository. For more
@@ -934,9 +1007,10 @@ class OpsWorksConnection(AWSQueryConnection):
:type default_root_device_type: string :type default_root_device_type: string
:param default_root_device_type: The default root device type. This :param default_root_device_type: The default root device type. This
value is used by default for all instances in the cloned stack, but value is used by default for all instances in the stack, but you
you can override it when you create an instance. For more can override it when you create an instance. The default option is
information, see `Storage for the Root Device`_. `instance-store`. For more information, see `Storage for the Root
Device`_.
""" """
params = { params = {
@@ -961,8 +1035,12 @@ class OpsWorksConnection(AWSQueryConnection):
params['CustomJson'] = custom_json params['CustomJson'] = custom_json
if configuration_manager is not None: if configuration_manager is not None:
params['ConfigurationManager'] = configuration_manager params['ConfigurationManager'] = configuration_manager
if chef_configuration is not None:
params['ChefConfiguration'] = chef_configuration
if use_custom_cookbooks is not None: if use_custom_cookbooks is not None:
params['UseCustomCookbooks'] = use_custom_cookbooks params['UseCustomCookbooks'] = use_custom_cookbooks
if use_opsworks_security_groups is not None:
params['UseOpsworksSecurityGroups'] = use_opsworks_security_groups
if custom_cookbooks_source is not None: if custom_cookbooks_source is not None:
params['CustomCookbooksSource'] = custom_cookbooks_source params['CustomCookbooksSource'] = custom_cookbooks_source
if default_ssh_key_name is not None: if default_ssh_key_name is not None:
@@ -986,7 +1064,12 @@ class OpsWorksConnection(AWSQueryConnection):
:param iam_user_arn: The user's IAM ARN. :param iam_user_arn: The user's IAM ARN.
:type ssh_username: string :type ssh_username: string
:param ssh_username: The user's SSH user name. :param ssh_username: The user's SSH user name. The allowable characters
are [a-z], [A-Z], [0-9], '-', and '_'. If the specified name
includes other punctuation marks, AWS OpsWorks removes them. For
example, `my.name` will be changed to `myname`. If you do not
specify an SSH user name, AWS OpsWorks generates one from the IAM
user name.
:type ssh_public_key: string :type ssh_public_key: string
:param ssh_public_key: The user's public SSH key. :param ssh_public_key: The user's public SSH key.
@@ -994,7 +1077,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type allow_self_management: boolean :type allow_self_management: boolean
:param allow_self_management: Whether users can specify their own SSH :param allow_self_management: Whether users can specify their own SSH
public key through the My Settings page. For more information, see public key through the My Settings page. For more information, see
``_. `Setting an IAM User's Public SSH Key`_.
""" """
params = {'IamUserArn': iam_user_arn, } params = {'IamUserArn': iam_user_arn, }
@@ -1046,7 +1129,7 @@ class OpsWorksConnection(AWSQueryConnection):
address. address.
:type delete_volumes: boolean :type delete_volumes: boolean
:param delete_volumes: Whether to delete the instance Amazon EBS :param delete_volumes: Whether to delete the instance's Amazon EBS
volumes. volumes.
""" """
@@ -1135,6 +1218,18 @@ class OpsWorksConnection(AWSQueryConnection):
return self.make_request(action='DeregisterElasticIp', return self.make_request(action='DeregisterElasticIp',
body=json.dumps(params)) body=json.dumps(params))
def deregister_rds_db_instance(self, rds_db_instance_arn):
"""
Deregisters an Amazon RDS instance.
:type rds_db_instance_arn: string
:param rds_db_instance_arn: The Amazon RDS instance's ARN.
"""
params = {'RdsDbInstanceArn': rds_db_instance_arn, }
return self.make_request(action='DeregisterRdsDbInstance',
body=json.dumps(params))
def deregister_volume(self, volume_id): def deregister_volume(self, volume_id):
""" """
Deregisters an Amazon EBS volume. The volume can then be Deregisters an Amazon EBS volume. The volume can then be
@@ -1434,7 +1529,7 @@ class OpsWorksConnection(AWSQueryConnection):
explicitly grants permissions. For more information on user explicitly grants permissions. For more information on user
permissions, see `Managing User Permissions`_. permissions, see `Managing User Permissions`_.
""" """
params = {} params = {}
return self.make_request(action='DescribeMyUserProfile', return self.make_request(action='DescribeMyUserProfile',
@@ -1444,6 +1539,8 @@ class OpsWorksConnection(AWSQueryConnection):
""" """
Describes the permissions for a specified stack. Describes the permissions for a specified stack.
You must specify at least one of the parameters.
**Required Permissions**: To use this action, an IAM user must **Required Permissions**: To use this action, an IAM user must
have a Manage permissions level for the stack, or an attached have a Manage permissions level for the stack, or an attached
policy that explicitly grants permissions. For more policy that explicitly grants permissions. For more
@@ -1498,6 +1595,26 @@ class OpsWorksConnection(AWSQueryConnection):
return self.make_request(action='DescribeRaidArrays', return self.make_request(action='DescribeRaidArrays',
body=json.dumps(params)) body=json.dumps(params))
def describe_rds_db_instances(self, stack_id, rds_db_instance_arns=None):
"""
Describes Amazon RDS instances.
:type stack_id: string
:param stack_id: The stack ID that the instances are registered with.
The operation returns descriptions of all registered Amazon RDS
instances.
:type rds_db_instance_arns: list
:param rds_db_instance_arns: An array containing the ARNs of the
instances to be described.
"""
params = {'StackId': stack_id, }
if rds_db_instance_arns is not None:
params['RdsDbInstanceArns'] = rds_db_instance_arns
return self.make_request(action='DescribeRdsDbInstances',
body=json.dumps(params))
def describe_service_errors(self, stack_id=None, instance_id=None, def describe_service_errors(self, stack_id=None, instance_id=None,
service_error_ids=None): service_error_ids=None):
""" """
@@ -1583,8 +1700,6 @@ class OpsWorksConnection(AWSQueryConnection):
Describes time-based auto scaling configurations for specified Describes time-based auto scaling configurations for specified
instances. instances.
You must specify at least one of the parameters.
**Required Permissions**: To use this action, an IAM user must **Required Permissions**: To use this action, an IAM user must
have a Show, Deploy, or Manage permissions level for the have a Show, Deploy, or Manage permissions level for the
stack, or an attached policy that explicitly grants stack, or an attached policy that explicitly grants
@@ -1775,6 +1890,33 @@ class OpsWorksConnection(AWSQueryConnection):
return self.make_request(action='RegisterElasticIp', return self.make_request(action='RegisterElasticIp',
body=json.dumps(params)) body=json.dumps(params))
def register_rds_db_instance(self, stack_id, rds_db_instance_arn,
db_user, db_password):
"""
Registers an Amazon RDS instance with a stack.
:type stack_id: string
:param stack_id: The stack ID.
:type rds_db_instance_arn: string
:param rds_db_instance_arn: The Amazon RDS instance's ARN.
:type db_user: string
:param db_user: The database's master user name.
:type db_password: string
:param db_password: The database password.
"""
params = {
'StackId': stack_id,
'RdsDbInstanceArn': rds_db_instance_arn,
'DbUser': db_user,
'DbPassword': db_password,
}
return self.make_request(action='RegisterRdsDbInstance',
body=json.dumps(params))
def register_volume(self, stack_id, ec_2_volume_id=None): def register_volume(self, stack_id, ec_2_volume_id=None):
""" """
Registers an Amazon EBS volume with a specified stack. A Registers an Amazon EBS volume with a specified stack. A
@@ -1809,12 +1951,6 @@ class OpsWorksConnection(AWSQueryConnection):
specified layer. For more information, see `Managing Load with specified layer. For more information, see `Managing Load with
Time-based and Load-based Instances`_. Time-based and Load-based Instances`_.
To use load-based auto scaling, you must create a set of load-
based auto scaling instances. Load-based auto scaling operates
only on the instances from that set, so you must ensure that
you have created enough instances to handle the maximum
anticipated load.
**Required Permissions**: To use this action, an IAM user must **Required Permissions**: To use this action, an IAM user must
have a Manage permissions level for the stack, or an attached have a Manage permissions level for the stack, or an attached
policy that explicitly grants permissions. For more policy that explicitly grants permissions. For more
@@ -1853,7 +1989,7 @@ class OpsWorksConnection(AWSQueryConnection):
def set_permission(self, stack_id, iam_user_arn, allow_ssh=None, def set_permission(self, stack_id, iam_user_arn, allow_ssh=None,
allow_sudo=None, level=None): allow_sudo=None, level=None):
""" """
Specifies a stack's permissions. For more information, see Specifies a user's permissions. For more information, see
`Security and Permissions`_. `Security and Permissions`_.
**Required Permissions**: To use this action, an IAM user must **Required Permissions**: To use this action, an IAM user must
@@ -1949,7 +2085,7 @@ class OpsWorksConnection(AWSQueryConnection):
def start_stack(self, stack_id): def start_stack(self, stack_id):
""" """
Starts stack's instances. Starts a stack's instances.
**Required Permissions**: To use this action, an IAM user must **Required Permissions**: To use this action, an IAM user must
have a Manage permissions level for the stack, or an attached have a Manage permissions level for the stack, or an attached
@@ -2025,9 +2161,10 @@ class OpsWorksConnection(AWSQueryConnection):
return self.make_request(action='UnassignVolume', return self.make_request(action='UnassignVolume',
body=json.dumps(params)) body=json.dumps(params))
def update_app(self, app_id, name=None, description=None, type=None, def update_app(self, app_id, name=None, description=None,
app_source=None, domains=None, enable_ssl=None, data_sources=None, type=None, app_source=None,
ssl_configuration=None, attributes=None): domains=None, enable_ssl=None, ssl_configuration=None,
attributes=None):
""" """
Updates a specified app. Updates a specified app.
@@ -2046,6 +2183,9 @@ class OpsWorksConnection(AWSQueryConnection):
:type description: string :type description: string
:param description: A description of the app. :param description: A description of the app.
:type data_sources: list
:param data_sources: The app's data sources.
:type type: string :type type: string
:param type: The app type. :param type: The app type.
@@ -2065,7 +2205,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map :type attributes: map
:param attributes: One or more user-defined key/value pairs to be added :param attributes: One or more user-defined key/value pairs to be added
to the stack attributes bag. to the stack attributes.
""" """
params = {'AppId': app_id, } params = {'AppId': app_id, }
@@ -2073,6 +2213,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['Name'] = name params['Name'] = name
if description is not None: if description is not None:
params['Description'] = description params['Description'] = description
if data_sources is not None:
params['DataSources'] = data_sources
if type is not None: if type is not None:
params['Type'] = type params['Type'] = type
if app_source is not None: if app_source is not None:
@@ -2116,7 +2258,7 @@ class OpsWorksConnection(AWSQueryConnection):
instance_type=None, auto_scaling_type=None, instance_type=None, auto_scaling_type=None,
hostname=None, os=None, ami_id=None, hostname=None, os=None, ami_id=None,
ssh_key_name=None, architecture=None, ssh_key_name=None, architecture=None,
install_updates_on_boot=None): install_updates_on_boot=None, ebs_optimized=None):
""" """
Updates a specified instance. Updates a specified instance.
@@ -2185,16 +2327,16 @@ class OpsWorksConnection(AWSQueryConnection):
see `Instance Families and Types`_. see `Instance Families and Types`_.
:type install_updates_on_boot: boolean :type install_updates_on_boot: boolean
:param install_updates_on_boot: :param install_updates_on_boot: Whether to install operating system and
Whether to install operating system and package updates when the package updates when the instance boots. The default value is
instance boots. The default value is `True`. To control when `True`. To control when updates are installed, set this value to
updates are installed, set this value to `False`. You must then `False`. You must then update your instances manually by using
update your instances manually by using CreateDeployment to run the CreateDeployment to run the `update_dependencies` stack command or
`update_dependencies` stack command or manually running `yum` manually running `yum` (Amazon Linux) or `apt-get` (Ubuntu) on the
(Amazon Linux) or `apt-get` (Ubuntu) on the instances. instances.
We strongly recommend using the default value of `True`, to ensure that :type ebs_optimized: boolean
your instances have the latest security updates. :param ebs_optimized: Whether this is an Amazon EBS-optimized instance.
""" """
params = {'InstanceId': instance_id, } params = {'InstanceId': instance_id, }
@@ -2216,6 +2358,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['Architecture'] = architecture params['Architecture'] = architecture
if install_updates_on_boot is not None: if install_updates_on_boot is not None:
params['InstallUpdatesOnBoot'] = install_updates_on_boot params['InstallUpdatesOnBoot'] = install_updates_on_boot
if ebs_optimized is not None:
params['EbsOptimized'] = ebs_optimized
return self.make_request(action='UpdateInstance', return self.make_request(action='UpdateInstance',
body=json.dumps(params)) body=json.dumps(params))
@@ -2225,7 +2369,8 @@ class OpsWorksConnection(AWSQueryConnection):
volume_configurations=None, enable_auto_healing=None, volume_configurations=None, enable_auto_healing=None,
auto_assign_elastic_ips=None, auto_assign_elastic_ips=None,
auto_assign_public_ips=None, custom_recipes=None, auto_assign_public_ips=None, custom_recipes=None,
install_updates_on_boot=None): install_updates_on_boot=None,
use_ebs_optimized_instances=None):
""" """
Updates a specified layer. Updates a specified layer.
@@ -2250,7 +2395,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map :type attributes: map
:param attributes: One or more user-defined key/value pairs to be added :param attributes: One or more user-defined key/value pairs to be added
to the stack attributes bag. to the stack attributes.
:type custom_instance_profile_arn: string :type custom_instance_profile_arn: string
:param custom_instance_profile_arn: The ARN of an IAM profile to be :param custom_instance_profile_arn: The ARN of an IAM profile to be
@@ -2288,16 +2433,17 @@ class OpsWorksConnection(AWSQueryConnection):
layer's custom recipes. layer's custom recipes.
:type install_updates_on_boot: boolean :type install_updates_on_boot: boolean
:param install_updates_on_boot: :param install_updates_on_boot: Whether to install operating system and
Whether to install operating system and package updates when the package updates when the instance boots. The default value is
instance boots. The default value is `True`. To control when `True`. To control when updates are installed, set this value to
updates are installed, set this value to `False`. You must then `False`. You must then update your instances manually by using
update your instances manually by using CreateDeployment to run the CreateDeployment to run the `update_dependencies` stack command or
`update_dependencies` stack command or manually running `yum` manually running `yum` (Amazon Linux) or `apt-get` (Ubuntu) on the
(Amazon Linux) or `apt-get` (Ubuntu) on the instances. instances.
We strongly recommend using the default value of `True`, to ensure that :type use_ebs_optimized_instances: boolean
your instances have the latest security updates. :param use_ebs_optimized_instances: Whether to use Amazon EBS-optimized
instances.
""" """
params = {'LayerId': layer_id, } params = {'LayerId': layer_id, }
@@ -2325,6 +2471,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['CustomRecipes'] = custom_recipes params['CustomRecipes'] = custom_recipes
if install_updates_on_boot is not None: if install_updates_on_boot is not None:
params['InstallUpdatesOnBoot'] = install_updates_on_boot params['InstallUpdatesOnBoot'] = install_updates_on_boot
if use_ebs_optimized_instances is not None:
params['UseEbsOptimizedInstances'] = use_ebs_optimized_instances
return self.make_request(action='UpdateLayer', return self.make_request(action='UpdateLayer',
body=json.dumps(params)) body=json.dumps(params))
@@ -2347,14 +2495,39 @@ class OpsWorksConnection(AWSQueryConnection):
return self.make_request(action='UpdateMyUserProfile', return self.make_request(action='UpdateMyUserProfile',
body=json.dumps(params)) body=json.dumps(params))
def update_rds_db_instance(self, rds_db_instance_arn, db_user=None,
db_password=None):
"""
Updates an Amazon RDS instance.
:type rds_db_instance_arn: string
:param rds_db_instance_arn: The Amazon RDS instance's ARN.
:type db_user: string
:param db_user: The master user name.
:type db_password: string
:param db_password: The database password.
"""
params = {'RdsDbInstanceArn': rds_db_instance_arn, }
if db_user is not None:
params['DbUser'] = db_user
if db_password is not None:
params['DbPassword'] = db_password
return self.make_request(action='UpdateRdsDbInstance',
body=json.dumps(params))
def update_stack(self, stack_id, name=None, attributes=None, def update_stack(self, stack_id, name=None, attributes=None,
service_role_arn=None, service_role_arn=None,
default_instance_profile_arn=None, default_os=None, default_instance_profile_arn=None, default_os=None,
hostname_theme=None, default_availability_zone=None, hostname_theme=None, default_availability_zone=None,
default_subnet_id=None, custom_json=None, default_subnet_id=None, custom_json=None,
configuration_manager=None, use_custom_cookbooks=None, configuration_manager=None, chef_configuration=None,
custom_cookbooks_source=None, default_ssh_key_name=None, use_custom_cookbooks=None, custom_cookbooks_source=None,
default_root_device_type=None): default_ssh_key_name=None,
default_root_device_type=None,
use_opsworks_security_groups=None):
""" """
Updates a specified stack. Updates a specified stack.
@@ -2372,20 +2545,14 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map :type attributes: map
:param attributes: One or more user-defined key/value pairs to be added :param attributes: One or more user-defined key/value pairs to be added
to the stack attributes bag. to the stack attributes.
:type service_role_arn: string :type service_role_arn: string
:param service_role_arn: :param service_role_arn: The stack AWS Identity and Access Management
The stack AWS Identity and Access Management (IAM) role, which allows (IAM) role, which allows AWS OpsWorks to work with AWS resources on
AWS OpsWorks to work with AWS resources on your behalf. You must your behalf. You must set this parameter to the Amazon Resource
set this parameter to the Amazon Resource Name (ARN) for an Name (ARN) for an existing IAM role. For more information about IAM
existing IAM role. For more information about IAM ARNs, see `Using ARNs, see `Using Identifiers`_.
Identifiers`_.
You must set this parameter to a valid service role ARN or the action
will fail; there is no default value. You can specify the stack's
current service role ARN, if you prefer, but you must do so
explicitly.
:type default_instance_profile_arn: string :type default_instance_profile_arn: string
:param default_instance_profile_arn: The ARN of an IAM profile that is :param default_instance_profile_arn: The ARN of an IAM profile that is
@@ -2438,16 +2605,22 @@ class OpsWorksConnection(AWSQueryConnection):
:param custom_json: A string that contains user-defined, custom JSON. :param custom_json: A string that contains user-defined, custom JSON.
It is used to override the corresponding default stack It is used to override the corresponding default stack
configuration JSON values. The string should be in the following configuration JSON values. The string should be in the following
format and must escape characters such as '"'.: `"{\"key1\": format and must escape characters such as '"'.:
\"value1\", \"key2\": \"value2\",...}"` `"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
For more information on custom JSON, see `Use Custom JSON to Modify the For more information on custom JSON, see `Use Custom JSON to Modify the
Stack Configuration JSON`_. Stack Configuration JSON`_.
:type configuration_manager: dict :type configuration_manager: dict
:param configuration_manager: The configuration manager. When you :param configuration_manager: The configuration manager. When you clone
update a stack you can optionally use the configuration manager to a stack we recommend that you use the configuration manager to
specify the Chef version, 0.9 or 11.4. If you omit this parameter, specify the Chef version, 0.9, 11.4, or 11.10. The default value is
AWS OpsWorks does not change the Chef version. currently 11.4.
:type chef_configuration: dict
:param chef_configuration: A `ChefConfiguration` object that specifies
whether to enable Berkshelf and the Berkshelf version on Chef 11.10
stacks. For more information, see `Create a New Stack`_.
:type use_custom_cookbooks: boolean :type use_custom_cookbooks: boolean
:param use_custom_cookbooks: Whether the stack uses custom cookbooks. :param use_custom_cookbooks: Whether the stack uses custom cookbooks.
@@ -2464,9 +2637,33 @@ class OpsWorksConnection(AWSQueryConnection):
:type default_root_device_type: string :type default_root_device_type: string
:param default_root_device_type: The default root device type. This :param default_root_device_type: The default root device type. This
value is used by default for all instances in the cloned stack, but value is used by default for all instances in the stack, but you
you can override it when you create an instance. For more can override it when you create an instance. For more information,
information, see `Storage for the Root Device`_. see `Storage for the Root Device`_.
:type use_opsworks_security_groups: boolean
:param use_opsworks_security_groups: Whether to associate the AWS
OpsWorks built-in security groups with the stack's layers.
AWS OpsWorks provides a standard set of built-in security groups, one
for each layer, which are associated with layers by default.
`UseOpsworksSecurityGroups` allows you to instead provide your own
custom security groups. `UseOpsworksSecurityGroups` has the
following settings:
+ True - AWS OpsWorks automatically associates the appropriate built-in
security group with each layer (default setting). You can associate
additional security groups with a layer after you create it but you
cannot delete the built-in security group.
+ False - AWS OpsWorks does not associate built-in security groups with
layers. You must create appropriate EC2 security groups and
associate a security group with each layer that you create.
However, you can still manually associate a built-in security group
with a layer on creation; custom security groups are required only
for those layers that need custom settings.
For more information, see `Create a New Stack`_.
""" """
params = {'StackId': stack_id, } params = {'StackId': stack_id, }
@@ -2490,6 +2687,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['CustomJson'] = custom_json params['CustomJson'] = custom_json
if configuration_manager is not None: if configuration_manager is not None:
params['ConfigurationManager'] = configuration_manager params['ConfigurationManager'] = configuration_manager
if chef_configuration is not None:
params['ChefConfiguration'] = chef_configuration
if use_custom_cookbooks is not None: if use_custom_cookbooks is not None:
params['UseCustomCookbooks'] = use_custom_cookbooks params['UseCustomCookbooks'] = use_custom_cookbooks
if custom_cookbooks_source is not None: if custom_cookbooks_source is not None:
@@ -2498,6 +2697,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['DefaultSshKeyName'] = default_ssh_key_name params['DefaultSshKeyName'] = default_ssh_key_name
if default_root_device_type is not None: if default_root_device_type is not None:
params['DefaultRootDeviceType'] = default_root_device_type params['DefaultRootDeviceType'] = default_root_device_type
if use_opsworks_security_groups is not None:
params['UseOpsworksSecurityGroups'] = use_opsworks_security_groups
return self.make_request(action='UpdateStack', return self.make_request(action='UpdateStack',
body=json.dumps(params)) body=json.dumps(params))
@@ -2515,7 +2716,12 @@ class OpsWorksConnection(AWSQueryConnection):
:param iam_user_arn: The user IAM ARN. :param iam_user_arn: The user IAM ARN.
:type ssh_username: string :type ssh_username: string
:param ssh_username: The user's new SSH user name. :param ssh_username: The user's SSH user name. The allowable characters
are [a-z], [A-Z], [0-9], '-', and '_'. If the specified name
includes other punctuation marks, AWS OpsWorks removes them. For
example, `my.name` will be changed to `myname`. If you do not
specify an SSH user name, AWS OpsWorks generates one from the IAM
user name.
:type ssh_public_key: string :type ssh_public_key: string
:param ssh_public_key: The user's new SSH public key. :param ssh_public_key: The user's new SSH public key.

View File

@@ -14,7 +14,7 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
@@ -26,19 +26,20 @@ Implements plugin related api.
To define a new plugin just subclass Plugin, like this. To define a new plugin just subclass Plugin, like this.
class AuthPlugin(Plugin): class AuthPlugin(Plugin):
pass pass
Then start creating subclasses of your new plugin. Then start creating subclasses of your new plugin.
class MyFancyAuth(AuthPlugin): class MyFancyAuth(AuthPlugin):
capability = ['sign', 'vmac'] capability = ['sign', 'vmac']
The actual interface is duck typed. The actual interface is duck typed.
""" """
import glob import glob
import imp, os.path import imp
import os.path
class Plugin(object): class Plugin(object):
"""Base class for all plugins.""" """Base class for all plugins."""
@@ -50,10 +51,11 @@ class Plugin(object):
"""Returns true if the requested capability is supported by this plugin """Returns true if the requested capability is supported by this plugin
""" """
for c in requested_capability: for c in requested_capability:
if not c in cls.capability: if c not in cls.capability:
return False return False
return True return True
def get_plugin(cls, requested_capability=None): def get_plugin(cls, requested_capability=None):
if not requested_capability: if not requested_capability:
requested_capability = [] requested_capability = []
@@ -63,18 +65,20 @@ def get_plugin(cls, requested_capability=None):
result.append(handler) result.append(handler)
return result return result
def _import_module(filename): def _import_module(filename):
(path, name) = os.path.split(filename) (path, name) = os.path.split(filename)
(name, ext) = os.path.splitext(name) (name, ext) = os.path.splitext(name)
(file, filename, data) = imp.find_module(name, [path]) (file, filename, data) = imp.find_module(name, [path])
try: try:
return imp.load_module(name, file, filename, data) return imp.load_module(name, file, filename, data)
finally: finally:
if file: if file:
file.close() file.close()
_plugin_loaded = False
_plugin_loaded = False
def load_plugins(config): def load_plugins(config):
global _plugin_loaded global _plugin_loaded
@@ -87,4 +91,3 @@ def load_plugins(config):
directory = config.get('Plugin', 'plugin_directory') directory = config.get('Plugin', 'plugin_directory')
for file in glob.glob(os.path.join(directory, '*.py')): for file in glob.glob(os.path.join(directory, '*.py')):
_import_module(file) _import_module(file)

View File

@@ -69,7 +69,8 @@ STORAGE_PERMISSIONS_ERROR = 'StoragePermissionsError'
STORAGE_RESPONSE_ERROR = 'StorageResponseError' STORAGE_RESPONSE_ERROR = 'StorageResponseError'
class ProfileNotFoundError(ValueError): pass class ProfileNotFoundError(ValueError):
pass
class Provider(object): class Provider(object):
@@ -252,7 +253,7 @@ class Provider(object):
# datetime docs. # datetime docs.
seconds_left = ( seconds_left = (
(delta.microseconds + (delta.seconds + delta.days * 24 * 3600) (delta.microseconds + (delta.seconds + delta.days * 24 * 3600)
* 10**6) / 10**6) * 10 ** 6) / 10 ** 6)
if seconds_left < (5 * 60): if seconds_left < (5 * 60):
boto.log.debug("Credentials need to be refreshed.") boto.log.debug("Credentials need to be refreshed.")
return True return True
@@ -444,6 +445,7 @@ class Provider(object):
def supports_chunked_transfer(self): def supports_chunked_transfer(self):
return self.ChunkedTransferSupport[self.name] return self.ChunkedTransferSupport[self.name]
# Static utility method for getting default Provider. # Static utility method for getting default Provider.
def get_default(): def get_default():
return Provider('aws') return Provider('aws')

View File

@@ -34,10 +34,6 @@ class ClusterSnapshotNotFoundFault(JSONResponseError):
pass pass
class ClusterNotFoundFault(JSONResponseError):
pass
class ClusterSecurityGroupQuotaExceededFault(JSONResponseError): class ClusterSecurityGroupQuotaExceededFault(JSONResponseError):
pass pass
@@ -457,3 +453,7 @@ class SnapshotCopyAlreadyEnabled(JSONResponseError):
class IncompatibleOrderableOptions(JSONResponseError): class IncompatibleOrderableOptions(JSONResponseError):
pass pass
class InvalidSubscriptionState(JSONResponseError):
pass

View File

@@ -1,4 +1,4 @@
# Copyright (c) 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved # Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved
# #
# Permission is hereby granted, free of charge, to any person obtaining a # Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the # copy of this software and associated documentation files (the
@@ -20,8 +20,8 @@
# IN THE SOFTWARE. # IN THE SOFTWARE.
# #
import json
import boto import boto
from boto.compat import json
from boto.connection import AWSQueryConnection from boto.connection import AWSQueryConnection
from boto.regioninfo import RegionInfo from boto.regioninfo import RegionInfo
from boto.exception import JSONResponseError from boto.exception import JSONResponseError
@@ -113,6 +113,7 @@ class RedshiftConnection(AWSQueryConnection):
"InvalidS3KeyPrefix": exceptions.InvalidS3KeyPrefix, "InvalidS3KeyPrefix": exceptions.InvalidS3KeyPrefix,
"SubscriptionAlreadyExist": exceptions.SubscriptionAlreadyExist, "SubscriptionAlreadyExist": exceptions.SubscriptionAlreadyExist,
"HsmConfigurationNotFound": exceptions.HsmConfigurationNotFound, "HsmConfigurationNotFound": exceptions.HsmConfigurationNotFound,
"InvalidSubscriptionState": exceptions.InvalidSubscriptionState,
"AuthorizationNotFound": exceptions.AuthorizationNotFound, "AuthorizationNotFound": exceptions.AuthorizationNotFound,
"ClusterSecurityGroupQuotaExceeded": exceptions.ClusterSecurityGroupQuotaExceeded, "ClusterSecurityGroupQuotaExceeded": exceptions.ClusterSecurityGroupQuotaExceeded,
"SubnetAlreadyInUse": exceptions.SubnetAlreadyInUse, "SubnetAlreadyInUse": exceptions.SubnetAlreadyInUse,
@@ -140,8 +141,10 @@ class RedshiftConnection(AWSQueryConnection):
if not region: if not region:
region = RegionInfo(self, self.DefaultRegionName, region = RegionInfo(self, self.DefaultRegionName,
self.DefaultRegionEndpoint) self.DefaultRegionEndpoint)
if 'host' not in kwargs:
if 'host' not in kwargs or kwargs['host'] is None:
kwargs['host'] = region.endpoint kwargs['host'] = region.endpoint
super(RedshiftConnection, self).__init__(**kwargs) super(RedshiftConnection, self).__init__(**kwargs)
self.region = region self.region = region
@@ -161,8 +164,7 @@ class RedshiftConnection(AWSQueryConnection):
Routing (CIDR) IP address range or an EC2 security group. You Routing (CIDR) IP address range or an EC2 security group. You
can add as many as 20 ingress rules to an Amazon Redshift can add as many as 20 ingress rules to an Amazon Redshift
security group. security group.
The EC2 security group must be defined in the AWS region where
the cluster resides.
For an overview of CIDR blocks, see the Wikipedia article on For an overview of CIDR blocks, see the Wikipedia article on
`Classless Inter-Domain Routing`_. `Classless Inter-Domain Routing`_.
@@ -269,7 +271,7 @@ class RedshiftConnection(AWSQueryConnection):
+ Must be the identifier for a valid automated snapshot whose state is + Must be the identifier for a valid automated snapshot whose state is
"available". `available`.
:type source_snapshot_cluster_identifier: string :type source_snapshot_cluster_identifier: string
:param source_snapshot_cluster_identifier: :param source_snapshot_cluster_identifier:
@@ -386,7 +388,8 @@ class RedshiftConnection(AWSQueryConnection):
:param node_type: The node type to be provisioned for the cluster. For :param node_type: The node type to be provisioned for the cluster. For
information about node types, go to ` Working with Clusters`_ in information about node types, go to ` Working with Clusters`_ in
the Amazon Redshift Management Guide . the Amazon Redshift Management Guide .
Valid Values: `dw.hs1.xlarge` | `dw.hs1.8xlarge`. Valid Values: `dw1.xlarge` | `dw1.8xlarge` | `dw2.large` |
`dw2.8xlarge`.
:type master_username: string :type master_username: string
:param master_username: :param master_username:
@@ -459,6 +462,10 @@ class RedshiftConnection(AWSQueryConnection):
+ **US-East (Northern Virginia) Region:** 03:00-11:00 UTC + **US-East (Northern Virginia) Region:** 03:00-11:00 UTC
+ **US-West (Oregon) Region** 06:00-14:00 UTC + **US-West (Oregon) Region** 06:00-14:00 UTC
+ **EU (Ireland) Region** 22:00-06:00 UTC
+ **Asia Pacific (Singapore) Region** 14:00-22:00 UTC
+ **Asia Pacific (Sydney) Region** 12:00-20:00 UTC
+ **Asia Pacific (Tokyo) Region** 17:00-03:00 UTC
Valid Days: Mon | Tue | Wed | Thu | Fri | Sat | Sun Valid Days: Mon | Tue | Wed | Thu | Fri | Sat | Sun
@@ -541,7 +548,8 @@ class RedshiftConnection(AWSQueryConnection):
a public network. a public network.
:type encrypted: boolean :type encrypted: boolean
:param encrypted: If `True`, the data in cluster is encrypted at rest. :param encrypted: If `True`, the data in the cluster is encrypted at
rest.
Default: false Default: false
:type hsm_client_certificate_identifier: string :type hsm_client_certificate_identifier: string
@@ -643,8 +651,7 @@ class RedshiftConnection(AWSQueryConnection):
+ Must be 1 to 255 alphanumeric characters or hyphens + Must be 1 to 255 alphanumeric characters or hyphens
+ First character must be a letter. + First character must be a letter.
+ Cannot end with a hyphen or contain two consecutive hyphens. + Cannot end with a hyphen or contain two consecutive hyphens.
+ Must be unique withing your AWS account. + Must be unique within your AWS account.
This value is stored as a lower-case string. This value is stored as a lower-case string.
@@ -680,7 +687,7 @@ class RedshiftConnection(AWSQueryConnection):
Creates a new Amazon Redshift security group. You use security Creates a new Amazon Redshift security group. You use security
groups to control access to non-VPC clusters. groups to control access to non-VPC clusters.
For information about managing security groups, go to`Amazon For information about managing security groups, go to `Amazon
Redshift Cluster Security Groups`_ in the Amazon Redshift Redshift Cluster Security Groups`_ in the Amazon Redshift
Management Guide . Management Guide .
@@ -715,7 +722,7 @@ class RedshiftConnection(AWSQueryConnection):
cluster_identifier): cluster_identifier):
""" """
Creates a manual snapshot of the specified cluster. The Creates a manual snapshot of the specified cluster. The
cluster must be in the "available" state. cluster must be in the `available` state.
For more information about working with snapshots, go to For more information about working with snapshots, go to
`Amazon Redshift Snapshots`_ in the Amazon Redshift Management `Amazon Redshift Snapshots`_ in the Amazon Redshift Management
@@ -758,7 +765,7 @@ class RedshiftConnection(AWSQueryConnection):
Private Cloud (Amazon VPC) when creating Amazon Redshift Private Cloud (Amazon VPC) when creating Amazon Redshift
subnet group. subnet group.
For information about subnet groups, go to`Amazon Redshift For information about subnet groups, go to `Amazon Redshift
Cluster Subnet Groups`_ in the Amazon Redshift Management Cluster Subnet Groups`_ in the Amazon Redshift Management
Guide . Guide .
@@ -815,13 +822,13 @@ class RedshiftConnection(AWSQueryConnection):
those criteria. For example, you can specify source type = those criteria. For example, you can specify source type =
cluster, source ID = my-cluster-1 and mycluster2, event cluster, source ID = my-cluster-1 and mycluster2, event
categories = Availability, Backup, and severity = ERROR. The categories = Availability, Backup, and severity = ERROR. The
subsription will only send notifications for those ERROR subscription will only send notifications for those ERROR
events in the Availability and Backup categores for the events in the Availability and Backup categories for the
specified clusters. specified clusters.
If you specify both the source type and source IDs, such as If you specify both the source type and source IDs, such as
source type = cluster and source identifier = my-cluster-1, source type = cluster and source identifier = my-cluster-1,
notifiactions will be sent for all the cluster events for my- notifications will be sent for all the cluster events for my-
cluster-1. If you specify a source type but do not specify a cluster-1. If you specify a source type but do not specify a
source identifier, you will receive notice of the events for source identifier, you will receive notice of the events for
the objects of that type in your AWS account. If you do not the objects of that type in your AWS account. If you do not
@@ -917,16 +924,16 @@ class RedshiftConnection(AWSQueryConnection):
databases. databases.
The command returns a public key, which you must store in the The command returns a public key, which you must store in the
HSM. After creating the HSM certificate, you must create an HSM. In addition to creating the HSM certificate, you must
Amazon Redshift HSM configuration that provides a cluster the create an Amazon Redshift HSM configuration that provides a
information needed to store and retrieve database encryption cluster the information needed to store and use encryption
keys in the HSM. For more information, go to aLinkToHSMTopic keys in the HSM. For more information, go to `Hardware
in the Amazon Redshift Management Guide. Security Modules`_ in the Amazon Redshift Management Guide.
:type hsm_client_certificate_identifier: string :type hsm_client_certificate_identifier: string
:param hsm_client_certificate_identifier: The identifier to be assigned :param hsm_client_certificate_identifier: The identifier to be assigned
to the new HSM client certificate that the cluster will use to to the new HSM client certificate that the cluster will use to
connect to the HSM to retrieve the database encryption keys. connect to the HSM to use the database encryption keys.
""" """
params = { params = {
@@ -943,15 +950,16 @@ class RedshiftConnection(AWSQueryConnection):
hsm_server_public_certificate): hsm_server_public_certificate):
""" """
Creates an HSM configuration that contains the information Creates an HSM configuration that contains the information
required by an Amazon Redshift cluster to store and retrieve required by an Amazon Redshift cluster to store and use
database encryption keys in a Hardware Storeage Module (HSM). database encryption keys in a Hardware Security Module (HSM).
After creating the HSM configuration, you can specify it as a After creating the HSM configuration, you can specify it as a
parameter when creating a cluster. The cluster will then store parameter when creating a cluster. The cluster will then store
its encryption keys in the HSM. its encryption keys in the HSM.
Before creating an HSM configuration, you must have first In addition to creating an HSM configuration, you must also
created an HSM client certificate. For more information, go to create an HSM client certificate. For more information, go to
aLinkToHSMTopic in the Amazon Redshift Management Guide. `Hardware Security Modules`_ in the Amazon Redshift Management
Guide.
:type hsm_configuration_identifier: string :type hsm_configuration_identifier: string
:param hsm_configuration_identifier: The identifier to be assigned to :param hsm_configuration_identifier: The identifier to be assigned to
@@ -975,9 +983,8 @@ class RedshiftConnection(AWSQueryConnection):
partition. partition.
:type hsm_server_public_certificate: string :type hsm_server_public_certificate: string
:param hsm_server_public_certificate: The public key used to access the :param hsm_server_public_certificate: The HSMs public certificate file.
HSM client certificate, which was created by calling the Amazon When using Cloud HSM, the file name is server.pem.
Redshift create HSM certificate command.
""" """
params = { params = {
@@ -1026,9 +1033,6 @@ class RedshiftConnection(AWSQueryConnection):
cluster. If `True`, a final cluster snapshot is not created. If cluster. If `True`, a final cluster snapshot is not created. If
`False`, a final cluster snapshot is created before the cluster is `False`, a final cluster snapshot is created before the cluster is
deleted. deleted.
The FinalClusterSnapshotIdentifier parameter must be specified if
SkipFinalClusterSnapshot is `False`.
Default: `False` Default: `False`
:type final_cluster_snapshot_identifier: string :type final_cluster_snapshot_identifier: string
@@ -1058,9 +1062,7 @@ class RedshiftConnection(AWSQueryConnection):
def delete_cluster_parameter_group(self, parameter_group_name): def delete_cluster_parameter_group(self, parameter_group_name):
""" """
Deletes a specified Amazon Redshift parameter group. You Deletes a specified Amazon Redshift parameter group.
cannot delete a parameter group if it is associated with a
cluster.
:type parameter_group_name: string :type parameter_group_name: string
:param parameter_group_name: :param parameter_group_name:
@@ -1082,9 +1084,8 @@ class RedshiftConnection(AWSQueryConnection):
def delete_cluster_security_group(self, cluster_security_group_name): def delete_cluster_security_group(self, cluster_security_group_name):
""" """
Deletes an Amazon Redshift security group. Deletes an Amazon Redshift security group.
You cannot delete a security group that is associated with any
clusters. You cannot delete the default security group. For information about managing security groups, go to `Amazon
For information about managing security groups, go to`Amazon
Redshift Cluster Security Groups`_ in the Amazon Redshift Redshift Cluster Security Groups`_ in the Amazon Redshift
Management Guide . Management Guide .
@@ -1105,7 +1106,7 @@ class RedshiftConnection(AWSQueryConnection):
snapshot_cluster_identifier=None): snapshot_cluster_identifier=None):
""" """
Deletes the specified manual snapshot. The snapshot must be in Deletes the specified manual snapshot. The snapshot must be in
the "available" state, with no other users authorized to the `available` state, with no other users authorized to
access the snapshot. access the snapshot.
Unlike automated snapshots, manual snapshots are retained even Unlike automated snapshots, manual snapshots are retained even
@@ -1224,19 +1225,23 @@ class RedshiftConnection(AWSQueryConnection):
groups and the default parameter group are returned. groups and the default parameter group are returned.
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of parameter group records to :param max_records: The maximum number of response records to return in
include in the response. If more records exist than the specified each call. If the number of remaining response records exceeds the
`MaxRecords` value, the response includes a marker that you can use specified `MaxRecords` value, a value is returned in a `marker`
in a subsequent DescribeClusterParameterGroups request to retrieve field of the response. You can retrieve the next set of records by
the next set of records. retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: Value must be at least 20 and no more than 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned by a previous :param marker: An optional parameter that specifies the starting point
DescribeClusterParameterGroups request to indicate the first to return a set of response records. When the results of a
parameter group that the current request will return. DescribeClusterParameterGroups request exceed the value specified
in `MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {} params = {}
@@ -1284,19 +1289,23 @@ class RedshiftConnection(AWSQueryConnection):
Valid Values: `user` | `engine-default` Valid Values: `user` | `engine-default`
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to include in the :param max_records: The maximum number of response records to return in
response. If more records exist than the specified `MaxRecords` each call. If the number of remaining response records exceeds the
value, response includes a marker that you can specify in your specified `MaxRecords` value, a value is returned in a `marker`
subsequent request to retrieve remaining result. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: Value must be at least 20 and no more than 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned from a previous :param marker: An optional parameter that specifies the starting point
**DescribeClusterParameters** request. If this parameter is to return a set of response records. When the results of a
specified, the response includes only records beyond the specified DescribeClusterParameters request exceed the value specified in
marker, up to the value specified by `MaxRecords`. `MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {'ParameterGroupName': parameter_group_name, } params = {'ParameterGroupName': parameter_group_name, }
@@ -1319,7 +1328,7 @@ class RedshiftConnection(AWSQueryConnection):
the name of a security group is specified, the response will the name of a security group is specified, the response will
contain only information about only that security group. contain only information about only that security group.
For information about managing security groups, go to`Amazon For information about managing security groups, go to `Amazon
Redshift Cluster Security Groups`_ in the Amazon Redshift Redshift Cluster Security Groups`_ in the Amazon Redshift
Management Guide . Management Guide .
@@ -1331,20 +1340,25 @@ class RedshiftConnection(AWSQueryConnection):
Example: `securitygroup1` Example: `securitygroup1`
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to be included in the :param max_records: The maximum number of response records to return in
response. If more records exist than the specified `MaxRecords` each call. If the number of remaining response records exceeds the
value, a marker is included in the response, which you can use in a specified `MaxRecords` value, a value is returned in a `marker`
subsequent DescribeClusterSecurityGroups request. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: Value must be at least 20 and no more than 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned by a previous :param marker: An optional parameter that specifies the starting point
DescribeClusterSecurityGroups request to indicate the first to return a set of response records. When the results of a
security group that the current request will return. You can DescribeClusterSecurityGroups request exceed the value specified in
specify either the **Marker** parameter or a `MaxRecords`, AWS returns a value in the `Marker` field of the
**ClusterSecurityGroupName** parameter, but not both. response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
Constraints: You can specify either the **ClusterSecurityGroupName**
parameter or the **Marker** parameter, but not both.
""" """
params = {} params = {}
@@ -1401,19 +1415,23 @@ class RedshiftConnection(AWSQueryConnection):
Example: `2012-07-16T18:00:00Z` Example: `2012-07-16T18:00:00Z`
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of snapshot records to include :param max_records: The maximum number of response records to return in
in the response. If more records exist than the specified each call. If the number of remaining response records exceeds the
`MaxRecords` value, the response returns a marker that you can use specified `MaxRecords` value, a value is returned in a `marker`
in a subsequent DescribeClusterSnapshots request in order to field of the response. You can retrieve the next set of records by
retrieve the next set of snapshot records. retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: Must be at least 20 and no more than 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned by a previous :param marker: An optional parameter that specifies the starting point
DescribeClusterSnapshots request to indicate the first snapshot to return a set of response records. When the results of a
that the request will return. DescribeClusterSnapshots request exceed the value specified in
`MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
:type owner_account: string :type owner_account: string
:param owner_account: The AWS customer account used to create or copy :param owner_account: The AWS customer account used to create or copy
@@ -1458,19 +1476,23 @@ class RedshiftConnection(AWSQueryConnection):
for which information is requested. for which information is requested.
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of cluster subnet group records :param max_records: The maximum number of response records to return in
to include in the response. If more records exist than the each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, the response returns a marker that specified `MaxRecords` value, a value is returned in a `marker`
you can use in a subsequent DescribeClusterSubnetGroups request in field of the response. You can retrieve the next set of records by
order to retrieve the next set of cluster subnet group records. retrying the command with the returned marker value.
Default: 100 Default: `100`
Constraints: Must be at least 20 and no more than 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned by a previous :param marker: An optional parameter that specifies the starting point
DescribeClusterSubnetGroups request to indicate the first cluster to return a set of response records. When the results of a
subnet group that the current request will return. DescribeClusterSubnetGroups request exceed the value specified in
`MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {} params = {}
@@ -1512,18 +1534,23 @@ class RedshiftConnection(AWSQueryConnection):
+ Cannot end with a hyphen or contain two consecutive hyphens + Cannot end with a hyphen or contain two consecutive hyphens
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to include in the :param max_records: The maximum number of response records to return in
response. If more than the `MaxRecords` value is available, a each call. If the number of remaining response records exceeds the
marker is included in the response so that the following results specified `MaxRecords` value, a value is returned in a `marker`
can be retrieved. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: Value must be at least 20 and no more than 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: The marker returned from a previous request. If this :param marker: An optional parameter that specifies the starting point
parameter is specified, the response includes records beyond the to return a set of response records. When the results of a
marker only, up to `MaxRecords`. DescribeClusterVersions request exceed the value specified in
`MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {} params = {}
@@ -1552,25 +1579,29 @@ class RedshiftConnection(AWSQueryConnection):
:type cluster_identifier: string :type cluster_identifier: string
:param cluster_identifier: The unique identifier of a cluster whose :param cluster_identifier: The unique identifier of a cluster whose
properties you are requesting. This parameter isn't case sensitive. properties you are requesting. This parameter is case sensitive.
The default is that all clusters defined for an account are returned. The default is that all clusters defined for an account are returned.
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records that the response can :param max_records: The maximum number of response records to return in
include. If more records exist than the specified `MaxRecords` each call. If the number of remaining response records exceeds the
value, a `marker` is included in the response that can be used in a specified `MaxRecords` value, a value is returned in a `marker`
new **DescribeClusters** request to continue listing results. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: Value must be at least 20 and no more than 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned by a previous :param marker: An optional parameter that specifies the starting point
**DescribeClusters** request to indicate the first cluster that the to return a set of response records. When the results of a
current **DescribeClusters** request will return. DescribeClusters request exceed the value specified in
You can specify either a **Marker** parameter or a `MaxRecords`, AWS returns a value in the `Marker` field of the
**ClusterIdentifier** parameter in a **DescribeClusters** request, response. You can retrieve the next set of response records by
but not both. providing the returned marker value in the `Marker` parameter and
retrying the request.
Constraints: You can specify either the **ClusterIdentifier** parameter
or the **Marker** parameter, but not both.
""" """
params = {} params = {}
@@ -1600,19 +1631,23 @@ class RedshiftConnection(AWSQueryConnection):
family. family.
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to include in the :param max_records: The maximum number of response records to return in
response. If more records exist than the specified `MaxRecords` each call. If the number of remaining response records exceeds the
value, a marker is included in the response so that the remaining specified `MaxRecords` value, a value is returned in a `marker`
results may be retrieved. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: Value must be at least 20 and no more than 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned from a previous :param marker: An optional parameter that specifies the starting point
**DescribeDefaultClusterParameters** request. If this parameter is to return a set of response records. When the results of a
specified, the response includes only records beyond the marker, up DescribeDefaultClusterParameters request exceed the value specified
to the value specified by `MaxRecords`. in `MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {'ParameterGroupFamily': parameter_group_family, } params = {'ParameterGroupFamily': parameter_group_family, }
@@ -1659,19 +1694,23 @@ class RedshiftConnection(AWSQueryConnection):
notification subscription to be described. notification subscription to be described.
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to include in the :param max_records: The maximum number of response records to return in
response. If more records exist than the specified MaxRecords each call. If the number of remaining response records exceeds the
value, a pagination token called a marker is included in the specified `MaxRecords` value, a value is returned in a `marker`
response so that the remaining results can be retrieved. field of the response. You can retrieve the next set of records by
Default: 100 retrying the command with the returned marker value.
Default: `100`
Constraints: minimum 20, maximum 100 Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional pagination token provided by a previous :param marker: An optional parameter that specifies the starting point
DescribeOrderableClusterOptions request. If this parameter is to return a set of response records. When the results of a
specified, the response includes only records beyond the marker, up DescribeEventSubscriptions request exceed the value specified in
to the value specified by MaxRecords. `MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {} params = {}
@@ -1753,19 +1792,22 @@ class RedshiftConnection(AWSQueryConnection):
Default: `60` Default: `60`
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to include in the :param max_records: The maximum number of response records to return in
response. If more records exist than the specified `MaxRecords` each call. If the number of remaining response records exceeds the
value, a marker is included in the response so that the remaining specified `MaxRecords` value, a value is returned in a `marker`
results may be retrieved. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: Value must be at least 20 and no more than 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned from a previous :param marker: An optional parameter that specifies the starting point
**DescribeEvents** request. If this parameter is specified, the to return a set of response records. When the results of a
response includes only records beyond the marker, up to the value DescribeEvents request exceed the value specified in `MaxRecords`,
specified by `MaxRecords`. AWS returns a value in the `Marker` field of the response. You can
retrieve the next set of response records by providing the returned
marker value in the `Marker` parameter and retrying the request.
""" """
params = {} params = {}
@@ -1801,23 +1843,26 @@ class RedshiftConnection(AWSQueryConnection):
:param hsm_client_certificate_identifier: The identifier of a specific :param hsm_client_certificate_identifier: The identifier of a specific
HSM client certificate for which you want information. If no HSM client certificate for which you want information. If no
identifier is specified, information is returned for all HSM client identifier is specified, information is returned for all HSM client
certificates associated with Amazon Redshift clusters owned by your certificates owned by your AWS customer account.
AWS customer account.
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to include in the :param max_records: The maximum number of response records to return in
response. If more records exist than the specified `MaxRecords` each call. If the number of remaining response records exceeds the
value, a marker is included in the response so that the remaining specified `MaxRecords` value, a value is returned in a `marker`
results may be retrieved. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: minimum 20, maximum 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned from a previous :param marker: An optional parameter that specifies the starting point
**DescribeOrderableClusterOptions** request. If this parameter is to return a set of response records. When the results of a
specified, the response includes only records beyond the marker, up DescribeHsmClientCertificates request exceed the value specified in
to the value specified by `MaxRecords`. `MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {} params = {}
@@ -1847,19 +1892,23 @@ class RedshiftConnection(AWSQueryConnection):
owned by your AWS customer account. owned by your AWS customer account.
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to include in the :param max_records: The maximum number of response records to return in
response. If more records exist than the specified `MaxRecords` each call. If the number of remaining response records exceeds the
value, a marker is included in the response so that the remaining specified `MaxRecords` value, a value is returned in a `marker`
results may be retrieved. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: minimum 20, maximum 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned from a previous :param marker: An optional parameter that specifies the starting point
**DescribeOrderableClusterOptions** request. If this parameter is to return a set of response records. When the results of a
specified, the response includes only records beyond the marker, up DescribeHsmConfigurations request exceed the value specified in
to the value specified by `MaxRecords`. `MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {} params = {}
@@ -1921,19 +1970,23 @@ class RedshiftConnection(AWSQueryConnection):
show only the available offerings matching the specified node type. show only the available offerings matching the specified node type.
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to include in the :param max_records: The maximum number of response records to return in
response. If more records exist than the specified `MaxRecords` each call. If the number of remaining response records exceeds the
value, a marker is included in the response so that the remaining specified `MaxRecords` value, a value is returned in a `marker`
results may be retrieved. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: minimum 20, maximum 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned from a previous :param marker: An optional parameter that specifies the starting point
**DescribeOrderableClusterOptions** request. If this parameter is to return a set of response records. When the results of a
specified, the response includes only records beyond the marker, up DescribeOrderableClusterOptions request exceed the value specified
to the value specified by `MaxRecords`. in `MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {} params = {}
@@ -1972,21 +2025,23 @@ class RedshiftConnection(AWSQueryConnection):
offering. offering.
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to include in the :param max_records: The maximum number of response records to return in
response. If more records exist than the specified `MaxRecords` each call. If the number of remaining response records exceeds the
value, a marker is included in the response so that the remaining specified `MaxRecords` value, a value is returned in a `marker`
results may be retrieved. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: minimum 20, maximum 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned by a previous :param marker: An optional parameter that specifies the starting point
DescribeReservedNodeOfferings request to indicate the first to return a set of response records. When the results of a
offering that the request will return. DescribeReservedNodeOfferings request exceed the value specified in
You can specify either a **Marker** parameter or a `MaxRecords`, AWS returns a value in the `Marker` field of the
**ClusterIdentifier** parameter in a DescribeClusters request, but response. You can retrieve the next set of response records by
not both. providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {} params = {}
@@ -2010,18 +2065,23 @@ class RedshiftConnection(AWSQueryConnection):
:param reserved_node_id: Identifier for the node reservation. :param reserved_node_id: Identifier for the node reservation.
:type max_records: integer :type max_records: integer
:param max_records: The maximum number of records to include in the :param max_records: The maximum number of response records to return in
response. If more records exist than the specified `MaxRecords` each call. If the number of remaining response records exceeds the
value, a marker is included in the response so that the remaining specified `MaxRecords` value, a value is returned in a `marker`
results may be retrieved. field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100` Default: `100`
Constraints: minimum 20, maximum 100. Constraints: minimum 20, maximum 100.
:type marker: string :type marker: string
:param marker: An optional marker returned by a previous :param marker: An optional parameter that specifies the starting point
DescribeReservedNodes request to indicate the first parameter group to return a set of response records. When the results of a
that the current request will return. DescribeReservedNodes request exceed the value specified in
`MaxRecords`, AWS returns a value in the `Marker` field of the
response. You can retrieve the next set of response records by
providing the returned marker value in the `Marker` parameter and
retrying the request.
""" """
params = {} params = {}
@@ -2199,16 +2259,17 @@ class RedshiftConnection(AWSQueryConnection):
preferred_maintenance_window=None, preferred_maintenance_window=None,
cluster_version=None, allow_version_upgrade=None, cluster_version=None, allow_version_upgrade=None,
hsm_client_certificate_identifier=None, hsm_client_certificate_identifier=None,
hsm_configuration_identifier=None): hsm_configuration_identifier=None,
new_cluster_identifier=None):
""" """
Modifies the settings for a cluster. For example, you can add Modifies the settings for a cluster. For example, you can add
another security or parameter group, update the preferred another security or parameter group, update the preferred
maintenance window, or change the master user password. maintenance window, or change the master user password.
Resetting a cluster password or modifying the security groups Resetting a cluster password or modifying the security groups
associated with a cluster do not need a reboot. However, associated with a cluster do not need a reboot. However,
modifying parameter group requires a reboot for parameters to modifying a parameter group requires a reboot for parameters
take effect. For more information about managing clusters, go to take effect. For more information about managing clusters,
to `Amazon Redshift Clusters`_ in the Amazon Redshift go to `Amazon Redshift Clusters`_ in the Amazon Redshift
Management Guide Management Guide
You can also change node type and the number of nodes to scale You can also change node type and the number of nodes to scale
@@ -2247,7 +2308,8 @@ class RedshiftConnection(AWSQueryConnection):
permissions for the cluster are restored. You can use the permissions for the cluster are restored. You can use the
DescribeResize to track the progress of the resize request. DescribeResize to track the progress of the resize request.
Valid Values: ` dw.hs1.xlarge` | `dw.hs1.8xlarge` Valid Values: ` dw1.xlarge` | `dw1.8xlarge` | `dw2.large` |
`dw2.8xlarge`.
:type number_of_nodes: integer :type number_of_nodes: integer
:param number_of_nodes: The new number of nodes of the cluster. If you :param number_of_nodes: The new number of nodes of the cluster. If you
@@ -2269,7 +2331,7 @@ class RedshiftConnection(AWSQueryConnection):
A list of cluster security groups to be authorized on this cluster. A list of cluster security groups to be authorized on this cluster.
This change is asynchronously applied as soon as possible. This change is asynchronously applied as soon as possible.
Security groups currently associated with the cluster and not in the Security groups currently associated with the cluster, and not in the
list of groups to apply, will be revoked from the cluster. list of groups to apply, will be revoked from the cluster.
Constraints: Constraints:
@@ -2280,7 +2342,7 @@ class RedshiftConnection(AWSQueryConnection):
+ Cannot end with a hyphen or contain two consecutive hyphens + Cannot end with a hyphen or contain two consecutive hyphens
:type vpc_security_group_ids: list :type vpc_security_group_ids: list
:param vpc_security_group_ids: A list of Virtual Private Cloud (VPC) :param vpc_security_group_ids: A list of virtual private cloud (VPC)
security groups to be associated with the cluster. security groups to be associated with the cluster.
:type master_user_password: string :type master_user_password: string
@@ -2290,10 +2352,6 @@ class RedshiftConnection(AWSQueryConnection):
request and the completion of the request, the `MasterUserPassword` request and the completion of the request, the `MasterUserPassword`
element exists in the `PendingModifiedValues` element of the element exists in the `PendingModifiedValues` element of the
operation response. operation response.
Operations never return the password, so this operation provides a way
to regain access to the master user account for a cluster if the
password is lost.
Default: Uses existing setting. Default: Uses existing setting.
@@ -2323,7 +2381,7 @@ class RedshiftConnection(AWSQueryConnection):
you can still create manual snapshots when you want with you can still create manual snapshots when you want with
CreateClusterSnapshot. CreateClusterSnapshot.
If you decrease the automated snapshot retention period from its If you decrease the automated snapshot retention period from its
current value, existing automated snapshots which fall outside of current value, existing automated snapshots that fall outside of
the new retention period will be immediately deleted. the new retention period will be immediately deleted.
Default: Uses existing setting. Default: Uses existing setting.
@@ -2376,6 +2434,20 @@ class RedshiftConnection(AWSQueryConnection):
configuration that contains the information the Amazon Redshift configuration that contains the information the Amazon Redshift
cluster can use to retrieve and store keys in an HSM. cluster can use to retrieve and store keys in an HSM.
:type new_cluster_identifier: string
:param new_cluster_identifier: The new identifier for the cluster.
Constraints:
+ Must contain from 1 to 63 alphanumeric characters or hyphens.
+ Alphabetic characters must be lowercase.
+ First character must be a letter.
+ Cannot end with a hyphen or contain two consecutive hyphens.
+ Must be unique for all clusters within an AWS account.
Example: `examplecluster`
""" """
params = {'ClusterIdentifier': cluster_identifier, } params = {'ClusterIdentifier': cluster_identifier, }
if cluster_type is not None: if cluster_type is not None:
@@ -2409,6 +2481,8 @@ class RedshiftConnection(AWSQueryConnection):
params['HsmClientCertificateIdentifier'] = hsm_client_certificate_identifier params['HsmClientCertificateIdentifier'] = hsm_client_certificate_identifier
if hsm_configuration_identifier is not None: if hsm_configuration_identifier is not None:
params['HsmConfigurationIdentifier'] = hsm_configuration_identifier params['HsmConfigurationIdentifier'] = hsm_configuration_identifier
if new_cluster_identifier is not None:
params['NewClusterIdentifier'] = new_cluster_identifier
return self._make_request( return self._make_request(
action='ModifyCluster', action='ModifyCluster',
verb='POST', verb='POST',
@@ -2434,6 +2508,9 @@ class RedshiftConnection(AWSQueryConnection):
parameter name and parameter value; other name-value pairs of the parameter name and parameter value; other name-value pairs of the
parameter are optional. parameter are optional.
For the workload management (WLM) configuration, you must supply all
the name-value pairs in the wlm_json_configuration parameter.
""" """
params = {'ParameterGroupName': parameter_group_name, } params = {'ParameterGroupName': parameter_group_name, }
self.build_complex_list_params( self.build_complex_list_params(
@@ -2694,7 +2771,12 @@ class RedshiftConnection(AWSQueryConnection):
owner_account=None, owner_account=None,
hsm_client_certificate_identifier=None, hsm_client_certificate_identifier=None,
hsm_configuration_identifier=None, hsm_configuration_identifier=None,
elastic_ip=None): elastic_ip=None,
cluster_parameter_group_name=None,
cluster_security_groups=None,
vpc_security_group_ids=None,
preferred_maintenance_window=None,
automated_snapshot_retention_period=None):
""" """
Creates a new cluster from a snapshot. Amazon Redshift creates Creates a new cluster from a snapshot. Amazon Redshift creates
the resulting cluster with the same configuration as the the resulting cluster with the same configuration as the
@@ -2705,11 +2787,8 @@ class RedshiftConnection(AWSQueryConnection):
different security group and different parameter group with different security group and different parameter group with
the restored cluster. the restored cluster.
If a snapshot is taken of a cluster in VPC, you can restore it If you restore a cluster into a VPC, you must provide a
only in VPC. In this case, you must provide a cluster subnet cluster subnet group where you want the cluster restored.
group where you want the cluster restored. If snapshot is
taken of a cluster outside VPC, then you can restore it only
outside VPC.
For more information about working with snapshots, go to For more information about working with snapshots, go to
`Amazon Redshift Snapshots`_ in the Amazon Redshift Management `Amazon Redshift Snapshots`_ in the Amazon Redshift Management
@@ -2787,6 +2866,68 @@ class RedshiftConnection(AWSQueryConnection):
:type elastic_ip: string :type elastic_ip: string
:param elastic_ip: The elastic IP (EIP) address for the cluster. :param elastic_ip: The elastic IP (EIP) address for the cluster.
:type cluster_parameter_group_name: string
:param cluster_parameter_group_name:
The name of the parameter group to be associated with this cluster.
Default: The default Amazon Redshift cluster parameter group. For
information about the default parameter group, go to `Working with
Amazon Redshift Parameter Groups`_.
Constraints:
+ Must be 1 to 255 alphanumeric characters or hyphens.
+ First character must be a letter.
+ Cannot end with a hyphen or contain two consecutive hyphens.
:type cluster_security_groups: list
:param cluster_security_groups: A list of security groups to be
associated with this cluster.
Default: The default cluster security group for Amazon Redshift.
Cluster security groups only apply to clusters outside of VPCs.
:type vpc_security_group_ids: list
:param vpc_security_group_ids: A list of Virtual Private Cloud (VPC)
security groups to be associated with the cluster.
Default: The default VPC security group is associated with the cluster.
VPC security groups only apply to clusters in VPCs.
:type preferred_maintenance_window: string
:param preferred_maintenance_window: The weekly time range (in UTC)
during which automated cluster maintenance can occur.
Format: `ddd:hh24:mi-ddd:hh24:mi`
Default: The value selected for the cluster from which the snapshot was
taken. The following list shows the time blocks for each region
from which the default maintenance windows are assigned.
+ **US-East (Northern Virginia) Region:** 03:00-11:00 UTC
+ **US-West (Oregon) Region** 06:00-14:00 UTC
+ **EU (Ireland) Region** 22:00-06:00 UTC
+ **Asia Pacific (Singapore) Region** 14:00-22:00 UTC
+ **Asia Pacific (Sydney) Region** 12:00-20:00 UTC
+ **Asia Pacific (Tokyo) Region** 17:00-03:00 UTC
Valid Days: Mon | Tue | Wed | Thu | Fri | Sat | Sun
Constraints: Minimum 30-minute window.
:type automated_snapshot_retention_period: integer
:param automated_snapshot_retention_period: The number of days that
automated snapshots are retained. If the value is 0, automated
snapshots are disabled. Even if automated snapshots are disabled,
you can still create manual snapshots when you want with
CreateClusterSnapshot.
Default: The value selected for the cluster from which the snapshot was
taken.
Constraints: Must be a value from 0 to 35.
""" """
params = { params = {
'ClusterIdentifier': cluster_identifier, 'ClusterIdentifier': cluster_identifier,
@@ -2814,6 +2955,20 @@ class RedshiftConnection(AWSQueryConnection):
params['HsmConfigurationIdentifier'] = hsm_configuration_identifier params['HsmConfigurationIdentifier'] = hsm_configuration_identifier
if elastic_ip is not None: if elastic_ip is not None:
params['ElasticIp'] = elastic_ip params['ElasticIp'] = elastic_ip
if cluster_parameter_group_name is not None:
params['ClusterParameterGroupName'] = cluster_parameter_group_name
if cluster_security_groups is not None:
self.build_list_params(params,
cluster_security_groups,
'ClusterSecurityGroups.member')
if vpc_security_group_ids is not None:
self.build_list_params(params,
vpc_security_group_ids,
'VpcSecurityGroupIds.member')
if preferred_maintenance_window is not None:
params['PreferredMaintenanceWindow'] = preferred_maintenance_window
if automated_snapshot_retention_period is not None:
params['AutomatedSnapshotRetentionPeriod'] = automated_snapshot_retention_period
return self._make_request( return self._make_request(
action='RestoreFromClusterSnapshot', action='RestoreFromClusterSnapshot',
verb='POST', verb='POST',
@@ -2829,7 +2984,7 @@ class RedshiftConnection(AWSQueryConnection):
for a previously authorized IP range or Amazon EC2 security for a previously authorized IP range or Amazon EC2 security
group. To add an ingress rule, see group. To add an ingress rule, see
AuthorizeClusterSecurityGroupIngress. For information about AuthorizeClusterSecurityGroupIngress. For information about
managing security groups, go to`Amazon Redshift Cluster managing security groups, go to `Amazon Redshift Cluster
Security Groups`_ in the Amazon Redshift Management Guide . Security Groups`_ in the Amazon Redshift Management Guide .
:type cluster_security_group_name: string :type cluster_security_group_name: string

View File

@@ -124,7 +124,7 @@ def get_regions(service_name, region_cls=None, connection_cls=None):
""" """
endpoints = load_regions() endpoints = load_regions()
if not service_name in endpoints: if service_name not in endpoints:
raise BotoClientError( raise BotoClientError(
"Service '%s' not found in endpoints." % service_name "Service '%s' not found in endpoints." % service_name
) )

View File

@@ -1,9 +1,11 @@
import sys
from datetime import datetime from datetime import datetime
from threading import Thread from threading import Thread
import Queue import Queue
from boto.utils import RequestHook from boto.utils import RequestHook
from boto.compat import long_type
class RequestLogger(RequestHook): class RequestLogger(RequestHook):
""" """
@@ -14,18 +16,16 @@ class RequestLogger(RequestHook):
self.request_log_file = open(filename, 'w') self.request_log_file = open(filename, 'w')
self.request_log_queue = Queue.Queue(100) self.request_log_queue = Queue.Queue(100)
Thread(target=self._request_log_worker).start() Thread(target=self._request_log_worker).start()
def handle_request_data(self, request, response, error=False): def handle_request_data(self, request, response, error=False):
len = 0 if error else response.getheader('Content-Length') len = 0 if error else response.getheader('Content-Length')
now = datetime.now() now = datetime.now()
time = now.strftime('%Y-%m-%d %H:%M:%S') time = now.strftime('%Y-%m-%d %H:%M:%S')
td = (now - request.start_time) td = (now - request.start_time)
duration = (td.microseconds + long(td.seconds + td.days*24*3600) * 1e6) / 1e6 duration = (td.microseconds + long_type(td.seconds + td.days * 24 * 3600) * 1e6) / 1e6
# write output including timestamp, status code, response time, response size, request action # write output including timestamp, status code, response time, response size, request action
self.request_log_queue.put("'%s', '%s', '%s', '%s', '%s'\n" % (time, response.status, duration, len, request.params['Action'])) self.request_log_queue.put("'%s', '%s', '%s', '%s', '%s'\n" % (time, response.status, duration, len, request.params['Action']))
def _request_log_worker(self): def _request_log_worker(self):
while True: while True:
@@ -35,5 +35,5 @@ class RequestLogger(RequestHook):
self.request_log_file.flush() self.request_log_file.flush()
self.request_log_queue.task_done() self.request_log_queue.task_done()
except: except:
import traceback; traceback.print_exc(file=sys.stdout) import traceback
traceback.print_exc(file=sys.stdout)

View File

@@ -21,14 +21,15 @@
from boto.s3.user import User from boto.s3.user import User
class ResultSet(list): class ResultSet(list):
""" """
The ResultSet is used to pass results back from the Amazon services The ResultSet is used to pass results back from the Amazon services
to the client. It is light wrapper around Python's :py:class:`list` class, to the client. It is light wrapper around Python's :py:class:`list` class,
with some additional methods for parsing XML results from AWS. with some additional methods for parsing XML results from AWS.
Because I don't really want any dependencies on external libraries, Because I don't really want any dependencies on external libraries,
I'm using the standard SAX parser that comes with Python. The good news is I'm using the standard SAX parser that comes with Python. The good news is
that it's quite fast and efficient but it makes some things rather that it's quite fast and efficient but it makes some things rather
difficult. difficult.
You can pass in, as the marker_elem parameter, a list of tuples. You can pass in, as the marker_elem parameter, a list of tuples.
@@ -54,7 +55,7 @@ class ResultSet(list):
self.next_key_marker = None self.next_key_marker = None
self.next_upload_id_marker = None self.next_upload_id_marker = None
self.next_version_id_marker = None self.next_version_id_marker = None
self.next_generation_marker= None self.next_generation_marker = None
self.version_id_marker = None self.version_id_marker = None
self.is_truncated = False self.is_truncated = False
self.next_token = None self.next_token = None
@@ -132,6 +133,7 @@ class ResultSet(list):
else: else:
setattr(self, name, value) setattr(self, name, value)
class BooleanResult(object): class BooleanResult(object):
def __init__(self, marker_elem=None): def __init__(self, marker_elem=None):

View File

@@ -47,7 +47,7 @@ HZXML = """<?xml version="1.0" encoding="UTF-8"?>
</HostedZoneConfig> </HostedZoneConfig>
</CreateHostedZoneRequest>""" </CreateHostedZoneRequest>"""
#boto.set_stream_logger('dns') # boto.set_stream_logger('dns')
class Route53Connection(AWSAuthConnection): class Route53Connection(AWSAuthConnection):
@@ -65,13 +65,14 @@ class Route53Connection(AWSAuthConnection):
host=DefaultHost, debug=0, security_token=None, host=DefaultHost, debug=0, security_token=None,
validate_certs=True, https_connection_factory=None, validate_certs=True, https_connection_factory=None,
profile_name=None): profile_name=None):
super(Route53Connection, self).__init__(host, super(Route53Connection, self).__init__(
aws_access_key_id, aws_secret_access_key, host,
True, port, proxy, proxy_port, debug=debug, aws_access_key_id, aws_secret_access_key,
security_token=security_token, True, port, proxy, proxy_port, debug=debug,
validate_certs=validate_certs, security_token=security_token,
https_connection_factory=https_connection_factory, validate_certs=validate_certs,
profile_name=profile_name) https_connection_factory=https_connection_factory,
profile_name=profile_name)
def _required_auth_capability(self): def _required_auth_capability(self):
return ['route53'] return ['route53']
@@ -84,9 +85,9 @@ class Route53Connection(AWSAuthConnection):
continue continue
pairs.append(key + '=' + urllib.parse.quote(str(val))) pairs.append(key + '=' + urllib.parse.quote(str(val)))
path += '?' + '&'.join(pairs) path += '?' + '&'.join(pairs)
return super(Route53Connection, self).make_request(action, path, return super(Route53Connection, self).make_request(
headers, data, action, path, headers, data,
retry_handler=self._retry_handler) retry_handler=self._retry_handler)
# Hosted Zones # Hosted Zones
@@ -103,7 +104,7 @@ class Route53Connection(AWSAuthConnection):
if start_marker: if start_marker:
params = {'marker': start_marker} params = {'marker': start_marker}
response = self.make_request('GET', '/%s/hostedzone' % self.Version, response = self.make_request('GET', '/%s/hostedzone' % self.Version,
params=params) params=params)
body = response.read() body = response.read()
boto.log.debug(body) boto.log.debug(body)
if response.status >= 300: if response.status >= 300:
@@ -157,7 +158,7 @@ class Route53Connection(AWSAuthConnection):
hosted_zone_name += '.' hosted_zone_name += '.'
all_hosted_zones = self.get_all_hosted_zones() all_hosted_zones = self.get_all_hosted_zones()
for zone in all_hosted_zones['ListHostedZonesResponse']['HostedZones']: for zone in all_hosted_zones['ListHostedZonesResponse']['HostedZones']:
#check that they gave us the FQDN for their zone # check that they gave us the FQDN for their zone
if zone['Name'] == hosted_zone_name: if zone['Name'] == hosted_zone_name:
return self.get_hosted_zone(zone['Id'].split('/')[-1]) return self.get_hosted_zone(zone['Id'].split('/')[-1])
@@ -233,7 +234,6 @@ class Route53Connection(AWSAuthConnection):
h.parse(body) h.parse(body)
return e return e
# Health checks # Health checks
POSTHCXMLBody = """<CreateHealthCheckRequest xmlns="%(xmlns)s"> POSTHCXMLBody = """<CreateHealthCheckRequest xmlns="%(xmlns)s">
@@ -327,7 +327,6 @@ class Route53Connection(AWSAuthConnection):
h.parse(body) h.parse(body)
return e return e
# Resource Record Sets # Resource Record Sets
def get_all_rrsets(self, hosted_zone_id, type=None, def get_all_rrsets(self, hosted_zone_id, type=None,
@@ -383,7 +382,7 @@ class Route53Connection(AWSAuthConnection):
""" """
params = {'type': type, 'name': name, params = {'type': type, 'name': name,
'Identifier': identifier, 'maxitems': maxitems} 'identifier': identifier, 'maxitems': maxitems}
uri = '/%s/hostedzone/%s/rrset' % (self.Version, hosted_zone_id) uri = '/%s/hostedzone/%s/rrset' % (self.Version, hosted_zone_id)
response = self.make_request('GET', uri, params=params) response = self.make_request('GET', uri, params=params)
body = response.read() body = response.read()
@@ -522,12 +521,18 @@ class Route53Connection(AWSAuthConnection):
if response.status == 400: if response.status == 400:
code = response.getheader('Code') code = response.getheader('Code')
if code and 'PriorRequestNotComplete' in code: if code:
# This is a case where we need to ignore a 400 error, as # This is a case where we need to ignore a 400 error, as
# Route53 returns this. See # Route53 returns this. See
# http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html # http://docs.aws.amazon.com/Route53/latest/DeveloperGuide/DNSLimitations.html
if 'PriorRequestNotComplete' in code:
error = 'PriorRequestNotComplete'
elif 'Throttling' in code:
error = 'Throttling'
else:
return status
msg = "%s, retry attempt %s" % ( msg = "%s, retry attempt %s" % (
'PriorRequestNotComplete', error,
i i
) )
next_sleep = min(random.random() * (2 ** i), next_sleep = min(random.random() * (2 ** i),

View File

@@ -0,0 +1,40 @@
# Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
from boto.regioninfo import RegionInfo, get_regions
def regions():
"""
Get all available regions for the Amazon Route 53 Domains service.
:rtype: list
:return: A list of :class:`boto.regioninfo.RegionInfo`
"""
from boto.route53.domains.layer1 import Route53DomainsConnection
return get_regions('route53domains',
connection_cls=Route53DomainsConnection)
def connect_to_region(region_name, **kw_params):
for region in regions():
if region.name == region_name:
return region.connect(**kw_params)
return None

View File

@@ -0,0 +1,46 @@
# Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
from boto.exception import BotoServerError
class DuplicateRequest(BotoServerError):
pass
class DomainLimitExceeded(BotoServerError):
pass
class InvalidInput(BotoServerError):
pass
class OperationLimitExceeded(BotoServerError):
pass
class UnsupportedTLD(BotoServerError):
pass
class TLDRulesViolation(BotoServerError):
pass

View File

@@ -0,0 +1,868 @@
# Copyright (c) 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish, dis-
# tribute, sublicense, and/or sell copies of the Software, and to permit
# persons to whom the Software is furnished to do so, subject to the fol-
# lowing conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
import boto
from boto.compat import json
from boto.connection import AWSQueryConnection
from boto.regioninfo import RegionInfo
from boto.exception import JSONResponseError
from boto.route53.domains import exceptions
class Route53DomainsConnection(AWSQueryConnection):
"""
"""
APIVersion = "2014-05-15"
DefaultRegionName = "us-east-1"
DefaultRegionEndpoint = "route53domains.us-east-1.amazonaws.com"
ServiceName = "Route53Domains"
TargetPrefix = "Route53Domains_v20140515"
ResponseError = JSONResponseError
_faults = {
"DuplicateRequest": exceptions.DuplicateRequest,
"DomainLimitExceeded": exceptions.DomainLimitExceeded,
"InvalidInput": exceptions.InvalidInput,
"OperationLimitExceeded": exceptions.OperationLimitExceeded,
"UnsupportedTLD": exceptions.UnsupportedTLD,
"TLDRulesViolation": exceptions.TLDRulesViolation,
}
def __init__(self, **kwargs):
region = kwargs.pop('region', None)
if not region:
region = RegionInfo(self, self.DefaultRegionName,
self.DefaultRegionEndpoint)
if 'host' not in kwargs or kwargs['host'] is None:
kwargs['host'] = region.endpoint
super(Route53DomainsConnection, self).__init__(**kwargs)
self.region = region
def _required_auth_capability(self):
return ['hmac-v4']
def check_domain_availability(self, domain_name, idn_lang_code=None):
"""
This operation checks the availability of one domain name. You
can access this API without authenticating. Note that if the
availability status of a domain is pending, you must submit
another request to determine the availability of the domain
name.
:type domain_name: string
:param domain_name: The name of a domain.
Type: String
Default: None
Constraints: The domain name can contain only the letters a through z,
the numbers 0 through 9, and hyphen (-). Internationalized Domain
Names are not supported.
Required: Yes
:type idn_lang_code: string
:param idn_lang_code: Reserved for future use.
"""
params = {'DomainName': domain_name, }
if idn_lang_code is not None:
params['IdnLangCode'] = idn_lang_code
return self.make_request(action='CheckDomainAvailability',
body=json.dumps(params))
def disable_domain_transfer_lock(self, domain_name):
"""
This operation removes the transfer lock on the domain
(specifically the `clientTransferProhibited` status) to allow
domain transfers. We recommend you refrain from performing
this action unless you intend to transfer the domain to a
different registrar. Successful submission returns an
operation ID that you can use to track the progress and
completion of the action. If the request is not completed
successfully, the domain registrant will be notified by email.
:type domain_name: string
:param domain_name: The name of a domain.
Type: String
Default: None
Constraints: The domain name can contain only the letters a through z,
the numbers 0 through 9, and hyphen (-). Internationalized Domain
Names are not supported.
Required: Yes
"""
params = {'DomainName': domain_name, }
return self.make_request(action='DisableDomainTransferLock',
body=json.dumps(params))
def enable_domain_transfer_lock(self, domain_name):
"""
This operation sets the transfer lock on the domain
(specifically the `clientTransferProhibited` status) to
prevent domain transfers. Successful submission returns an
operation ID that you can use to track the progress and
completion of the action. If the request is not completed
successfully, the domain registrant will be notified by email.
:type domain_name: string
:param domain_name: The name of a domain.
Type: String
Default: None
Constraints: The domain name can contain only the letters a through z,
the numbers 0 through 9, and hyphen (-). Internationalized Domain
Names are not supported.
Required: Yes
"""
params = {'DomainName': domain_name, }
return self.make_request(action='EnableDomainTransferLock',
body=json.dumps(params))
def get_domain_detail(self, domain_name):
"""
This operation returns detailed information about the domain.
The domain's contact information is also returned as part of
the output.
:type domain_name: string
:param domain_name: The name of a domain.
Type: String
Default: None
Constraints: The domain name can contain only the letters a through z,
the numbers 0 through 9, and hyphen (-). Internationalized Domain
Names are not supported.
Required: Yes
"""
params = {'DomainName': domain_name, }
return self.make_request(action='GetDomainDetail',
body=json.dumps(params))
def get_operation_detail(self, operation_id):
"""
This operation returns the current status of an operation that
is not completed.
:type operation_id: string
:param operation_id: The identifier for the operation for which you
want to get the status. Amazon Route 53 returned the identifier in
the response to the original request.
Type: String
Default: None
Required: Yes
"""
params = {'OperationId': operation_id, }
return self.make_request(action='GetOperationDetail',
body=json.dumps(params))
def list_domains(self, marker=None, max_items=None):
"""
This operation returns all the domain names registered with
Amazon Route 53 for the current AWS account.
:type marker: string
:param marker: For an initial request for a list of domains, omit this
element. If the number of domains that are associated with the
current AWS account is greater than the value that you specified
for `MaxItems`, you can use `Marker` to return additional domains.
Get the value of `NextPageMarker` from the previous response, and
submit another request that includes the value of `NextPageMarker`
in the `Marker` element.
Type: String
Default: None
Constraints: The marker must match the value specified in the previous
request.
Required: No
:type max_items: integer
:param max_items: Number of domains to be returned.
Type: Integer
Default: 20
Constraints: A numeral between 1 and 100.
Required: No
"""
params = {}
if marker is not None:
params['Marker'] = marker
if max_items is not None:
params['MaxItems'] = max_items
return self.make_request(action='ListDomains',
body=json.dumps(params))
def list_operations(self, marker=None, max_items=None):
"""
This operation returns the operation IDs of operations that
are not yet complete.
:type marker: string
:param marker: For an initial request for a list of operations, omit
this element. If the number of operations that are not yet complete
is greater than the value that you specified for `MaxItems`, you
can use `Marker` to return additional operations. Get the value of
`NextPageMarker` from the previous response, and submit another
request that includes the value of `NextPageMarker` in the `Marker`
element.
Type: String
Default: None
Required: No
:type max_items: integer
:param max_items: Number of domains to be returned.
Type: Integer
Default: 20
Constraints: A value between 1 and 100.
Required: No
"""
params = {}
if marker is not None:
params['Marker'] = marker
if max_items is not None:
params['MaxItems'] = max_items
return self.make_request(action='ListOperations',
body=json.dumps(params))
def register_domain(self, domain_name, duration_in_years, admin_contact,
registrant_contact, tech_contact, idn_lang_code=None,
auto_renew=None, privacy_protect_admin_contact=None,
privacy_protect_registrant_contact=None,
privacy_protect_tech_contact=None):
"""
This operation registers a domain. Domains are registered by
the AWS registrar partner, Gandi. For some top-level domains
(TLDs), this operation requires extra parameters.
When you register a domain, Amazon Route 53 does the
following:
+ Creates a Amazon Route 53 hosted zone that has the same name
as the domain. Amazon Route 53 assigns four name servers to
your hosted zone and automatically updates your domain
registration with the names of these name servers.
+ Enables autorenew, so your domain registration will renew
automatically each year. We'll notify you in advance of the
renewal date so you can choose whether to renew the
registration.
+ Optionally enables privacy protection, so WHOIS queries
return contact information for our registrar partner, Gandi,
instead of the information you entered for registrant, admin,
and tech contacts.
+ If registration is successful, returns an operation ID that
you can use to track the progress and completion of the
action. If the request is not completed successfully, the
domain registrant is notified by email.
+ Charges your AWS account an amount based on the top-level
domain. For more information, see `Amazon Route 53 Pricing`_.
:type domain_name: string
:param domain_name: The name of a domain.
Type: String
Default: None
Constraints: The domain name can contain only the letters a through z,
the numbers 0 through 9, and hyphen (-). Internationalized Domain
Names are not supported.
Required: Yes
:type idn_lang_code: string
:param idn_lang_code: Reserved for future use.
:type duration_in_years: integer
:param duration_in_years: The number of years the domain will be
registered. Domains are registered for a minimum of one year. The
maximum period depends on the top-level domain.
Type: Integer
Default: 1
Valid values: Integer from 1 to 10
Required: Yes
:type auto_renew: boolean
:param auto_renew: Indicates whether the domain will be automatically
renewed ( `True`) or not ( `False`). Autorenewal only takes effect
after the account is charged.
Type: Boolean
Valid values: `True` | `False`
Default: `True`
Required: No
:type admin_contact: dict
:param admin_contact: Provides detailed contact information.
Type: Complex
Children: `FirstName`, `MiddleName`, `LastName`, `ContactType`,
`OrganizationName`, `AddressLine1`, `AddressLine2`, `City`,
`State`, `CountryCode`, `ZipCode`, `PhoneNumber`, `Email`, `Fax`,
`ExtraParams`
Required: Yes
:type registrant_contact: dict
:param registrant_contact: Provides detailed contact information.
Type: Complex
Children: `FirstName`, `MiddleName`, `LastName`, `ContactType`,
`OrganizationName`, `AddressLine1`, `AddressLine2`, `City`,
`State`, `CountryCode`, `ZipCode`, `PhoneNumber`, `Email`, `Fax`,
`ExtraParams`
Required: Yes
:type tech_contact: dict
:param tech_contact: Provides detailed contact information.
Type: Complex
Children: `FirstName`, `MiddleName`, `LastName`, `ContactType`,
`OrganizationName`, `AddressLine1`, `AddressLine2`, `City`,
`State`, `CountryCode`, `ZipCode`, `PhoneNumber`, `Email`, `Fax`,
`ExtraParams`
Required: Yes
:type privacy_protect_admin_contact: boolean
:param privacy_protect_admin_contact: Whether you want to conceal
contact information from WHOIS queries. If you specify true, WHOIS
("who is") queries will return contact information for our
registrar partner, Gandi, instead of the contact information that
you enter.
Type: Boolean
Default: `True`
Valid values: `True` | `False`
Required: No
:type privacy_protect_registrant_contact: boolean
:param privacy_protect_registrant_contact: Whether you want to conceal
contact information from WHOIS queries. If you specify true, WHOIS
("who is") queries will return contact information for our
registrar partner, Gandi, instead of the contact information that
you enter.
Type: Boolean
Default: `True`
Valid values: `True` | `False`
Required: No
:type privacy_protect_tech_contact: boolean
:param privacy_protect_tech_contact: Whether you want to conceal
contact information from WHOIS queries. If you specify true, WHOIS
("who is") queries will return contact information for our
registrar partner, Gandi, instead of the contact information that
you enter.
Type: Boolean
Default: `True`
Valid values: `True` | `False`
Required: No
"""
params = {
'DomainName': domain_name,
'DurationInYears': duration_in_years,
'AdminContact': admin_contact,
'RegistrantContact': registrant_contact,
'TechContact': tech_contact,
}
if idn_lang_code is not None:
params['IdnLangCode'] = idn_lang_code
if auto_renew is not None:
params['AutoRenew'] = auto_renew
if privacy_protect_admin_contact is not None:
params['PrivacyProtectAdminContact'] = privacy_protect_admin_contact
if privacy_protect_registrant_contact is not None:
params['PrivacyProtectRegistrantContact'] = privacy_protect_registrant_contact
if privacy_protect_tech_contact is not None:
params['PrivacyProtectTechContact'] = privacy_protect_tech_contact
return self.make_request(action='RegisterDomain',
body=json.dumps(params))
def retrieve_domain_auth_code(self, domain_name):
"""
This operation returns the AuthCode for the domain. To
transfer a domain to another registrar, you provide this value
to the new registrar.
:type domain_name: string
:param domain_name: The name of a domain.
Type: String
Default: None
Constraints: The domain name can contain only the letters a through z,
the numbers 0 through 9, and hyphen (-). Internationalized Domain
Names are not supported.
Required: Yes
"""
params = {'DomainName': domain_name, }
return self.make_request(action='RetrieveDomainAuthCode',
body=json.dumps(params))
def transfer_domain(self, domain_name, duration_in_years, nameservers,
admin_contact, registrant_contact, tech_contact,
idn_lang_code=None, auth_code=None, auto_renew=None,
privacy_protect_admin_contact=None,
privacy_protect_registrant_contact=None,
privacy_protect_tech_contact=None):
"""
This operation transfers a domain from another registrar to
Amazon Route 53. Domains are registered by the AWS registrar,
Gandi upon transfer.
To transfer a domain, you need to meet all the domain transfer
criteria, including the following:
+ You must supply nameservers to transfer a domain.
+ You must disable the domain transfer lock (if any) before
transferring the domain.
+ A minimum of 60 days must have elapsed since the domain's
registration or last transfer.
We recommend you use the Amazon Route 53 as the DNS service
for your domain. You can create a hosted zone in Amazon Route
53 for your current domain before transferring your domain.
Note that upon transfer, the domain duration is extended for a
year if not otherwise specified. Autorenew is enabled by
default.
If the transfer is successful, this method returns an
operation ID that you can use to track the progress and
completion of the action. If the request is not completed
successfully, the domain registrant will be notified by email.
Transferring domains charges your AWS account an amount based
on the top-level domain. For more information, see `Amazon
Route 53 Pricing`_.
:type domain_name: string
:param domain_name: The name of a domain.
Type: String
Default: None
Constraints: The domain name can contain only the letters a through z,
the numbers 0 through 9, and hyphen (-). Internationalized Domain
Names are not supported.
Required: Yes
:type idn_lang_code: string
:param idn_lang_code: Reserved for future use.
:type duration_in_years: integer
:param duration_in_years: The number of years the domain will be
registered. Domains are registered for a minimum of one year. The
maximum period depends on the top-level domain.
Type: Integer
Default: 1
Valid values: Integer from 1 to 10
Required: Yes
:type nameservers: list
:param nameservers: Contains details for the host and glue IP
addresses.
Type: Complex
Children: `GlueIps`, `Name`
:type auth_code: string
:param auth_code: The authorization code for the domain. You get this
value from the current registrar.
Type: String
Required: Yes
:type auto_renew: boolean
:param auto_renew: Indicates whether the domain will be automatically
renewed (true) or not (false). Autorenewal only takes effect after
the account is charged.
Type: Boolean
Valid values: `True` | `False`
Default: true
Required: No
:type admin_contact: dict
:param admin_contact: Provides detailed contact information.
Type: Complex
Children: `FirstName`, `MiddleName`, `LastName`, `ContactType`,
`OrganizationName`, `AddressLine1`, `AddressLine2`, `City`,
`State`, `CountryCode`, `ZipCode`, `PhoneNumber`, `Email`, `Fax`,
`ExtraParams`
Required: Yes
:type registrant_contact: dict
:param registrant_contact: Provides detailed contact information.
Type: Complex
Children: `FirstName`, `MiddleName`, `LastName`, `ContactType`,
`OrganizationName`, `AddressLine1`, `AddressLine2`, `City`,
`State`, `CountryCode`, `ZipCode`, `PhoneNumber`, `Email`, `Fax`,
`ExtraParams`
Required: Yes
:type tech_contact: dict
:param tech_contact: Provides detailed contact information.
Type: Complex
Children: `FirstName`, `MiddleName`, `LastName`, `ContactType`,
`OrganizationName`, `AddressLine1`, `AddressLine2`, `City`,
`State`, `CountryCode`, `ZipCode`, `PhoneNumber`, `Email`, `Fax`,
`ExtraParams`
Required: Yes
:type privacy_protect_admin_contact: boolean
:param privacy_protect_admin_contact: Whether you want to conceal
contact information from WHOIS queries. If you specify true, WHOIS
("who is") queries will return contact information for our
registrar partner, Gandi, instead of the contact information that
you enter.
Type: Boolean
Default: `True`
Valid values: `True` | `False`
Required: No
:type privacy_protect_registrant_contact: boolean
:param privacy_protect_registrant_contact: Whether you want to conceal
contact information from WHOIS queries. If you specify true, WHOIS
("who is") queries will return contact information for our
registrar partner, Gandi, instead of the contact information that
you enter.
Type: Boolean
Default: `True`
Valid values: `True` | `False`
Required: No
:type privacy_protect_tech_contact: boolean
:param privacy_protect_tech_contact: Whether you want to conceal
contact information from WHOIS queries. If you specify true, WHOIS
("who is") queries will return contact information for our
registrar partner, Gandi, instead of the contact information that
you enter.
Type: Boolean
Default: `True`
Valid values: `True` | `False`
Required: No
"""
params = {
'DomainName': domain_name,
'DurationInYears': duration_in_years,
'Nameservers': nameservers,
'AdminContact': admin_contact,
'RegistrantContact': registrant_contact,
'TechContact': tech_contact,
}
if idn_lang_code is not None:
params['IdnLangCode'] = idn_lang_code
if auth_code is not None:
params['AuthCode'] = auth_code
if auto_renew is not None:
params['AutoRenew'] = auto_renew
if privacy_protect_admin_contact is not None:
params['PrivacyProtectAdminContact'] = privacy_protect_admin_contact
if privacy_protect_registrant_contact is not None:
params['PrivacyProtectRegistrantContact'] = privacy_protect_registrant_contact
if privacy_protect_tech_contact is not None:
params['PrivacyProtectTechContact'] = privacy_protect_tech_contact
return self.make_request(action='TransferDomain',
body=json.dumps(params))
def update_domain_contact(self, domain_name, admin_contact=None,
registrant_contact=None, tech_contact=None):
"""
This operation updates the contact information for a
particular domain. Information for at least one contact
(registrant, administrator, or technical) must be supplied for
update.
If the update is successful, this method returns an operation
ID that you can use to track the progress and completion of
the action. If the request is not completed successfully, the
domain registrant will be notified by email.
:type domain_name: string
:param domain_name: The name of a domain.
Type: String
Default: None
Constraints: The domain name can contain only the letters a through z,
the numbers 0 through 9, and hyphen (-). Internationalized Domain
Names are not supported.
Required: Yes
:type admin_contact: dict
:param admin_contact: Provides detailed contact information.
Type: Complex
Children: `FirstName`, `MiddleName`, `LastName`, `ContactType`,
`OrganizationName`, `AddressLine1`, `AddressLine2`, `City`,
`State`, `CountryCode`, `ZipCode`, `PhoneNumber`, `Email`, `Fax`,
`ExtraParams`
Required: Yes
:type registrant_contact: dict
:param registrant_contact: Provides detailed contact information.
Type: Complex
Children: `FirstName`, `MiddleName`, `LastName`, `ContactType`,
`OrganizationName`, `AddressLine1`, `AddressLine2`, `City`,
`State`, `CountryCode`, `ZipCode`, `PhoneNumber`, `Email`, `Fax`,
`ExtraParams`
Required: Yes
:type tech_contact: dict
:param tech_contact: Provides detailed contact information.
Type: Complex
Children: `FirstName`, `MiddleName`, `LastName`, `ContactType`,
`OrganizationName`, `AddressLine1`, `AddressLine2`, `City`,
`State`, `CountryCode`, `ZipCode`, `PhoneNumber`, `Email`, `Fax`,
`ExtraParams`
Required: Yes
"""
params = {'DomainName': domain_name, }
if admin_contact is not None:
params['AdminContact'] = admin_contact
if registrant_contact is not None:
params['RegistrantContact'] = registrant_contact
if tech_contact is not None:
params['TechContact'] = tech_contact
return self.make_request(action='UpdateDomainContact',
body=json.dumps(params))
def update_domain_contact_privacy(self, domain_name, admin_privacy=None,
registrant_privacy=None,
tech_privacy=None):
"""
This operation updates the specified domain contact's privacy
setting. When the privacy option is enabled, personal
information such as postal or email address is hidden from the
results of a public WHOIS query. The privacy services are
provided by the AWS registrar, Gandi. For more information,
see the `Gandi privacy features`_.
This operation only affects the privacy of the specified
contact type (registrant, administrator, or tech). Successful
acceptance returns an operation ID that you can use with
GetOperationDetail to track the progress and completion of the
action. If the request is not completed successfully, the
domain registrant will be notified by email.
:type domain_name: string
:param domain_name: The name of a domain.
Type: String
Default: None
Constraints: The domain name can contain only the letters a through z,
the numbers 0 through 9, and hyphen (-). Internationalized Domain
Names are not supported.
Required: Yes
:type admin_privacy: boolean
:param admin_privacy: Whether you want to conceal contact information
from WHOIS queries. If you specify true, WHOIS ("who is") queries
will return contact information for our registrar partner, Gandi,
instead of the contact information that you enter.
Type: Boolean
Default: None
Valid values: `True` | `False`
Required: No
:type registrant_privacy: boolean
:param registrant_privacy: Whether you want to conceal contact
information from WHOIS queries. If you specify true, WHOIS ("who
is") queries will return contact information for our registrar
partner, Gandi, instead of the contact information that you enter.
Type: Boolean
Default: None
Valid values: `True` | `False`
Required: No
:type tech_privacy: boolean
:param tech_privacy: Whether you want to conceal contact information
from WHOIS queries. If you specify true, WHOIS ("who is") queries
will return contact information for our registrar partner, Gandi,
instead of the contact information that you enter.
Type: Boolean
Default: None
Valid values: `True` | `False`
Required: No
"""
params = {'DomainName': domain_name, }
if admin_privacy is not None:
params['AdminPrivacy'] = admin_privacy
if registrant_privacy is not None:
params['RegistrantPrivacy'] = registrant_privacy
if tech_privacy is not None:
params['TechPrivacy'] = tech_privacy
return self.make_request(action='UpdateDomainContactPrivacy',
body=json.dumps(params))
def update_domain_nameservers(self, domain_name, nameservers):
"""
This operation replaces the current set of name servers for
the domain with the specified set of name servers. If you use
Amazon Route 53 as your DNS service, specify the four name
servers in the delegation set for the hosted zone for the
domain.
If successful, this operation returns an operation ID that you
can use to track the progress and completion of the action. If
the request is not completed successfully, the domain
registrant will be notified by email.
:type domain_name: string
:param domain_name: The name of a domain.
Type: String
Default: None
Constraints: The domain name can contain only the letters a through z,
the numbers 0 through 9, and hyphen (-). Internationalized Domain
Names are not supported.
Required: Yes
:type nameservers: list
:param nameservers: A list of new name servers for the domain.
Type: Complex
Children: `Name`, `GlueIps`
Required: Yes
"""
params = {
'DomainName': domain_name,
'Nameservers': nameservers,
}
return self.make_request(action='UpdateDomainNameservers',
body=json.dumps(params))
def make_request(self, action, body):
headers = {
'X-Amz-Target': '%s.%s' % (self.TargetPrefix, action),
'Host': self.region.endpoint,
'Content-Type': 'application/x-amz-json-1.1',
'Content-Length': str(len(body)),
}
http_request = self.build_base_http_request(
method='POST', path='/', auth_path='/', params={},
headers=headers, data=body)
response = self._mexe(http_request, sender=None,
override_num_retries=10)
response_body = response.read().decode('utf-8')
boto.log.debug(response_body)
if response.status == 200:
if response_body:
return json.loads(response_body)
else:
json_body = json.loads(response_body)
fault_name = json_body.get('__type', None)
exception_class = self._faults.get(fault_name, self.ResponseError)
raise exception_class(response.status, response.reason,
body=json_body)

View File

@@ -15,13 +15,13 @@
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL-
# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
from boto.exception import BotoServerError from boto.exception import BotoServerError
class DNSServerError(BotoServerError):
class DNSServerError(BotoServerError):
pass pass

View File

@@ -141,6 +141,6 @@ class HealthCheck(object):
params['ip_addr_part'] = self.XMLIpAddrPart % {'ip_addr': self.ip_addr} params['ip_addr_part'] = self.XMLIpAddrPart % {'ip_addr': self.ip_addr}
if self.string_match is not None: if self.string_match is not None:
params['string_match_part'] = self.XMLStringMatchPart % {'string_match' : self.string_match} params['string_match_part'] = self.XMLStringMatchPart % {'string_match': self.string_match}
return self.POSTXMLBody % params return self.POSTXMLBody % params

View File

@@ -22,6 +22,7 @@
# IN THE SOFTWARE. # IN THE SOFTWARE.
# #
class HostedZone(object): class HostedZone(object):
def __init__(self, id=None, name=None, owner=None, version=None, def __init__(self, id=None, name=None, owner=None, version=None,
@@ -53,4 +54,3 @@ class HostedZone(object):
self.caller_reference = value self.caller_reference = value
else: else:
setattr(self, name, value) setattr(self, name, value)

View File

@@ -25,6 +25,8 @@
RECORD_TYPES = ['A', 'AAAA', 'TXT', 'CNAME', 'MX', 'PTR', 'SRV', 'SPF'] RECORD_TYPES = ['A', 'AAAA', 'TXT', 'CNAME', 'MX', 'PTR', 'SRV', 'SPF']
from boto.resultset import ResultSet from boto.resultset import ResultSet
class ResourceRecordSets(ResultSet): class ResourceRecordSets(ResultSet):
""" """
A list of resource records. A list of resource records.
@@ -54,6 +56,7 @@ class ResourceRecordSets(ResultSet):
self.changes = [] self.changes = []
self.next_record_name = None self.next_record_name = None
self.next_record_type = None self.next_record_type = None
self.next_record_identifier = None
super(ResourceRecordSets, self).__init__([('ResourceRecordSet', Record)]) super(ResourceRecordSets, self).__init__([('ResourceRecordSet', Record)])
def __repr__(self): def __repr__(self):
@@ -65,9 +68,9 @@ class ResourceRecordSets(ResultSet):
record_list) record_list)
def add_change(self, action, name, type, ttl=600, def add_change(self, action, name, type, ttl=600,
alias_hosted_zone_id=None, alias_dns_name=None, identifier=None, alias_hosted_zone_id=None, alias_dns_name=None, identifier=None,
weight=None, region=None, alias_evaluate_target_health=None, weight=None, region=None, alias_evaluate_target_health=None,
health_check=None, failover=None): health_check=None, failover=None):
""" """
Add a change request to the set. Add a change request to the set.
@@ -121,10 +124,10 @@ class ResourceRecordSets(ResultSet):
for the latency-based routing for the latency-based routing
:type alias_evaluate_target_health: Boolean :type alias_evaluate_target_health: Boolean
:param alias_evaluate_target_health: *Required for alias resource record sets* Indicates :param alias_evaluate_target_health: *Required for alias resource record
whether this Resource Record Set should respect the health status of sets* Indicates whether this Resource Record Set should respect the
any health checks associated with the ALIAS target record which it is health status of any health checks associated with the ALIAS target
linked to. record which it is linked to.
:type health_check: str :type health_check: str
:param health_check: Health check to associate with this record :param health_check: Health check to associate with this record
@@ -134,11 +137,11 @@ class ResourceRecordSets(ResultSet):
primary or secondary resource record set. primary or secondary resource record set.
""" """
change = Record(name, type, ttl, change = Record(name, type, ttl,
alias_hosted_zone_id=alias_hosted_zone_id, alias_hosted_zone_id=alias_hosted_zone_id,
alias_dns_name=alias_dns_name, identifier=identifier, alias_dns_name=alias_dns_name, identifier=identifier,
weight=weight, region=region, weight=weight, region=region,
alias_evaluate_target_health=alias_evaluate_target_health, alias_evaluate_target_health=alias_evaluate_target_health,
health_check=health_check, failover=failover) health_check=health_check, failover=failover)
self.changes.append([action, change]) self.changes.append([action, change])
return change return change
@@ -165,12 +168,14 @@ class ResourceRecordSets(ResultSet):
return self.connection.change_rrsets(self.hosted_zone_id, self.to_xml()) return self.connection.change_rrsets(self.hosted_zone_id, self.to_xml())
def endElement(self, name, value, connection): def endElement(self, name, value, connection):
"""Overwritten to also add the NextRecordName and """Overwritten to also add the NextRecordName,
NextRecordType to the base object""" NextRecordType and NextRecordIdentifier to the base object"""
if name == 'NextRecordName': if name == 'NextRecordName':
self.next_record_name = value self.next_record_name = value
elif name == 'NextRecordType': elif name == 'NextRecordType':
self.next_record_type = value self.next_record_type = value
elif name == 'NextRecordIdentifier':
self.next_record_identifier = value
else: else:
return super(ResourceRecordSets, self).endElement(name, value, connection) return super(ResourceRecordSets, self).endElement(name, value, connection)
@@ -183,14 +188,14 @@ class ResourceRecordSets(ResultSet):
yield obj yield obj
if self.is_truncated: if self.is_truncated:
self.is_truncated = False self.is_truncated = False
results = self.connection.get_all_rrsets(self.hosted_zone_id, name=self.next_record_name, type=self.next_record_type) results = self.connection.get_all_rrsets(self.hosted_zone_id, name=self.next_record_name,
type=self.next_record_type,
identifier=self.next_record_identifier)
else: else:
results = None results = None
self.is_truncated = truncated self.is_truncated = truncated
class Record(object): class Record(object):
"""An individual ResourceRecordSet""" """An individual ResourceRecordSet"""
@@ -237,11 +242,10 @@ class Record(object):
EvaluateTargetHealth = """<EvaluateTargetHealth>%s</EvaluateTargetHealth>""" EvaluateTargetHealth = """<EvaluateTargetHealth>%s</EvaluateTargetHealth>"""
def __init__(self, name=None, type=None, ttl=600, resource_records=None, def __init__(self, name=None, type=None, ttl=600, resource_records=None,
alias_hosted_zone_id=None, alias_dns_name=None, identifier=None, alias_hosted_zone_id=None, alias_dns_name=None, identifier=None,
weight=None, region=None, alias_evaluate_target_health=None, weight=None, region=None, alias_evaluate_target_health=None,
health_check=None, failover=None): health_check=None, failover=None):
self.name = name self.name = name
self.type = type self.type = type
self.ttl = ttl self.ttl = ttl
@@ -280,9 +284,9 @@ class Record(object):
else: else:
eval_target_health = "" eval_target_health = ""
body = self.AliasBody % { "hosted_zone_id": self.alias_hosted_zone_id, body = self.AliasBody % {"hosted_zone_id": self.alias_hosted_zone_id,
"dns_name": self.alias_dns_name, "dns_name": self.alias_dns_name,
"eval_target_health": eval_target_health } "eval_target_health": eval_target_health}
else: else:
# Use resource record(s) # Use resource record(s)
records = "" records = ""
@@ -298,14 +302,14 @@ class Record(object):
weight = "" weight = ""
if self.identifier is not None and self.weight is not None: if self.identifier is not None and self.weight is not None:
weight = self.WRRBody % {"identifier": self.identifier, "weight": weight = self.WRRBody % {"identifier": self.identifier,
self.weight} "weight": self.weight}
elif self.identifier is not None and self.region is not None: elif self.identifier is not None and self.region is not None:
weight = self.RRRBody % {"identifier": self.identifier, "region": weight = self.RRRBody % {"identifier": self.identifier,
self.region} "region": self.region}
elif self.identifier is not None and self.failover is not None: elif self.identifier is not None and self.failover is not None:
weight = self.FailoverBody % {"identifier": self.identifier, "failover": weight = self.FailoverBody % {"identifier": self.identifier,
self.failover} "failover": self.failover}
health_check = "" health_check = ""
if self.health_check is not None: if self.health_check is not None:
@@ -329,7 +333,7 @@ class Record(object):
rr += ' (EvalTarget %s)' % self.alias_evaluate_target_health rr += ' (EvalTarget %s)' % self.alias_evaluate_target_health
else: else:
# Show resource record(s) # Show resource record(s)
rr = ",".join(self.resource_records) rr = ",".join(self.resource_records)
if self.identifier is not None and self.weight is not None: if self.identifier is not None and self.weight is not None:
rr += ' (WRR id=%s, w=%s)' % (self.identifier, self.weight) rr += ' (WRR id=%s, w=%s)' % (self.identifier, self.weight)

View File

@@ -60,7 +60,7 @@ class Zone(object):
return response['ChangeResourceRecordSetsResponse']['ChangeInfo'] return response['ChangeResourceRecordSetsResponse']['ChangeInfo']
def _new_record(self, changes, resource_type, name, value, ttl, identifier, def _new_record(self, changes, resource_type, name, value, ttl, identifier,
comment=""): comment=""):
""" """
Add a CREATE change record to an existing ResourceRecordSets Add a CREATE change record to an existing ResourceRecordSets
@@ -233,7 +233,14 @@ class Zone(object):
# name/type for get_all_rrsets sets the starting record; they # name/type for get_all_rrsets sets the starting record; they
# are not a filter # are not a filter
results = [r for r in returned if r.name == name and r.type == type] results = []
for r in returned:
if r.name == name and r.type == type:
results.append(r)
# Is at the end of the list of matched records. No need to continue
# since the records are sorted by name and type.
else:
break
weight = None weight = None
region = None region = None

View File

@@ -204,12 +204,9 @@ class Bucket(object):
k = self.key_class(self) k = self.key_class(self)
provider = self.connection.provider provider = self.connection.provider
k.metadata = boto.utils.get_aws_metadata(response.msg, provider) k.metadata = boto.utils.get_aws_metadata(response.msg, provider)
k.etag = response.getheader('etag') for field in Key.base_fields:
k.content_type = response.getheader('content-type') k.__dict__[field.lower().replace('-', '_')] = \
k.content_encoding = response.getheader('content-encoding') response.getheader(field)
k.content_disposition = response.getheader('content-disposition')
k.content_language = response.getheader('content-language')
k.last_modified = response.getheader('last-modified')
# the following machinations are a workaround to the fact that # the following machinations are a workaround to the fact that
# apache/fastcgi omits the content-length header on HEAD # apache/fastcgi omits the content-length header on HEAD
# requests when the content-length is zero. # requests when the content-length is zero.
@@ -219,7 +216,6 @@ class Bucket(object):
k.size = int(response.getheader('content-length')) k.size = int(response.getheader('content-length'))
else: else:
k.size = 0 k.size = 0
k.cache_control = response.getheader('cache-control')
k.name = key_name k.name = key_name
k.handle_version_headers(response) k.handle_version_headers(response)
k.handle_encryption_headers(response) k.handle_encryption_headers(response)
@@ -381,7 +377,9 @@ class Bucket(object):
key = 'max-keys' key = 'max-keys'
if not isinstance(value, six.string_types + (six.binary_type,)): if not isinstance(value, six.string_types + (six.binary_type,)):
value = six.text_type(value) value = six.text_type(value)
if value != '': if not isinstance(value, six.binary_type):
value = value.encode('utf-8')
if value:
pairs.append(u'%s=%s' % ( pairs.append(u'%s=%s' % (
urllib.parse.quote(key), urllib.parse.quote(key),
urllib.parse.quote(value) urllib.parse.quote(value)

View File

@@ -19,6 +19,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE. # IN THE SOFTWARE.
from boto.compat import urllib, six
def bucket_lister(bucket, prefix='', delimiter='', marker='', headers=None, def bucket_lister(bucket, prefix='', delimiter='', marker='', headers=None,
encoding_type=None): encoding_type=None):
""" """
@@ -34,6 +36,10 @@ def bucket_lister(bucket, prefix='', delimiter='', marker='', headers=None,
yield k yield k
if k: if k:
marker = rs.next_marker or k.name marker = rs.next_marker or k.name
if marker and encoding_type == "url":
if isinstance(marker, six.text_type):
marker = marker.encode('utf-8')
marker = urllib.parse.unquote(marker)
more_results= rs.is_truncated more_results= rs.is_truncated
class BucketListResultSet(object): class BucketListResultSet(object):

View File

@@ -89,10 +89,16 @@ class Key(object):
# x-amz-meta). # x-amz-meta).
base_user_settable_fields = set(["cache-control", "content-disposition", base_user_settable_fields = set(["cache-control", "content-disposition",
"content-encoding", "content-language", "content-encoding", "content-language",
"content-md5", "content-type"]) "content-md5", "content-type",
"x-robots-tag", "expires"])
_underscore_base_user_settable_fields = set() _underscore_base_user_settable_fields = set()
for f in base_user_settable_fields: for f in base_user_settable_fields:
_underscore_base_user_settable_fields.add(f.replace('-', '_')) _underscore_base_user_settable_fields.add(f.replace('-', '_'))
# Metadata fields, whether user-settable or not, other than custom
# metadata fields (i.e., those beginning with a provider specific prefix
# like x-amz-meta).
base_fields = (base_user_settable_fields |
set(["last-modified", "content-length", "date", "etag"]))
@@ -130,9 +136,15 @@ class Key(object):
def __repr__(self): def __repr__(self):
if self.bucket: if self.bucket:
return '<Key: %s,%s>' % (self.bucket.name, self.name) name = u'<Key: %s,%s>' % (self.bucket.name, self.name)
else: else:
return '<Key: None,%s>' % self.name name = u'<Key: None,%s>' % self.name
# Encode to bytes for Python 2 to prevent display decoding issues
if not isinstance(name, str):
name = name.encode('utf-8')
return name
def __iter__(self): def __iter__(self):
return self return self
@@ -302,20 +314,8 @@ class Key(object):
elif name.lower() == 'content-range': elif name.lower() == 'content-range':
end_range = re.sub('.*/(.*)', '\\1', value) end_range = re.sub('.*/(.*)', '\\1', value)
self.size = int(end_range) self.size = int(end_range)
elif name.lower() == 'etag': elif name.lower() in Key.base_fields:
self.etag = value self.__dict__[name.lower().replace('-', '_')] = value
elif name.lower() == 'content-type':
self.content_type = value
elif name.lower() == 'content-encoding':
self.content_encoding = value
elif name.lower() == 'content-language':
self.content_language = value
elif name.lower() == 'last-modified':
self.last_modified = value
elif name.lower() == 'cache-control':
self.cache_control = value
elif name.lower() == 'content-disposition':
self.content_disposition = value
self.handle_version_headers(self.resp) self.handle_version_headers(self.resp)
self.handle_encryption_headers(self.resp) self.handle_encryption_headers(self.resp)
self.handle_restore_headers(self.resp) self.handle_restore_headers(self.resp)
@@ -555,6 +555,8 @@ class Key(object):
self.metadata['Content-MD5'] = value self.metadata['Content-MD5'] = value
else: else:
self.metadata[name] = value self.metadata[name] = value
if name.lower() in Key.base_user_settable_fields:
self.__dict__[name.lower().replace('-', '_')] = value
def update_metadata(self, d): def update_metadata(self, d):
self.metadata.update(d) self.metadata.update(d)
@@ -932,7 +934,10 @@ class Key(object):
# the auth mechanism (because closures). Detect if it's SigV4 & embelish # the auth mechanism (because closures). Detect if it's SigV4 & embelish
# while we can before the auth calculations occur. # while we can before the auth calculations occur.
if 'hmac-v4-s3' in self.bucket.connection._required_auth_capability(): if 'hmac-v4-s3' in self.bucket.connection._required_auth_capability():
headers['_sha256'] = compute_hash(fp, hash_algorithm=hashlib.sha256)[0] kwargs = {'fp': fp, 'hash_algorithm': hashlib.sha256}
if size is not None:
kwargs['size'] = size
headers['_sha256'] = compute_hash(**kwargs)[0]
headers['Expect'] = '100-Continue' headers['Expect'] = '100-Continue'
headers = boto.utils.merge_meta(headers, self.metadata, provider) headers = boto.utils.merge_meta(headers, self.metadata, provider)
resp = self.bucket.connection.make_request( resp = self.bucket.connection.make_request(

View File

@@ -48,13 +48,13 @@ class SESConnection(AWSAuthConnection):
self.DefaultRegionEndpoint) self.DefaultRegionEndpoint)
self.region = region self.region = region
super(SESConnection, self).__init__(self.region.endpoint, super(SESConnection, self).__init__(self.region.endpoint,
aws_access_key_id, aws_secret_access_key, aws_access_key_id, aws_secret_access_key,
is_secure, port, proxy, proxy_port, is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass, debug, proxy_user, proxy_pass, debug,
https_connection_factory, path, https_connection_factory, path,
security_token=security_token, security_token=security_token,
validate_certs=validate_certs, validate_certs=validate_certs,
profile_name=profile_name) profile_name=profile_name)
def _required_auth_capability(self): def _required_auth_capability(self):
return ['ses'] return ['ses']
@@ -260,18 +260,18 @@ class SESConnection(AWSAuthConnection):
raise ValueError("No text or html body found for mail") raise ValueError("No text or html body found for mail")
self._build_list_params(params, to_addresses, self._build_list_params(params, to_addresses,
'Destination.ToAddresses.member') 'Destination.ToAddresses.member')
if cc_addresses: if cc_addresses:
self._build_list_params(params, cc_addresses, self._build_list_params(params, cc_addresses,
'Destination.CcAddresses.member') 'Destination.CcAddresses.member')
if bcc_addresses: if bcc_addresses:
self._build_list_params(params, bcc_addresses, self._build_list_params(params, bcc_addresses,
'Destination.BccAddresses.member') 'Destination.BccAddresses.member')
if reply_addresses: if reply_addresses:
self._build_list_params(params, reply_addresses, self._build_list_params(params, reply_addresses,
'ReplyToAddresses.member') 'ReplyToAddresses.member')
return self._make_request('SendEmail', params) return self._make_request('SendEmail', params)
@@ -318,7 +318,7 @@ class SESConnection(AWSAuthConnection):
if destinations: if destinations:
self._build_list_params(params, destinations, self._build_list_params(params, destinations,
'Destinations.member') 'Destinations.member')
return self._make_request('SendRawEmail', params) return self._make_request('SendRawEmail', params)
@@ -475,7 +475,7 @@ class SESConnection(AWSAuthConnection):
""" """
params = {} params = {}
self._build_list_params(params, identities, self._build_list_params(params, identities,
'Identities.member') 'Identities.member')
return self._make_request('GetIdentityVerificationAttributes', params) return self._make_request('GetIdentityVerificationAttributes', params)
def verify_domain_identity(self, domain): def verify_domain_identity(self, domain):
@@ -531,17 +531,17 @@ class SESConnection(AWSAuthConnection):
:param identity: An email address or domain name. :param identity: An email address or domain name.
:type notification_type: string :type notification_type: string
:param notification_type: The type of feedback notifications that will :param notification_type: The type of feedback notifications that will
be published to the specified topic. be published to the specified topic.
Valid Values: Bounce | Complaint | Delivery Valid Values: Bounce | Complaint | Delivery
:type sns_topic: string or None :type sns_topic: string or None
:param sns_topic: The Amazon Resource Name (ARN) of the Amazon Simple :param sns_topic: The Amazon Resource Name (ARN) of the Amazon Simple
Notification Service (Amazon SNS) topic. Notification Service (Amazon SNS) topic.
""" """
params = { params = {
'Identity': identity, 'Identity': identity,
'NotificationType': notification_type 'NotificationType': notification_type
} }
if sns_topic: if sns_topic:
params['SnsTopic'] = sns_topic params['SnsTopic'] = sns_topic
@@ -560,7 +560,6 @@ class SESConnection(AWSAuthConnection):
:param forwarding_enabled: Specifies whether or not to enable feedback forwarding. :param forwarding_enabled: Specifies whether or not to enable feedback forwarding.
""" """
return self._make_request('SetIdentityFeedbackForwardingEnabled', { return self._make_request('SetIdentityFeedbackForwardingEnabled', {
'Identity': identity, 'Identity': identity,
'ForwardingEnabled': 'true' if forwarding_enabled else 'false' 'ForwardingEnabled': 'true' if forwarding_enabled else 'false'
}) })

View File

@@ -3,6 +3,7 @@ Various exceptions that are specific to the SES module.
""" """
from boto.exception import BotoServerError from boto.exception import BotoServerError
class SESError(BotoServerError): class SESError(BotoServerError):
""" """
Sub-class all SES-related errors from here. Don't raise this error Sub-class all SES-related errors from here. Don't raise this error
@@ -13,24 +14,26 @@ class SESError(BotoServerError):
pass pass
class SESAddressNotVerifiedError(SESError): class SESAddressNotVerifiedError(SESError):
""" """
Raised when a "Reply-To" address has not been validated in SES yet. Raised when a "Reply-To" address has not been validated in SES yet.
""" """
pass pass
class SESIdentityNotVerifiedError(SESError): class SESIdentityNotVerifiedError(SESError):
""" """
Raised when an identity (domain or address) has not been verified in SES yet. Raised when an identity (domain or address) has not been verified in SES yet.
""" """
pass pass
class SESDomainNotConfirmedError(SESError): class SESDomainNotConfirmedError(SESError):
""" """
""" """
pass pass
class SESAddressBlacklistedError(SESError): class SESAddressBlacklistedError(SESError):
""" """
After you attempt to send mail to an address, and delivery repeatedly After you attempt to send mail to an address, and delivery repeatedly

Some files were not shown because too many files have changed in this diff Show More