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)
Babel==1.3 (babel/*, excluded bin/pybabel)
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/fetch_file, bin/glacier, bin/instance_events, bin/kill_instance,
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.exception import InvalidUriError
__version__ = '2.32.1'
__version__ = '2.34.0'
Version = __version__ # for backware compatibility
# http://bugs.python.org/issue7980
@@ -858,6 +858,76 @@ def connect_logs(aws_access_key_id=None,
**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,
bucket_storage_uri_class=BucketStorageUri,
suppress_consec_slashes=True, is_latest=False):

View File

@@ -37,8 +37,6 @@ import datetime
from email.utils import formatdate
import hmac
import os
import sys
import time
import posixpath
from boto.compat import urllib, encodebytes
@@ -317,6 +315,8 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
for name, value in http_request.headers.items():
lname = name.lower()
if lname.startswith('x-amz'):
if isinstance(value, bytes):
value = value.decode('utf-8')
headers_to_sign[name] = value
return headers_to_sign
@@ -345,7 +345,7 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
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.decode('utf-8'), safe='-_.~')))
urllib.parse.quote(value, safe='-_.~')))
return '&'.join(l)
def canonical_headers(self, headers_to_sign):
@@ -376,7 +376,7 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
path = http_request.auth_path
# Normalize the path
# 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.
encoded = urllib.parse.quote(normalized)
if len(path) > 1 and path.endswith('/'):
@@ -472,7 +472,7 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
def signature(self, http_request, string_to_sign):
key = self._provider.secret_key
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_service = self._sign(k_region, http_request.service_name)
k_signing = self._sign(k_service, 'aws4_request')
@@ -494,10 +494,25 @@ class HmacAuthV4Handler(AuthHandler, HmacKeys):
if self._provider.security_token:
req.headers['X-Amz-Security-Token'] = self._provider.security_token
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
# 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-Length'] = str(len(req.body))
else:
@@ -549,6 +564,17 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
encoded = urllib.parse.quote(unquoted)
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):
port = http_request.port
secure = http_request.protocol == 'https'
@@ -568,7 +594,7 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
# Hooray for the only difference! The main SigV4 signer only does
# ``Host`` + ``x-amz-*``. But S3 wants pretty much everything
# signed, except for authorization itself.
if not lname in ['authorization']:
if lname not in ['authorization']:
headers_to_sign[name] = value
return headers_to_sign
@@ -581,8 +607,8 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
# - s3-us-west-2.amazonaws.com (Specific region)
# - bukkit.s3.amazonaws.com (Vhosted Classic)
# - bukkit.s3-ap-northeast-1.amazonaws.com (Vhosted specific region)
# - s3.cn-north-1.amazonaws.com.cn - (Bejing region)
# - bukkit.s3.cn-north-1.amazonaws.com.cn - (Vhosted Bejing region)
# - s3.cn-north-1.amazonaws.com.cn - (Beijing region)
# - bukkit.s3.cn-north-1.amazonaws.com.cn - (Vhosted Beijing region)
parts = self.split_host_parts(host)
if self.region_name is not None:
@@ -641,6 +667,13 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
if modified_req.params is None:
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
existing_qs = urllib.parse.parse_qs(
@@ -665,14 +698,15 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
return super(S3HmacAuthV4Handler, self).payload(http_request)
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:
req.headers['x-amz-content-sha256'] = req.headers.pop('_sha256')
else:
req.headers['x-amz-content-sha256'] = self.payload(req)
req = self.mangle_path_and_params(req)
return super(S3HmacAuthV4Handler, self).add_auth(req, **kwargs)
updated_req = self.mangle_path_and_params(req)
return super(S3HmacAuthV4Handler, self).add_auth(updated_req,
unmangled_req=req,
**kwargs)
def presign(self, req, expires, iso_date=None):
"""
@@ -703,6 +737,10 @@ class S3HmacAuthV4Handler(HmacAuthV4Handler, AuthHandler):
if 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)
cr = self.canonical_request(req)
@@ -749,7 +787,6 @@ class QueryAuthHandler(AuthHandler):
def add_auth(self, http_request, **kwargs):
headers = http_request.headers
params = http_request.params
qs = self._build_query_string(
http_request.params
)
@@ -897,7 +934,7 @@ class POSTPathQSV2AuthHandler(QuerySignatureV2AuthHandler, AuthHandler):
# already be there, we need to get rid of that and rebuild it
req.path = req.path.split('?')[0]
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):
@@ -923,7 +960,6 @@ def get_auth_handler(host, config, provider, requested_capability=None):
"""
ready_handlers = []
auth_handlers = boto.plugin.get_plugin(AuthHandler, requested_capability)
total_handlers = len(auth_handlers)
for handler in auth_handlers:
try:
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
names = [handler.__name__ for handler in checked_handlers]
raise boto.exception.NoAuthHandlerFound(
'No handler was ready to authenticate. %d handlers were checked.'
' %s '
'Check your credentials' % (len(names), str(names)))
'No handler was ready to authenticate. %d handlers were checked.'
' %s '
'Check your credentials' % (len(names), str(names)))
# 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
@@ -964,7 +1000,8 @@ def detect_potential_sigv4(func):
# ``boto/iam/connection.py``, as several things there are also
# endpoint-related.
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 func(self)
@@ -983,7 +1020,7 @@ def detect_potential_s3sigv4(func):
# If you're making changes here, you should also check
# ``boto/iam/connection.py``, as several things there are also
# endpoint-related.
if '.cn-' in self.host:
if '.cn-' in self.host or '.eu-central' in self.host:
return ['hmac-v4-s3']
return func(self)

View File

@@ -14,7 +14,7 @@
# 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,
# 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.
@@ -25,8 +25,10 @@ Defines an interface which all Auth handlers need to implement.
from boto.plugin import Plugin
class NotReadyToAuthenticate(Exception):
pass
pass
class AuthHandler(Plugin):
@@ -37,10 +39,10 @@ class AuthHandler(Plugin):
:type host: string
: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.
:type provider: boto.provider.Provider
:type provider: boto.provider.Provider
:param provider: Provider details.
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._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):
"""
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.
session = requests.Session()
session.proxies = self.proxy
adapter = requests.adapters.HTTPAdapter(
pool_connections=20,
pool_maxsize=50,

View File

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

View File

@@ -131,7 +131,7 @@ class Query(object):
params['highlight.%s' % k] = v
if self.options:
params['options'] = self.options
params['q.options'] = self.options
if self.return_fields:
params['return'] = ','.join(self.return_fields)
@@ -152,6 +152,10 @@ class SearchConnection(object):
self.endpoint = endpoint
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:
self.endpoint = domain.search_service_endpoint

View File

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

View File

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

View File

@@ -14,9 +14,8 @@
# 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,
# 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

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

View File

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

View File

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

View File

@@ -65,7 +65,6 @@ class DirectConnectConnection(AWSQueryConnection):
"DirectConnectServerException": exceptions.DirectConnectServerException,
}
def __init__(self, **kwargs):
region = kwargs.pop('region', None)
if not region:
@@ -626,4 +625,3 @@ class DirectConnectConnection(AWSQueryConnection):
exception_class = self._faults.get(fault_name, self.ResponseError)
raise exception_class(response.status, response.reason,
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)
Returns an ``Item`` instance containing all the data for that record.
Raises an ``ItemNotFound`` exception if the item is not found.
Example::

View File

@@ -22,6 +22,7 @@
from boto.ec2.ec2object import EC2Object
class Address(EC2Object):
"""
Represents an EC2 Elastic IP Address
@@ -78,33 +79,36 @@ class Address(EC2Object):
"""
if self.allocation_id:
return self.connection.release_address(
None,
self.allocation_id,
allocation_id=self.allocation_id,
dry_run=dry_run)
else:
return self.connection.release_address(
self.public_ip,
public_ip=self.public_ip,
dry_run=dry_run
)
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.
:see: :meth:`boto.ec2.connection.EC2Connection.associate_address`
"""
if self.allocation_id:
return self.connection.associate_address(
instance_id,
self.public_ip,
instance_id=instance_id,
public_ip=self.public_ip,
allocation_id=self.allocation_id,
network_interface_id=network_interface_id,
private_ip_address=private_ip_address,
allow_reassociation=allow_reassociation,
dry_run=dry_run
)
return self.connection.associate_address(
instance_id,
self.public_ip,
instance_id=instance_id,
public_ip=self.public_ip,
network_interface_id=network_interface_id,
private_ip_address=private_ip_address,
allow_reassociation=allow_reassociation,
dry_run=dry_run
)
@@ -116,14 +120,11 @@ class Address(EC2Object):
"""
if self.association_id:
return self.connection.disassociate_address(
None,
self.association_id,
association_id=self.association_id,
dry_run=dry_run
)
else:
return self.connection.disassociate_address(
self.public_ip,
public_ip=self.public_ip,
dry_run=dry_run
)

View File

@@ -105,14 +105,14 @@ class AutoScaleConnection(AWSQueryConnection):
self.region = region
self.use_block_device_types = use_block_device_types
super(AutoScaleConnection, self).__init__(aws_access_key_id,
aws_secret_access_key,
is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass,
self.region.endpoint, debug,
https_connection_factory, path=path,
security_token=security_token,
validate_certs=validate_certs,
profile_name=profile_name)
aws_secret_access_key,
is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass,
self.region.endpoint, debug,
https_connection_factory, path=path,
security_token=security_token,
validate_certs=validate_certs,
profile_name=profile_name)
def _required_auth_capability(self):
return ['hmac-v4']
@@ -222,7 +222,10 @@ class AutoScaleConnection(AWSQueryConnection):
if launch_config.key_name:
params['KeyName'] = launch_config.key_name
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:
params['KernelId'] = launch_config.kernel_id
if launch_config.ramdisk_id:

View File

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

View File

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

View File

@@ -20,7 +20,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
from datetime import datetime
from boto.ec2.elb.listelement import ListElement
# Namespacing issue with deprecated local class
from boto.ec2.blockdevicemapping import BlockDeviceMapping as BDM
@@ -129,8 +128,8 @@ class LaunchConfiguration(object):
:type instance_type: str
:param instance_type: The instance type
:type kern_id: str
:param kern_id: Kernel id for instance
:type kernel_id: str
:param kernel_id: Kernel id for instance
:type ramdisk_id: str
: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
# IN THE SOFTWARE.
class AccountLimits(object):
def __init__(self, connection=None):
self.connection = connection
@@ -27,7 +28,7 @@ class AccountLimits(object):
def __repr__(self):
return 'AccountLimits: [%s, %s]' % (self.max_autoscaling_groups,
self.max_launch_configurations)
self.max_launch_configurations)
def startElement(self, name, attrs, connection):
return None
@@ -41,4 +42,3 @@ class AccountLimits(object):
self.max_launch_configurations = int(value)
else:
setattr(self, name, value)

View File

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

View File

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

View File

@@ -38,7 +38,8 @@ class BlockDeviceType(object):
delete_on_termination=False,
size=None,
volume_type=None,
iops=None):
iops=None,
encrypted=None):
self.connection = connection
self.ephemeral_name = ephemeral_name
self.no_device = no_device
@@ -50,6 +51,7 @@ class BlockDeviceType(object):
self.size = size
self.volume_type = volume_type
self.iops = iops
self.encrypted = encrypted
def startElement(self, name, attrs, connection):
pass
@@ -76,6 +78,8 @@ class BlockDeviceType(object):
self.volume_type = value
elif lname == 'iops':
self.iops = int(value)
elif lname == 'encrypted':
self.encrypted = (value == 'true')
else:
setattr(self, name, value)
@@ -150,4 +154,12 @@ class BlockDeviceMapping(dict):
params['%s.Ebs.VolumeType' % pre] = block_dev.volume_type
if block_dev.iops is not None:
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

View File

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

View File

@@ -14,19 +14,21 @@
# 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,
# 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.ec2
from boto.sdb.db.property import StringProperty, IntegerProperty
from boto.manage import propget
from boto.compat import six
InstanceTypes = ['m1.small', 'm1.large', 'm1.xlarge',
'c1.medium', 'c1.xlarge', 'm2.xlarge',
'm2.2xlarge', 'm2.4xlarge', 'cc1.4xlarge',
't1.micro']
class BuyReservation(object):
def get_region(self, params):
@@ -51,7 +53,7 @@ class BuyReservation(object):
prop = StringProperty(name='zone', verbose_name='EC2 Availability Zone',
choices=self.ec2.get_all_zones)
params['zone'] = propget.get(prop)
def get(self, params):
self.get_region(params)
self.ec2 = params['region'].connect()
@@ -76,7 +78,7 @@ if __name__ == "__main__":
unit_price = float(offering.fixed_price)
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))
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':
offering.purchase(params['quantity'])
else:

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,11 +14,12 @@
# 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,
# 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.
class ListElement(list):
def startElement(self, name, attrs, connection):

View File

@@ -93,14 +93,14 @@ class EC2Connection(AWSQueryConnection):
self.DefaultRegionEndpoint)
self.region = region
super(EC2Connection, self).__init__(aws_access_key_id,
aws_secret_access_key,
is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass,
self.region.endpoint, debug,
https_connection_factory, path,
security_token,
validate_certs=validate_certs,
profile_name=profile_name)
aws_secret_access_key,
is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass,
self.region.endpoint, debug,
https_connection_factory, path,
security_token,
validate_certs=validate_certs,
profile_name=profile_name)
if api_version:
self.APIVersion = api_version
@@ -270,7 +270,7 @@ class EC2Connection(AWSQueryConnection):
root_device_name=None, block_device_map=None,
dry_run=False, virtualization_type=None,
sriov_net_support=None,
snapshot_id=None,
snapshot_id=None,
delete_root_volume_on_termination=False):
"""
Register an image.
@@ -323,7 +323,7 @@ class EC2Connection(AWSQueryConnection):
:type delete_root_volume_on_termination: bool
:param delete_root_volume_on_termination: Whether to delete the root
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.
:rtype: string
@@ -345,8 +345,8 @@ class EC2Connection(AWSQueryConnection):
if root_device_name:
params['RootDeviceName'] = root_device_name
if snapshot_id:
root_vol = BlockDeviceType(snapshot_id=snapshot_id,
delete_on_termination=delete_root_volume_on_termination)
root_vol = BlockDeviceType(snapshot_id=snapshot_id,
delete_on_termination=delete_root_volume_on_termination)
block_device_map = BlockDeviceMapping()
block_device_map[root_device_name] = root_vol
if block_device_map:
@@ -358,7 +358,6 @@ class EC2Connection(AWSQueryConnection):
if sriov_net_support:
params['SriovNetSupport'] = sriov_net_support
rs = self.get_object('RegisterImage', params, ResultSet, verb='POST')
image_id = getattr(rs, 'imageId', None)
return image_id
@@ -1881,9 +1880,9 @@ class EC2Connection(AWSQueryConnection):
return self.get_status('AssignPrivateIpAddresses', params, verb='POST')
def _associate_address(self, status, instance_id=None, public_ip=None,
allocation_id=None, network_interface_id=None,
private_ip_address=None, allow_reassociation=False,
dry_run=False):
allocation_id=None, network_interface_id=None,
private_ip_address=None, allow_reassociation=False,
dry_run=False):
params = {}
if instance_id is not None:
params['InstanceId'] = instance_id
@@ -1960,9 +1959,9 @@ class EC2Connection(AWSQueryConnection):
allow_reassociation=allow_reassociation, dry_run=dry_run)
def associate_address_object(self, instance_id=None, public_ip=None,
allocation_id=None, network_interface_id=None,
private_ip_address=None, allow_reassociation=False,
dry_run=False):
allocation_id=None, network_interface_id=None,
private_ip_address=None, allow_reassociation=False,
dry_run=False):
"""
Associate an Elastic IP address with a currently running instance.
This requires one of ``public_ip`` or ``allocation_id`` depending
@@ -2589,7 +2588,7 @@ class EC2Connection(AWSQueryConnection):
now = datetime.utcnow()
last_hour = datetime(now.year, now.month, now.day, now.hour)
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)
target_backup_times = []
@@ -2598,15 +2597,15 @@ class EC2Connection(AWSQueryConnection):
oldest_snapshot_date = datetime(2007, 1, 1)
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):
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):
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
while (start_of_month > oldest_snapshot_date and
(monthly_backups is True or
@@ -3080,7 +3079,7 @@ class EC2Connection(AWSQueryConnection):
:rtype: bool
:return: True if successful.
"""
params = {'GroupName':group_name}
params = {'GroupName': group_name}
if src_security_group_name:
params['SourceSecurityGroupName'] = src_security_group_name
if src_security_group_owner_id:
@@ -3184,7 +3183,7 @@ class EC2Connection(AWSQueryConnection):
if not isinstance(cidr_ip, list):
cidr_ip = [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
if dry_run:
params['DryRun'] = 'true'
@@ -3278,7 +3277,7 @@ class EC2Connection(AWSQueryConnection):
:rtype: bool
:return: True if successful.
"""
params = {'GroupName':group_name}
params = {'GroupName': group_name}
if src_security_group_name:
params['SourceSecurityGroupName'] = src_security_group_name
if src_security_group_owner_id:
@@ -3476,7 +3475,7 @@ class EC2Connection(AWSQueryConnection):
if dry_run:
params['DryRun'] = 'true'
regions = self.get_list('DescribeRegions', params,
[('item', RegionInfo)], verb='POST')
[('item', RegionInfo)], verb='POST')
for region in regions:
region.connection_cls = EC2Connection
return regions
@@ -4110,7 +4109,7 @@ class EC2Connection(AWSQueryConnection):
:rtype: bool
:return: True if successful
"""
params = {'GroupName':name, 'Strategy':strategy}
params = {'GroupName': name, 'Strategy': strategy}
if dry_run:
params['DryRun'] = 'true'
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.
"""
params = {'GroupName':name}
params = {'GroupName': name}
if dry_run:
params['DryRun'] = 'true'
return self.get_status('DeletePlacementGroup', params, verb='POST')
@@ -4139,9 +4138,9 @@ class EC2Connection(AWSQueryConnection):
i = 1
for key in keys:
value = tags[key]
params['Tag.%d.Key'%i] = key
params['Tag.%d.Key' % i] = key
if value is not None:
params['Tag.%d.Value'%i] = value
params['Tag.%d.Value' % i] = value
i += 1
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
:param network_interface_ids: a list of strings representing ENI IDs
:type filters: dict
:param filters: Optional filters that can be used to limit
the results returned. Filters are provided
@@ -4409,7 +4408,7 @@ class EC2Connection(AWSQueryConnection):
if dry_run:
params['DryRun'] = 'true'
return self.get_object('CopyImage', params, CopyImage,
verb='POST')
verb='POST')
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
class EC2Object(object):
def __init__(self, connection=None):
@@ -64,7 +65,7 @@ class TaggedEC2Object(EC2Object):
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 the EC2 service.
@@ -76,14 +77,7 @@ class TaggedEC2Object(EC2Object):
If you want only the tag name and no value, the
value should be the empty string.
"""
status = self.connection.create_tags(
[self.id],
{key : value},
dry_run=dry_run
)
if self.tags is None:
self.tags = TagSet()
self.tags[key] = value
self.add_tags({key: value}, dry_run)
def add_tags(self, tags, dry_run=False):
"""
@@ -116,21 +110,35 @@ class TaggedEC2Object(EC2Object):
:type value: str
:param value: An optional value that can be stored with the tag.
If a value is provided, it must match the value
currently stored in EC2. If not, the tag will not
be removed. If a value of None is provided, all
tags with the specified name will be deleted.
NOTE: There is an important distinction between
a value of '' and a value of None.
If a value is provided, it must match the value currently
stored in EC2. If not, the tag will not be removed. 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.
"""
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(
[self.id],
tags,
dry_run=dry_run
)
if key in self.tags:
del self.tags[key]
for key, value in tags.items():
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.
* crossZoneLoadBalancing - Boolean (true)
* connectingSettings - :py:class:`ConnectionSettingAttribute` instance
* accessLog - :py:class:`AccessLogAttribute` instance
* connectionDraining - :py:class:`ConnectionDrainingAttribute` instance
@@ -436,6 +437,9 @@ class ELBConnection(AWSQueryConnection):
value.enabled and 'true' or 'false'
params['LoadBalancerAttributes.ConnectionDraining.Timeout'] = \
value.timeout
elif attribute.lower() == 'connectingsettings':
params['LoadBalancerAttributes.ConnectionSettings.IdleTimeout'] = \
value.idle_timeout
else:
raise ValueError('InvalidAttribute', attribute)
return self.get_status('ModifyLoadBalancerAttributes', params,
@@ -468,6 +472,7 @@ class ELBConnection(AWSQueryConnection):
* accessLog - :py:class:`AccessLogAttribute` instance
* crossZoneLoadBalancing - Boolean
* connectingSettings - :py:class:`ConnectionSettingAttribute` instance
* connectionDraining - :py:class:`ConnectionDrainingAttribute`
instance
@@ -481,6 +486,8 @@ class ELBConnection(AWSQueryConnection):
return attributes.cross_zone_load_balancing.enabled
if attribute.lower() == 'connectiondraining':
return attributes.connection_draining
if attribute.lower() == 'connectingsettings':
return attributes.connecting_settings
return None
def register_instances(self, load_balancer_name, instances):

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -14,16 +14,17 @@
# 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,
# 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.
class InstanceInfo(object):
"""
Represents an EC2 Instance status response from CloudWatch
"""
def __init__(self, connection=None, id=None, state=None):
"""
:ivar str id: The instance's EC2 ID.
@@ -46,6 +47,3 @@ class InstanceInfo(object):
self.state = value
else:
setattr(self, name, value)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -185,6 +185,3 @@ class SnapshotAttribute(object):
self.snapshot_id = value
else:
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.spotinstancerequest import SpotInstanceStateFault
class SpotDatafeedSubscription(EC2Object):
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)
self.owner_id = owner_id
self.bucket = bucket
@@ -62,4 +63,3 @@ class SpotDatafeedSubscription(EC2Object):
return self.connection.delete_spot_datafeed_subscription(
dry_run=dry_run
)

View File

@@ -25,6 +25,7 @@ Represents an EC2 Spot Instance Request
from boto.ec2.ec2object import EC2Object
class SpotPriceHistory(EC2Object):
def __init__(self, connection=None):
@@ -51,5 +52,3 @@ class SpotPriceHistory(EC2Object):
self.availability_zone = value
else:
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
class MessageSet(list):
"""
A list object that contains messages associated with
@@ -39,6 +40,7 @@ class MessageSet(list):
else:
setattr(self, name, value)
class Zone(EC2Object):
"""
Represents an Availability Zone.
@@ -74,7 +76,3 @@ class Zone(EC2Object):
self.region_name = value
else:
setattr(self, name, value)

View File

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

View File

@@ -922,7 +922,7 @@ class ElasticTranscoderConnection(AWSAuthConnection):
if headers is None:
headers = {}
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'))
if response.status == expected_status:
return body

View File

@@ -9,7 +9,8 @@
"us-east-1": "autoscaling.us-east-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-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": {
"ap-northeast-1": "cloudformation.ap-northeast-1.amazonaws.com",
@@ -19,8 +20,10 @@
"eu-west-1": "cloudformation.eu-west-1.amazonaws.com",
"sa-east-1": "cloudformation.sa-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-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": {
"ap-northeast-1": "cloudfront.amazonaws.com",
@@ -30,18 +33,30 @@
"sa-east-1": "cloudfront.amazonaws.com",
"us-east-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": {
"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",
"us-east-1": "cloudsearch.us-east-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": {
"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-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": {
"ap-northeast-1": "monitoring.ap-northeast-1.amazonaws.com",
@@ -53,14 +68,22 @@
"us-east-1": "monitoring.us-east-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-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": {
"us-east-1": "datapipeline.us-east-1.amazonaws.com",
"us-west-2": "datapipeline.us-west-2.amazonaws.com",
"eu-west-1": "datapipeline.eu-west-1.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": {
"ap-northeast-1": "directconnect.ap-northeast-1.amazonaws.com",
@@ -70,7 +93,8 @@
"sa-east-1": "directconnect.sa-east-1.amazonaws.com",
"us-east-1": "directconnect.us-east-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": {
"ap-northeast-1": "dynamodb.ap-northeast-1.amazonaws.com",
@@ -82,7 +106,8 @@
"us-east-1": "dynamodb.us-east-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-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": {
"ap-northeast-1": "ec2.ap-northeast-1.amazonaws.com",
@@ -94,7 +119,8 @@
"us-east-1": "ec2.us-east-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-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": {
"ap-northeast-1": "elasticache.ap-northeast-1.amazonaws.com",
@@ -105,7 +131,8 @@
"sa-east-1": "elasticache.sa-east-1.amazonaws.com",
"us-east-1": "elasticache.us-east-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": {
"ap-northeast-1": "elasticbeanstalk.ap-northeast-1.amazonaws.com",
@@ -115,7 +142,8 @@
"sa-east-1": "elasticbeanstalk.sa-east-1.amazonaws.com",
"us-east-1": "elasticbeanstalk.us-east-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": {
"ap-northeast-1": "elasticloadbalancing.ap-northeast-1.amazonaws.com",
@@ -127,7 +155,8 @@
"us-east-1": "elasticloadbalancing.us-east-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-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": {
"ap-northeast-1": "ap-northeast-1.elasticmapreduce.amazonaws.com",
@@ -139,7 +168,8 @@
"us-east-1": "elasticmapreduce.us-east-1.amazonaws.com",
"us-gov-west-1": "us-gov-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": {
"ap-northeast-1": "elastictranscoder.ap-northeast-1.amazonaws.com",
@@ -147,7 +177,8 @@
"eu-west-1": "elastictranscoder.eu-west-1.amazonaws.com",
"us-east-1": "elastictranscoder.us-east-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": {
"ap-northeast-1": "glacier.ap-northeast-1.amazonaws.com",
@@ -156,7 +187,8 @@
"eu-west-1": "glacier.eu-west-1.amazonaws.com",
"us-east-1": "glacier.us-east-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": {
"ap-northeast-1": "iam.amazonaws.com",
@@ -181,10 +213,23 @@
"us-west-2": "importexport.amazonaws.com"
},
"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": {
"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": {
"ap-northeast-1": "rds.ap-northeast-1.amazonaws.com",
@@ -196,7 +241,8 @@
"us-east-1": "rds.amazonaws.com",
"us-gov-west-1": "rds.us-gov-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": {
"ap-northeast-1": "redshift.ap-northeast-1.amazonaws.com",
@@ -204,7 +250,8 @@
"ap-southeast-2": "redshift.ap-southeast-2.amazonaws.com",
"eu-west-1": "redshift.eu-west-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": {
"ap-northeast-1": "route53.amazonaws.com",
@@ -216,6 +263,9 @@
"us-west-1": "route53.amazonaws.com",
"us-west-2": "route53.amazonaws.com"
},
"route53domains": {
"us-east-1": "route53domains.us-east-1.amazonaws.com"
},
"s3": {
"ap-northeast-1": "s3-ap-northeast-1.amazonaws.com",
"ap-southeast-1": "s3-ap-southeast-1.amazonaws.com",
@@ -226,7 +276,8 @@
"us-east-1": "s3.amazonaws.com",
"us-gov-west-1": "s3-us-gov-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": {
"ap-northeast-1": "sdb.ap-northeast-1.amazonaws.com",
@@ -236,12 +287,14 @@
"sa-east-1": "sdb.sa-east-1.amazonaws.com",
"us-east-1": "sdb.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": {
"eu-west-1": "email.eu-west-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": {
"ap-northeast-1": "sns.ap-northeast-1.amazonaws.com",
@@ -253,7 +306,8 @@
"us-east-1": "sns.us-east-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-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": {
"ap-northeast-1": "ap-northeast-1.queue.amazonaws.com",
@@ -265,7 +319,8 @@
"us-east-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-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": {
"ap-northeast-1": "storagegateway.ap-northeast-1.amazonaws.com",
@@ -275,7 +330,8 @@
"sa-east-1": "storagegateway.sa-east-1.amazonaws.com",
"us-east-1": "storagegateway.us-east-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": {
"ap-northeast-1": "sts.amazonaws.com",
@@ -290,7 +346,8 @@
"us-west-2": "sts.amazonaws.com"
},
"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": {
"ap-northeast-1": "swf.ap-northeast-1.amazonaws.com",
@@ -302,6 +359,7 @@
"us-east-1": "swf.us-east-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-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
from boto import handler
from boto.compat import json, six, StandardError
from boto.compat import json, StandardError
from boto.resultset import ResultSet
class BotoClientError(StandardError):
"""
General Boto Client error (error accessing AWS)
@@ -112,7 +113,7 @@ class BotoServerError(StandardError):
try:
h = handler.XmlHandlerWrapper(self, self)
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.
try:
parsed = json.loads(self.body)
@@ -209,6 +210,7 @@ class StorageCreateError(BotoServerError):
else:
return super(StorageCreateError, self).endElement(name, value, connection)
class S3CreateError(StorageCreateError):
"""
Error creating a bucket or key on S3.
@@ -294,15 +296,15 @@ class StorageResponseError(BotoServerError):
super(StorageResponseError, self).__init__(status, reason, body)
def startElement(self, name, attrs, connection):
return super(StorageResponseError, self).startElement(name, attrs,
connection)
return super(StorageResponseError, self).startElement(
name, attrs, connection)
def endElement(self, name, value, connection):
if name == 'Resource':
self.resource = value
else:
return super(StorageResponseError, self).endElement(name, value,
connection)
return super(StorageResponseError, self).endElement(
name, value, connection)
def _cleanupParsedProperties(self):
super(StorageResponseError, self)._cleanupParsedProperties()
@@ -332,8 +334,8 @@ class EC2ResponseError(BotoServerError):
self.errors = None
self._errorResultSet = []
super(EC2ResponseError, self).__init__(status, reason, body)
self.errors = [ (e.error_code, e.error_message) \
for e in self._errorResultSet ]
self.errors = [
(e.error_code, e.error_message) for e in self._errorResultSet]
if len(self.errors):
self.error_code, self.error_message = self.errors[0]
@@ -348,7 +350,7 @@ class EC2ResponseError(BotoServerError):
if name == 'RequestID':
self.request_id = value
else:
return None # don't call subclass here
return None # don't call subclass here
def _cleanupParsedProperties(self):
super(EC2ResponseError, self)._cleanupParsedProperties()
@@ -420,30 +422,35 @@ class SDBResponseError(BotoServerError):
"""
pass
class AWSConnectionError(BotoClientError):
"""
General error connecting to Amazon Web Services.
"""
pass
class StorageDataError(BotoClientError):
"""
Error receiving data from a storage service.
"""
pass
class S3DataError(StorageDataError):
"""
Error receiving data from S3.
"""
pass
class GSDataError(StorageDataError):
"""
Error receiving data from GS.
"""
pass
class InvalidUriError(Exception):
"""Exception raised when URI is invalid."""
@@ -451,6 +458,7 @@ class InvalidUriError(Exception):
super(InvalidUriError, self).__init__(message)
self.message = message
class InvalidAclError(Exception):
"""Exception raised when ACL XML is invalid."""
@@ -458,6 +466,7 @@ class InvalidAclError(Exception):
super(InvalidAclError, self).__init__(message)
self.message = message
class InvalidCorsError(Exception):
"""Exception raised when CORS XML is invalid."""
@@ -465,10 +474,12 @@ class InvalidCorsError(Exception):
super(InvalidCorsError, self).__init__(message)
self.message = message
class NoAuthHandlerFound(Exception):
"""Is raised when no auth handlers were found ready to authenticate."""
pass
class InvalidLifecycleConfigError(Exception):
"""Exception raised when GCS lifecycle configuration XML is invalid."""
@@ -476,6 +487,7 @@ class InvalidLifecycleConfigError(Exception):
super(InvalidLifecycleConfigError, self).__init__(message)
self.message = message
# Enum class for resumable upload failure disposition.
class ResumableTransferDisposition(object):
# START_OVER means an attempt to resume an existing transfer failed,
@@ -500,6 +512,7 @@ class ResumableTransferDisposition(object):
# upload ID.
ABORT = 'ABORT'
class ResumableUploadException(Exception):
"""
Exception raised for various resumable upload problems.
@@ -516,6 +529,7 @@ class ResumableUploadException(Exception):
return 'ResumableUploadException("%s", %s)' % (
self.message, self.disposition)
class ResumableDownloadException(Exception):
"""
Exception raised for various resumable download problems.
@@ -532,6 +546,7 @@ class ResumableDownloadException(Exception):
return 'ResumableDownloadException("%s", %s)' % (
self.message, self.disposition)
class TooManyRecordsException(Exception):
"""
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
# 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,
# 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.
@@ -23,6 +23,7 @@ import xml.sax
from boto.compat import StringIO
class XmlHandler(xml.sax.ContentHandler):
def __init__(self, root_node, connection):

View File

@@ -27,111 +27,112 @@ import boto
from boto.compat import six, http_client
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):
"""Constructor.
def __init__(self, host, cert, reason):
"""Constructor.
Args:
host: The hostname the connection was made to.
cert: The SSL certificate (as a dictionary) the host returned.
"""
http_client.HTTPException.__init__(self)
self.host = host
self.cert = cert
self.reason = reason
Args:
host: The hostname the connection was made to.
cert: The SSL certificate (as a dictionary) the host returned.
"""
http_client.HTTPException.__init__(self)
self.host = host
self.cert = cert
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):
"""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):
"""Validates that a given hostname is valid for an SSL certificate.
"""Validates that a given hostname is valid for an SSL certificate.
Args:
cert: A dictionary representing an SSL certificate.
hostname: The hostname to test.
Returns:
bool: Whether or not the hostname is valid for this certificate.
"""
hosts = GetValidHostsForCert(cert)
boto.log.debug(
"validating server certificate: hostname=%s, certificate hosts=%s",
hostname, hosts)
for host in hosts:
host_re = host.replace('.', '\.').replace('*', '[^.]*')
if re.search('^%s$' % (host_re,), hostname, re.I):
return True
return False
Args:
cert: A dictionary representing an SSL certificate.
hostname: The hostname to test.
Returns:
bool: Whether or not the hostname is valid for this certificate.
"""
hosts = GetValidHostsForCert(cert)
boto.log.debug(
"validating server certificate: hostname=%s, certificate hosts=%s",
hostname, hosts)
for host in hosts:
host_re = host.replace('.', '\.').replace('*', '[^.]*')
if re.search('^%s$' % (host_re,), hostname, re.I):
return True
return False
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,
ca_certs=None, strict=None, **kwargs):
"""Constructor.
def __init__(self, host, port=default_port, key_file=None, cert_file=None,
ca_certs=None, strict=None, **kwargs):
"""Constructor.
Args:
host: The hostname. Can be in 'host:port' form.
port: The port. Defaults to 443.
key_file: A file containing the client's private key
cert_file: A file containing the client's certificates
ca_certs: A file contianing a set of concatenated certificate authority
certs for validating the server against.
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.
"""
if six.PY2:
# Python 3.2 and newer have deprecated and removed the strict
# parameter. Since the params are supported as keyword arguments
# we conditionally add it here.
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)
Args:
host: The hostname. Can be in 'host:port' form.
port: The port. Defaults to 443.
key_file: A file containing the client's private key
cert_file: A file containing the client's certificates
ca_certs: A file contianing a set of concatenated certificate authority
certs for validating the server against.
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.
"""
if six.PY2:
# Python 3.2 and newer have deprecated and removed the strict
# parameter. Since the params are supported as keyword arguments
# we conditionally add it here.
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)

View File

@@ -64,13 +64,13 @@ class IAMConnection(AWSQueryConnection):
debug=0, https_connection_factory=None, path='/',
security_token=None, validate_certs=True, profile_name=None):
super(IAMConnection, self).__init__(aws_access_key_id,
aws_secret_access_key,
is_secure, port, proxy,
proxy_port, proxy_user, proxy_pass,
host, debug, https_connection_factory,
path, security_token,
validate_certs=validate_certs,
profile_name=profile_name)
aws_secret_access_key,
is_secure, port, proxy,
proxy_port, proxy_user, proxy_pass,
host, debug, https_connection_factory,
path, security_token,
validate_certs=validate_certs,
profile_name=profile_name)
def _required_auth_capability(self):
return ['hmac-v4']
@@ -700,7 +700,7 @@ class IAMConnection(AWSQueryConnection):
#
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.
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
information about.
"""
return self.get_response('GetInstanceProfile', {'InstanceProfileName':
instance_profile_name})
return self.get_response('GetInstanceProfile',
{'InstanceProfileName': instance_profile_name})
def get_role(self, role_name):
"""
@@ -1453,7 +1453,7 @@ class IAMConnection(AWSQueryConnection):
provider to get information about.
"""
params = {'SAMLProviderArn': saml_provider_arn }
params = {'SAMLProviderArn': saml_provider_arn}
return self.get_response('GetSAMLProvider', params)
def update_saml_provider(self, saml_provider_arn, saml_metadata_document):
@@ -1496,5 +1496,51 @@ class IAMConnection(AWSQueryConnection):
provider to delete.
"""
params = {'SAMLProviderArn': saml_provider_arn }
params = {'SAMLProviderArn': saml_provider_arn}
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
# IN THE SOFTWARE.
class SummaryMap(dict):
def __init__(self, parent=None):
@@ -39,4 +40,3 @@ class SummaryMap(dict):
self[self._name] = value
else:
setattr(self, name, value)

View File

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

View File

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

View File

@@ -440,6 +440,21 @@ class MTurkConnection(AWSQueryConnection):
params['RequesterFeedback'] = feedback
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):
"""
"""
@@ -915,6 +930,14 @@ class HIT(BaseAutoResultElement):
expired = property(_has_expired)
class FileUploadURL(BaseAutoResultElement):
"""
Class to extract an FileUploadURL structure from a response
"""
pass
class HITTypeId(BaseAutoResultElement):
"""
Class to extract an HITTypeId structure from a response

View File

@@ -309,7 +309,7 @@ class MWSConnection(AWSQueryConnection):
try:
response = self._mexe(request, override_num_retries=None)
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()
boto.log.debug(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
# copy of this software and associated documentation files (the
@@ -20,13 +20,12 @@
# 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.opsworks import exceptions
from boto.compat import json
class OpsWorksConnection(AWSQueryConnection):
@@ -70,9 +69,8 @@ class OpsWorksConnection(AWSQueryConnection):
When you call CreateStack, CloneStack, or UpdateStack we recommend
you use the `ConfigurationManager` parameter to specify the Chef
version, 0.9 or 11.4. The default value is currently 0.9. However,
we expect to change the default value to 11.4 in October 2013. For
more information, see `Using AWS OpsWorks with Chef 11`_.
version, 0.9, 11.4, or 11.10. The default value is currently 11.4.
For more information, see `Chef Versions`_.
"""
APIVersion = "2013-02-18"
DefaultRegionName = "us-east-1"
@@ -92,7 +90,10 @@ class OpsWorksConnection(AWSQueryConnection):
if not region:
region = RegionInfo(self, self.DefaultRegionName,
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)
self.region = region
@@ -155,12 +156,8 @@ class OpsWorksConnection(AWSQueryConnection):
layer_id):
"""
Attaches an Elastic Load Balancing load balancer to a
specified layer.
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`_.
specified layer. For more information, see `Elastic Load
Balancing`_.
**Required Permissions**: To use this action, an IAM user must
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,
hostname_theme=None, default_availability_zone=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,
clone_permissions=None, clone_app_ids=None,
default_root_device_type=None):
@@ -247,19 +246,14 @@ class OpsWorksConnection(AWSQueryConnection):
pairs to be added to the cloned stack.
:type service_role_arn: string
:param service_role_arn:
The stack AWS Identity and Access Management (IAM) role, which allows
AWS OpsWorks to work with AWS resources on your behalf. You must
set this parameter to the Amazon Resource Name (ARN) for an
existing IAM role. If you create a stack by using the AWS OpsWorks
console, it creates the role for you. You can obtain an existing
stack's IAM ARN programmatically by calling DescribePermissions.
For more information about IAM ARNs, see `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.
:param service_role_arn: The stack AWS Identity and Access Management
(IAM) role, which allows AWS OpsWorks to work with AWS resources on
your behalf. You must set this parameter to the Amazon Resource
Name (ARN) for an existing IAM role. If you create a stack by using
the AWS OpsWorks console, it creates the role for you. You can
obtain an existing stack's IAM ARN programmatically by calling
DescribePermissions. For more information about IAM ARNs, see
`Using Identifiers`_.
:type default_instance_profile_arn: string
: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.
It is used to override the corresponding default stack
configuration JSON values. The string should be in the following
format and must escape characters such as '"'.: `"{\"key1\":
\"value1\", \"key2\": \"value2\",...}"`
format and must escape characters such as '"'.:
`"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
For more information on custom JSON, see `Use Custom JSON to Modify the
Stack Configuration JSON`_
:type configuration_manager: dict
:param configuration_manager: The configuration manager. When you clone
a stack we recommend that you use the configuration manager to
specify the Chef version, 0.9 or 11.4. The default value is
currently 0.9. However, we expect to change the default value to
11.4 in September 2013.
specify the Chef version, 0.9, 11.4, or 11.10. The default value is
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
: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
:param custom_cookbooks_source: Contains the information required to
retrieve an app or cookbook from a repository. For more
@@ -379,8 +402,12 @@ class OpsWorksConnection(AWSQueryConnection):
params['CustomJson'] = custom_json
if configuration_manager is not None:
params['ConfigurationManager'] = configuration_manager
if chef_configuration is not None:
params['ChefConfiguration'] = chef_configuration
if use_custom_cookbooks is not None:
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:
params['CustomCookbooksSource'] = custom_cookbooks_source
if default_ssh_key_name is not None:
@@ -395,8 +422,9 @@ class OpsWorksConnection(AWSQueryConnection):
body=json.dumps(params))
def create_app(self, stack_id, name, type, shortname=None,
description=None, app_source=None, domains=None,
enable_ssl=None, ssl_configuration=None, attributes=None):
description=None, data_sources=None, app_source=None,
domains=None, enable_ssl=None, ssl_configuration=None,
attributes=None):
"""
Creates an app for a specified stack. For more information,
see `Creating Apps`_.
@@ -419,6 +447,9 @@ class OpsWorksConnection(AWSQueryConnection):
:type description: string
:param description: A description of the app.
:type data_sources: list
:param data_sources: The app's data source.
:type type: string
:param type: The app type. Each supported type is associated with a
particular layer. For example, PHP applications are associated with
@@ -441,7 +472,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map
: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, }
@@ -449,6 +480,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['Shortname'] = shortname
if description is not None:
params['Description'] = description
if data_sources is not None:
params['DataSources'] = data_sources
if app_source is not None:
params['AppSource'] = app_source
if domains is not None:
@@ -505,8 +538,9 @@ class OpsWorksConnection(AWSQueryConnection):
:param custom_json: A string that contains user-defined, custom JSON.
It is used to override the corresponding default stack
configuration JSON values. The string should be in the following
format and must escape characters such as '"'.: `"{\"key1\":
\"value1\", \"key2\": \"value2\",...}"`
format and must escape characters such as '"'.:
`"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
For more information on custom JSON, see `Use Custom JSON to Modify the
Stack Configuration JSON`_.
@@ -526,9 +560,10 @@ class OpsWorksConnection(AWSQueryConnection):
def create_instance(self, stack_id, layer_ids, instance_type,
auto_scaling_type=None, hostname=None, os=None,
ami_id=None, ssh_key_name=None,
availability_zone=None, subnet_id=None,
architecture=None, root_device_type=None,
install_updates_on_boot=None):
availability_zone=None, virtualization_type=None,
subnet_id=None, architecture=None,
root_device_type=None, install_updates_on_boot=None,
ebs_optimized=None):
"""
Creates an instance in a specified stack. For more
information, see `Adding an Instance to a Layer`_.
@@ -598,6 +633,10 @@ class OpsWorksConnection(AWSQueryConnection):
:param availability_zone: The instance Availability Zone. For more
information, see `Regions and Endpoints`_.
:type virtualization_type: string
:param virtualization_type: The instance's virtualization type,
`paravirtual` or `hvm`.
:type subnet_id: string
: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
@@ -605,26 +644,28 @@ class OpsWorksConnection(AWSQueryConnection):
the instance in a different subnet.
:type architecture: string
:param architecture: The instance architecture. Instance types do not
necessarily support both architectures. For a list of the
architectures that are supported by the different instance types,
see `Instance Families and Types`_.
:param architecture: The instance architecture. The default option is
`x86_64`. Instance types do not necessarily support both
architectures. For a list of the architectures that are supported
by the different instance types, see `Instance Families and
Types`_.
:type root_device_type: string
:param root_device_type: The instance root device type. For more
information, see `Storage for the Root Device`_.
:type install_updates_on_boot: boolean
:param install_updates_on_boot:
Whether to install operating system and package updates when the
instance boots. The default value is `True`. To control when
updates are installed, set this value to `False`. You must then
update your instances manually by using CreateDeployment to run the
`update_dependencies` stack command or manually running `yum`
(Amazon Linux) or `apt-get` (Ubuntu) on the instances.
:param install_updates_on_boot: Whether to install operating system and
package updates when the instance boots. The default value is
`True`. To control when updates are installed, set this value to
`False`. You must then update your instances manually by using
CreateDeployment to run the `update_dependencies` stack command or
manually running `yum` (Amazon Linux) or `apt-get` (Ubuntu) on the
instances.
We strongly recommend using the default value of `True`, to ensure that
your instances have the latest security updates.
:type ebs_optimized: boolean
:param ebs_optimized: Whether to create an Amazon EBS-optimized
instance.
"""
params = {
@@ -644,6 +685,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['SshKeyName'] = ssh_key_name
if availability_zone is not None:
params['AvailabilityZone'] = availability_zone
if virtualization_type is not None:
params['VirtualizationType'] = virtualization_type
if subnet_id is not None:
params['SubnetId'] = subnet_id
if architecture is not None:
@@ -652,6 +695,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['RootDeviceType'] = root_device_type
if install_updates_on_boot is not None:
params['InstallUpdatesOnBoot'] = install_updates_on_boot
if ebs_optimized is not None:
params['EbsOptimized'] = ebs_optimized
return self.make_request(action='CreateInstance',
body=json.dumps(params))
@@ -661,19 +706,12 @@ class OpsWorksConnection(AWSQueryConnection):
volume_configurations=None, enable_auto_healing=None,
auto_assign_elastic_ips=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
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
have a Manage permissions level for the stack, or an attached
policy that explicitly grants permissions. For more
@@ -685,19 +723,21 @@ class OpsWorksConnection(AWSQueryConnection):
:type type: string
:param type:
The layer type. A stack cannot have more than one layer of the same
type. This parameter must be set to one of the following:
The layer type. A stack cannot have more than one built-in layer of the
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
+ 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
:param name: The layer name, which is used by the console.
@@ -711,7 +751,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map
: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
:param custom_instance_profile_arn: The ARN of an IAM profile that to
@@ -728,7 +768,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type volume_configurations: list
: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
:param enable_auto_healing: Whether to disable auto healing for the
@@ -749,16 +789,17 @@ class OpsWorksConnection(AWSQueryConnection):
layer custom recipes.
:type install_updates_on_boot: boolean
:param install_updates_on_boot:
Whether to install operating system and package updates when the
instance boots. The default value is `True`. To control when
updates are installed, set this value to `False`. You must then
update your instances manually by using CreateDeployment to run the
`update_dependencies` stack command or manually running `yum`
(Amazon Linux) or `apt-get` (Ubuntu) on the instances.
:param install_updates_on_boot: Whether to install operating system and
package updates when the instance boots. The default value is
`True`. To control when updates are installed, set this value to
`False`. You must then update your instances manually by using
CreateDeployment to run the `update_dependencies` stack command or
manually running `yum` (Amazon Linux) or `apt-get` (Ubuntu) on the
instances.
We strongly recommend using the default value of `True`, to ensure that
your instances have the latest security updates.
:type use_ebs_optimized_instances: boolean
:param use_ebs_optimized_instances: Whether to use Amazon EBS-optimized
instances.
"""
params = {
@@ -787,6 +828,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['CustomRecipes'] = custom_recipes
if install_updates_on_boot is not None:
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',
body=json.dumps(params))
@@ -795,8 +838,9 @@ class OpsWorksConnection(AWSQueryConnection):
attributes=None, default_os=None, hostname_theme=None,
default_availability_zone=None, default_subnet_id=None,
custom_json=None, configuration_manager=None,
use_custom_cookbooks=None, custom_cookbooks_source=None,
default_ssh_key_name=None,
chef_configuration=None, use_custom_cookbooks=None,
use_opsworks_security_groups=None,
custom_cookbooks_source=None, default_ssh_key_name=None,
default_root_device_type=None):
"""
Creates a new stack. For more information, see `Create a New
@@ -846,7 +890,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map
: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
: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.
It is used to override the corresponding default stack
configuration JSON values. The string should be in the following
format and must escape characters such as '"'.: `"{\"key1\":
\"value1\", \"key2\": \"value2\",...}"`
format and must escape characters such as '"'.:
`"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
For more information on custom JSON, see `Use Custom JSON to Modify the
Stack Configuration JSON`_.
:type configuration_manager: dict
:param configuration_manager: The configuration manager. When you
create a stack we recommend that you use the configuration manager
to specify the Chef version, 0.9 or 11.4. The default value is
currently 0.9. However, we expect to change the default value to
11.4 in September 2013.
:param configuration_manager: The configuration manager. When you clone
a stack we recommend that you use the configuration manager to
specify the Chef version, 0.9, 11.4, or 11.10. The default value is
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
: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
:param custom_cookbooks_source: Contains the information required to
retrieve an app or cookbook from a repository. For more
@@ -934,9 +1007,10 @@ class OpsWorksConnection(AWSQueryConnection):
:type default_root_device_type: string
:param default_root_device_type: The default root device type. This
value is used by default for all instances in the cloned stack, but
you can override it when you create an instance. For more
information, see `Storage for the Root Device`_.
value is used by default for all instances in the stack, but you
can override it when you create an instance. The default option is
`instance-store`. For more information, see `Storage for the Root
Device`_.
"""
params = {
@@ -961,8 +1035,12 @@ class OpsWorksConnection(AWSQueryConnection):
params['CustomJson'] = custom_json
if configuration_manager is not None:
params['ConfigurationManager'] = configuration_manager
if chef_configuration is not None:
params['ChefConfiguration'] = chef_configuration
if use_custom_cookbooks is not None:
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:
params['CustomCookbooksSource'] = custom_cookbooks_source
if default_ssh_key_name is not None:
@@ -986,7 +1064,12 @@ class OpsWorksConnection(AWSQueryConnection):
:param iam_user_arn: The user's IAM ARN.
: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
:param ssh_public_key: The user's public SSH key.
@@ -994,7 +1077,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type allow_self_management: boolean
:param allow_self_management: Whether users can specify their own SSH
public key through the My Settings page. For more information, see
``_.
`Setting an IAM User's Public SSH Key`_.
"""
params = {'IamUserArn': iam_user_arn, }
@@ -1046,7 +1129,7 @@ class OpsWorksConnection(AWSQueryConnection):
address.
: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.
"""
@@ -1135,6 +1218,18 @@ class OpsWorksConnection(AWSQueryConnection):
return self.make_request(action='DeregisterElasticIp',
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):
"""
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
permissions, see `Managing User Permissions`_.
"""
params = {}
return self.make_request(action='DescribeMyUserProfile',
@@ -1444,6 +1539,8 @@ class OpsWorksConnection(AWSQueryConnection):
"""
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
have a Manage permissions level for the stack, or an attached
policy that explicitly grants permissions. For more
@@ -1498,6 +1595,26 @@ class OpsWorksConnection(AWSQueryConnection):
return self.make_request(action='DescribeRaidArrays',
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,
service_error_ids=None):
"""
@@ -1583,8 +1700,6 @@ class OpsWorksConnection(AWSQueryConnection):
Describes time-based auto scaling configurations for specified
instances.
You must specify at least one of the parameters.
**Required Permissions**: To use this action, an IAM user must
have a Show, Deploy, or Manage permissions level for the
stack, or an attached policy that explicitly grants
@@ -1775,6 +1890,33 @@ class OpsWorksConnection(AWSQueryConnection):
return self.make_request(action='RegisterElasticIp',
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):
"""
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
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
have a Manage permissions level for the stack, or an attached
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,
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`_.
**Required Permissions**: To use this action, an IAM user must
@@ -1949,7 +2085,7 @@ class OpsWorksConnection(AWSQueryConnection):
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
have a Manage permissions level for the stack, or an attached
@@ -2025,9 +2161,10 @@ class OpsWorksConnection(AWSQueryConnection):
return self.make_request(action='UnassignVolume',
body=json.dumps(params))
def update_app(self, app_id, name=None, description=None, type=None,
app_source=None, domains=None, enable_ssl=None,
ssl_configuration=None, attributes=None):
def update_app(self, app_id, name=None, description=None,
data_sources=None, type=None, app_source=None,
domains=None, enable_ssl=None, ssl_configuration=None,
attributes=None):
"""
Updates a specified app.
@@ -2046,6 +2183,9 @@ class OpsWorksConnection(AWSQueryConnection):
:type description: string
:param description: A description of the app.
:type data_sources: list
:param data_sources: The app's data sources.
:type type: string
:param type: The app type.
@@ -2065,7 +2205,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map
: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, }
@@ -2073,6 +2213,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['Name'] = name
if description is not None:
params['Description'] = description
if data_sources is not None:
params['DataSources'] = data_sources
if type is not None:
params['Type'] = type
if app_source is not None:
@@ -2116,7 +2258,7 @@ class OpsWorksConnection(AWSQueryConnection):
instance_type=None, auto_scaling_type=None,
hostname=None, os=None, ami_id=None,
ssh_key_name=None, architecture=None,
install_updates_on_boot=None):
install_updates_on_boot=None, ebs_optimized=None):
"""
Updates a specified instance.
@@ -2185,16 +2327,16 @@ class OpsWorksConnection(AWSQueryConnection):
see `Instance Families and Types`_.
:type install_updates_on_boot: boolean
:param install_updates_on_boot:
Whether to install operating system and package updates when the
instance boots. The default value is `True`. To control when
updates are installed, set this value to `False`. You must then
update your instances manually by using CreateDeployment to run the
`update_dependencies` stack command or manually running `yum`
(Amazon Linux) or `apt-get` (Ubuntu) on the instances.
:param install_updates_on_boot: Whether to install operating system and
package updates when the instance boots. The default value is
`True`. To control when updates are installed, set this value to
`False`. You must then update your instances manually by using
CreateDeployment to run the `update_dependencies` stack command or
manually running `yum` (Amazon Linux) or `apt-get` (Ubuntu) on the
instances.
We strongly recommend using the default value of `True`, to ensure that
your instances have the latest security updates.
:type ebs_optimized: boolean
:param ebs_optimized: Whether this is an Amazon EBS-optimized instance.
"""
params = {'InstanceId': instance_id, }
@@ -2216,6 +2358,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['Architecture'] = architecture
if install_updates_on_boot is not None:
params['InstallUpdatesOnBoot'] = install_updates_on_boot
if ebs_optimized is not None:
params['EbsOptimized'] = ebs_optimized
return self.make_request(action='UpdateInstance',
body=json.dumps(params))
@@ -2225,7 +2369,8 @@ class OpsWorksConnection(AWSQueryConnection):
volume_configurations=None, enable_auto_healing=None,
auto_assign_elastic_ips=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.
@@ -2250,7 +2395,7 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map
: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
:param custom_instance_profile_arn: The ARN of an IAM profile to be
@@ -2288,16 +2433,17 @@ class OpsWorksConnection(AWSQueryConnection):
layer's custom recipes.
:type install_updates_on_boot: boolean
:param install_updates_on_boot:
Whether to install operating system and package updates when the
instance boots. The default value is `True`. To control when
updates are installed, set this value to `False`. You must then
update your instances manually by using CreateDeployment to run the
`update_dependencies` stack command or manually running `yum`
(Amazon Linux) or `apt-get` (Ubuntu) on the instances.
:param install_updates_on_boot: Whether to install operating system and
package updates when the instance boots. The default value is
`True`. To control when updates are installed, set this value to
`False`. You must then update your instances manually by using
CreateDeployment to run the `update_dependencies` stack command or
manually running `yum` (Amazon Linux) or `apt-get` (Ubuntu) on the
instances.
We strongly recommend using the default value of `True`, to ensure that
your instances have the latest security updates.
:type use_ebs_optimized_instances: boolean
:param use_ebs_optimized_instances: Whether to use Amazon EBS-optimized
instances.
"""
params = {'LayerId': layer_id, }
@@ -2325,6 +2471,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['CustomRecipes'] = custom_recipes
if install_updates_on_boot is not None:
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',
body=json.dumps(params))
@@ -2347,14 +2495,39 @@ class OpsWorksConnection(AWSQueryConnection):
return self.make_request(action='UpdateMyUserProfile',
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,
service_role_arn=None,
default_instance_profile_arn=None, default_os=None,
hostname_theme=None, default_availability_zone=None,
default_subnet_id=None, custom_json=None,
configuration_manager=None, use_custom_cookbooks=None,
custom_cookbooks_source=None, default_ssh_key_name=None,
default_root_device_type=None):
configuration_manager=None, chef_configuration=None,
use_custom_cookbooks=None, custom_cookbooks_source=None,
default_ssh_key_name=None,
default_root_device_type=None,
use_opsworks_security_groups=None):
"""
Updates a specified stack.
@@ -2372,20 +2545,14 @@ class OpsWorksConnection(AWSQueryConnection):
:type attributes: map
: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
:param service_role_arn:
The stack AWS Identity and Access Management (IAM) role, which allows
AWS OpsWorks to work with AWS resources on your behalf. You must
set this parameter to the Amazon Resource Name (ARN) for an
existing IAM role. For more information about IAM ARNs, see `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 stack's
current service role ARN, if you prefer, but you must do so
explicitly.
:param service_role_arn: The stack AWS Identity and Access Management
(IAM) role, which allows AWS OpsWorks to work with AWS resources on
your behalf. You must set this parameter to the Amazon Resource
Name (ARN) for an existing IAM role. For more information about IAM
ARNs, see `Using Identifiers`_.
:type default_instance_profile_arn: string
: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.
It is used to override the corresponding default stack
configuration JSON values. The string should be in the following
format and must escape characters such as '"'.: `"{\"key1\":
\"value1\", \"key2\": \"value2\",...}"`
format and must escape characters such as '"'.:
`"{\"key1\": \"value1\", \"key2\": \"value2\",...}"`
For more information on custom JSON, see `Use Custom JSON to Modify the
Stack Configuration JSON`_.
:type configuration_manager: dict
:param configuration_manager: The configuration manager. When you
update a stack you can optionally use the configuration manager to
specify the Chef version, 0.9 or 11.4. If you omit this parameter,
AWS OpsWorks does not change the Chef version.
:param configuration_manager: The configuration manager. When you clone
a stack we recommend that you use the configuration manager to
specify the Chef version, 0.9, 11.4, or 11.10. The default value is
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
:param use_custom_cookbooks: Whether the stack uses custom cookbooks.
@@ -2464,9 +2637,33 @@ class OpsWorksConnection(AWSQueryConnection):
:type default_root_device_type: string
:param default_root_device_type: The default root device type. This
value is used by default for all instances in the cloned stack, but
you can override it when you create an instance. For more
information, see `Storage for the Root Device`_.
value is used by default for all instances in the stack, but you
can override it when you create an instance. For more information,
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, }
@@ -2490,6 +2687,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['CustomJson'] = custom_json
if configuration_manager is not None:
params['ConfigurationManager'] = configuration_manager
if chef_configuration is not None:
params['ChefConfiguration'] = chef_configuration
if use_custom_cookbooks is not None:
params['UseCustomCookbooks'] = use_custom_cookbooks
if custom_cookbooks_source is not None:
@@ -2498,6 +2697,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['DefaultSshKeyName'] = default_ssh_key_name
if default_root_device_type is not None:
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',
body=json.dumps(params))
@@ -2515,7 +2716,12 @@ class OpsWorksConnection(AWSQueryConnection):
:param iam_user_arn: The user IAM ARN.
: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
: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
# 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,
# 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.
@@ -26,19 +26,20 @@ Implements plugin related api.
To define a new plugin just subclass Plugin, like this.
class AuthPlugin(Plugin):
pass
pass
Then start creating subclasses of your new plugin.
class MyFancyAuth(AuthPlugin):
capability = ['sign', 'vmac']
capability = ['sign', 'vmac']
The actual interface is duck typed.
"""
import glob
import imp, os.path
import imp
import os.path
class Plugin(object):
"""Base class for all plugins."""
@@ -50,10 +51,11 @@ class Plugin(object):
"""Returns true if the requested capability is supported by this plugin
"""
for c in requested_capability:
if not c in cls.capability:
if c not in cls.capability:
return False
return True
def get_plugin(cls, requested_capability=None):
if not requested_capability:
requested_capability = []
@@ -63,18 +65,20 @@ def get_plugin(cls, requested_capability=None):
result.append(handler)
return result
def _import_module(filename):
(path, name) = os.path.split(filename)
(name, ext) = os.path.splitext(name)
(file, filename, data) = imp.find_module(name, [path])
try:
return imp.load_module(name, file, filename, data)
return imp.load_module(name, file, filename, data)
finally:
if file:
file.close()
if file:
file.close()
_plugin_loaded = False
_plugin_loaded = False
def load_plugins(config):
global _plugin_loaded
@@ -87,4 +91,3 @@ def load_plugins(config):
directory = config.get('Plugin', 'plugin_directory')
for file in glob.glob(os.path.join(directory, '*.py')):
_import_module(file)

View File

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

View File

@@ -34,10 +34,6 @@ class ClusterSnapshotNotFoundFault(JSONResponseError):
pass
class ClusterNotFoundFault(JSONResponseError):
pass
class ClusterSecurityGroupQuotaExceededFault(JSONResponseError):
pass
@@ -457,3 +453,7 @@ class SnapshotCopyAlreadyEnabled(JSONResponseError):
class IncompatibleOrderableOptions(JSONResponseError):
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
# copy of this software and associated documentation files (the
@@ -20,8 +20,8 @@
# IN THE SOFTWARE.
#
import json
import boto
from boto.compat import json
from boto.connection import AWSQueryConnection
from boto.regioninfo import RegionInfo
from boto.exception import JSONResponseError
@@ -113,6 +113,7 @@ class RedshiftConnection(AWSQueryConnection):
"InvalidS3KeyPrefix": exceptions.InvalidS3KeyPrefix,
"SubscriptionAlreadyExist": exceptions.SubscriptionAlreadyExist,
"HsmConfigurationNotFound": exceptions.HsmConfigurationNotFound,
"InvalidSubscriptionState": exceptions.InvalidSubscriptionState,
"AuthorizationNotFound": exceptions.AuthorizationNotFound,
"ClusterSecurityGroupQuotaExceeded": exceptions.ClusterSecurityGroupQuotaExceeded,
"SubnetAlreadyInUse": exceptions.SubnetAlreadyInUse,
@@ -140,8 +141,10 @@ class RedshiftConnection(AWSQueryConnection):
if not region:
region = RegionInfo(self, self.DefaultRegionName,
self.DefaultRegionEndpoint)
if 'host' not in kwargs:
if 'host' not in kwargs or kwargs['host'] is None:
kwargs['host'] = region.endpoint
super(RedshiftConnection, self).__init__(**kwargs)
self.region = region
@@ -161,8 +164,7 @@ class RedshiftConnection(AWSQueryConnection):
Routing (CIDR) IP address range or an EC2 security group. You
can add as many as 20 ingress rules to an Amazon Redshift
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
`Classless Inter-Domain Routing`_.
@@ -269,7 +271,7 @@ class RedshiftConnection(AWSQueryConnection):
+ Must be the identifier for a valid automated snapshot whose state is
"available".
`available`.
:type source_snapshot_cluster_identifier: string
: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
information about node types, go to ` Working with Clusters`_ in
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
:param master_username:
@@ -459,6 +462,10 @@ class RedshiftConnection(AWSQueryConnection):
+ **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
@@ -541,7 +548,8 @@ class RedshiftConnection(AWSQueryConnection):
a public network.
: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
:type hsm_client_certificate_identifier: string
@@ -643,8 +651,7 @@ class RedshiftConnection(AWSQueryConnection):
+ 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.
+ Must be unique withing your AWS account.
+ Must be unique within your AWS account.
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
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
Management Guide .
@@ -715,7 +722,7 @@ class RedshiftConnection(AWSQueryConnection):
cluster_identifier):
"""
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
`Amazon Redshift Snapshots`_ in the Amazon Redshift Management
@@ -758,7 +765,7 @@ class RedshiftConnection(AWSQueryConnection):
Private Cloud (Amazon VPC) when creating Amazon Redshift
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
Guide .
@@ -815,13 +822,13 @@ class RedshiftConnection(AWSQueryConnection):
those criteria. For example, you can specify source type =
cluster, source ID = my-cluster-1 and mycluster2, event
categories = Availability, Backup, and severity = ERROR. The
subsription will only send notifications for those ERROR
events in the Availability and Backup categores for the
subscription will only send notifications for those ERROR
events in the Availability and Backup categories for the
specified clusters.
If you specify both the source type and source IDs, such as
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
source identifier, you will receive notice of the events for
the objects of that type in your AWS account. If you do not
@@ -917,16 +924,16 @@ class RedshiftConnection(AWSQueryConnection):
databases.
The command returns a public key, which you must store in the
HSM. After creating the HSM certificate, you must create an
Amazon Redshift HSM configuration that provides a cluster the
information needed to store and retrieve database encryption
keys in the HSM. For more information, go to aLinkToHSMTopic
in the Amazon Redshift Management Guide.
HSM. In addition to creating the HSM certificate, you must
create an Amazon Redshift HSM configuration that provides a
cluster the information needed to store and use encryption
keys in the HSM. For more information, go to `Hardware
Security Modules`_ in the Amazon Redshift Management Guide.
:type hsm_client_certificate_identifier: string
:param hsm_client_certificate_identifier: The identifier to be assigned
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 = {
@@ -943,15 +950,16 @@ class RedshiftConnection(AWSQueryConnection):
hsm_server_public_certificate):
"""
Creates an HSM configuration that contains the information
required by an Amazon Redshift cluster to store and retrieve
database encryption keys in a Hardware Storeage Module (HSM).
required by an Amazon Redshift cluster to store and use
database encryption keys in a Hardware Security Module (HSM).
After creating the HSM configuration, you can specify it as a
parameter when creating a cluster. The cluster will then store
its encryption keys in the HSM.
Before creating an HSM configuration, you must have first
created an HSM client certificate. For more information, go to
aLinkToHSMTopic in the Amazon Redshift Management Guide.
In addition to creating an HSM configuration, you must also
create an HSM client certificate. For more information, go to
`Hardware Security Modules`_ in the Amazon Redshift Management
Guide.
:type hsm_configuration_identifier: string
:param hsm_configuration_identifier: The identifier to be assigned to
@@ -975,9 +983,8 @@ class RedshiftConnection(AWSQueryConnection):
partition.
:type hsm_server_public_certificate: string
:param hsm_server_public_certificate: The public key used to access the
HSM client certificate, which was created by calling the Amazon
Redshift create HSM certificate command.
:param hsm_server_public_certificate: The HSMs public certificate file.
When using Cloud HSM, the file name is server.pem.
"""
params = {
@@ -1026,9 +1033,6 @@ class RedshiftConnection(AWSQueryConnection):
cluster. If `True`, a final cluster snapshot is not created. If
`False`, a final cluster snapshot is created before the cluster is
deleted.
The FinalClusterSnapshotIdentifier parameter must be specified if
SkipFinalClusterSnapshot is `False`.
Default: `False`
:type final_cluster_snapshot_identifier: string
@@ -1058,9 +1062,7 @@ class RedshiftConnection(AWSQueryConnection):
def delete_cluster_parameter_group(self, parameter_group_name):
"""
Deletes a specified Amazon Redshift parameter group. You
cannot delete a parameter group if it is associated with a
cluster.
Deletes a specified Amazon Redshift parameter group.
:type parameter_group_name: string
:param parameter_group_name:
@@ -1082,9 +1084,8 @@ class RedshiftConnection(AWSQueryConnection):
def delete_cluster_security_group(self, cluster_security_group_name):
"""
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
Management Guide .
@@ -1105,7 +1106,7 @@ class RedshiftConnection(AWSQueryConnection):
snapshot_cluster_identifier=None):
"""
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.
Unlike automated snapshots, manual snapshots are retained even
@@ -1224,19 +1225,23 @@ class RedshiftConnection(AWSQueryConnection):
groups and the default parameter group are returned.
:type max_records: integer
:param max_records: The maximum number of parameter group records to
include in the response. If more records exist than the specified
`MaxRecords` value, the response includes a marker that you can use
in a subsequent DescribeClusterParameterGroups request to retrieve
the next set of records.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: Value must be at least 20 and no more than 100.
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned by a previous
DescribeClusterParameterGroups request to indicate the first
parameter group that the current request will return.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
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 = {}
@@ -1284,19 +1289,23 @@ class RedshiftConnection(AWSQueryConnection):
Valid Values: `user` | `engine-default`
:type max_records: integer
:param max_records: The maximum number of records to include in the
response. If more records exist than the specified `MaxRecords`
value, response includes a marker that you can specify in your
subsequent request to retrieve remaining result.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: Value must be at least 20 and no more than 100.
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned from a previous
**DescribeClusterParameters** request. If this parameter is
specified, the response includes only records beyond the specified
marker, up to the value specified by `MaxRecords`.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
DescribeClusterParameters 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 = {'ParameterGroupName': parameter_group_name, }
@@ -1319,7 +1328,7 @@ class RedshiftConnection(AWSQueryConnection):
the name of a security group is specified, the response will
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
Management Guide .
@@ -1331,20 +1340,25 @@ class RedshiftConnection(AWSQueryConnection):
Example: `securitygroup1`
:type max_records: integer
:param max_records: The maximum number of records to be included in the
response. If more records exist than the specified `MaxRecords`
value, a marker is included in the response, which you can use in a
subsequent DescribeClusterSecurityGroups request.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: Value must be at least 20 and no more than 100.
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned by a previous
DescribeClusterSecurityGroups request to indicate the first
security group that the current request will return. You can
specify either the **Marker** parameter or a
**ClusterSecurityGroupName** parameter, but not both.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
DescribeClusterSecurityGroups 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.
Constraints: You can specify either the **ClusterSecurityGroupName**
parameter or the **Marker** parameter, but not both.
"""
params = {}
@@ -1401,19 +1415,23 @@ class RedshiftConnection(AWSQueryConnection):
Example: `2012-07-16T18:00:00Z`
:type max_records: integer
:param max_records: The maximum number of snapshot records to include
in the response. If more records exist than the specified
`MaxRecords` value, the response returns a marker that you can use
in a subsequent DescribeClusterSnapshots request in order to
retrieve the next set of snapshot records.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: Must be at least 20 and no more than 100.
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned by a previous
DescribeClusterSnapshots request to indicate the first snapshot
that the request will return.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
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
:param owner_account: The AWS customer account used to create or copy
@@ -1458,19 +1476,23 @@ class RedshiftConnection(AWSQueryConnection):
for which information is requested.
:type max_records: integer
:param max_records: The maximum number of cluster subnet group records
to include in the response. If more records exist than the
specified `MaxRecords` value, the response returns a marker that
you can use in a subsequent DescribeClusterSubnetGroups request in
order to retrieve the next set of cluster subnet group records.
Default: 100
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: Must be at least 20 and no more than 100.
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned by a previous
DescribeClusterSubnetGroups request to indicate the first cluster
subnet group that the current request will return.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
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 = {}
@@ -1512,18 +1534,23 @@ class RedshiftConnection(AWSQueryConnection):
+ Cannot end with a hyphen or contain two consecutive hyphens
:type max_records: integer
:param max_records: The maximum number of records to include in the
response. If more than the `MaxRecords` value is available, a
marker is included in the response so that the following results
can be retrieved.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: Value must be at least 20 and no more than 100.
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: The marker returned from a previous request. If this
parameter is specified, the response includes records beyond the
marker only, up to `MaxRecords`.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
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 = {}
@@ -1552,25 +1579,29 @@ class RedshiftConnection(AWSQueryConnection):
:type cluster_identifier: string
: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.
:type max_records: integer
:param max_records: The maximum number of records that the response can
include. If more records exist than the specified `MaxRecords`
value, a `marker` is included in the response that can be used in a
new **DescribeClusters** request to continue listing results.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: Value must be at least 20 and no more than 100.
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned by a previous
**DescribeClusters** request to indicate the first cluster that the
current **DescribeClusters** request will return.
You can specify either a **Marker** parameter or a
**ClusterIdentifier** parameter in a **DescribeClusters** request,
but not both.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
DescribeClusters 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.
Constraints: You can specify either the **ClusterIdentifier** parameter
or the **Marker** parameter, but not both.
"""
params = {}
@@ -1600,19 +1631,23 @@ class RedshiftConnection(AWSQueryConnection):
family.
:type max_records: integer
:param max_records: The maximum number of records to include in the
response. If more records exist than the specified `MaxRecords`
value, a marker is included in the response so that the remaining
results may be retrieved.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: Value must be at least 20 and no more than 100.
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned from a previous
**DescribeDefaultClusterParameters** request. If this parameter is
specified, the response includes only records beyond the marker, up
to the value specified by `MaxRecords`.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
DescribeDefaultClusterParameters 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 = {'ParameterGroupFamily': parameter_group_family, }
@@ -1659,19 +1694,23 @@ class RedshiftConnection(AWSQueryConnection):
notification subscription to be described.
:type max_records: integer
:param max_records: The maximum number of records to include in the
response. If more records exist than the specified MaxRecords
value, a pagination token called a marker is included in the
response so that the remaining results can be retrieved.
Default: 100
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: minimum 20, maximum 100
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional pagination token provided by a previous
DescribeOrderableClusterOptions request. If this parameter is
specified, the response includes only records beyond the marker, up
to the value specified by MaxRecords.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
DescribeEventSubscriptions 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 = {}
@@ -1753,19 +1792,22 @@ class RedshiftConnection(AWSQueryConnection):
Default: `60`
:type max_records: integer
:param max_records: The maximum number of records to include in the
response. If more records exist than the specified `MaxRecords`
value, a marker is included in the response so that the remaining
results may be retrieved.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: Value must be at least 20 and no more than 100.
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned from a previous
**DescribeEvents** request. If this parameter is specified, the
response includes only records beyond the marker, up to the value
specified by `MaxRecords`.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
DescribeEvents 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 = {}
@@ -1801,23 +1843,26 @@ class RedshiftConnection(AWSQueryConnection):
:param hsm_client_certificate_identifier: The identifier of a specific
HSM client certificate for which you want information. If no
identifier is specified, information is returned for all HSM client
certificates associated with Amazon Redshift clusters owned by your
AWS customer account.
certificates owned by your AWS customer account.
:type max_records: integer
:param max_records: The maximum number of records to include in the
response. If more records exist than the specified `MaxRecords`
value, a marker is included in the response so that the remaining
results may be retrieved.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned from a previous
**DescribeOrderableClusterOptions** request. If this parameter is
specified, the response includes only records beyond the marker, up
to the value specified by `MaxRecords`.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
DescribeHsmClientCertificates 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 = {}
@@ -1847,19 +1892,23 @@ class RedshiftConnection(AWSQueryConnection):
owned by your AWS customer account.
:type max_records: integer
:param max_records: The maximum number of records to include in the
response. If more records exist than the specified `MaxRecords`
value, a marker is included in the response so that the remaining
results may be retrieved.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned from a previous
**DescribeOrderableClusterOptions** request. If this parameter is
specified, the response includes only records beyond the marker, up
to the value specified by `MaxRecords`.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
DescribeHsmConfigurations 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 = {}
@@ -1921,19 +1970,23 @@ class RedshiftConnection(AWSQueryConnection):
show only the available offerings matching the specified node type.
:type max_records: integer
:param max_records: The maximum number of records to include in the
response. If more records exist than the specified `MaxRecords`
value, a marker is included in the response so that the remaining
results may be retrieved.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned from a previous
**DescribeOrderableClusterOptions** request. If this parameter is
specified, the response includes only records beyond the marker, up
to the value specified by `MaxRecords`.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
DescribeOrderableClusterOptions 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 = {}
@@ -1972,21 +2025,23 @@ class RedshiftConnection(AWSQueryConnection):
offering.
:type max_records: integer
:param max_records: The maximum number of records to include in the
response. If more records exist than the specified `MaxRecords`
value, a marker is included in the response so that the remaining
results may be retrieved.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned by a previous
DescribeReservedNodeOfferings request to indicate the first
offering that the request will return.
You can specify either a **Marker** parameter or a
**ClusterIdentifier** parameter in a DescribeClusters request, but
not both.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
DescribeReservedNodeOfferings 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 = {}
@@ -2010,18 +2065,23 @@ class RedshiftConnection(AWSQueryConnection):
:param reserved_node_id: Identifier for the node reservation.
:type max_records: integer
:param max_records: The maximum number of records to include in the
response. If more records exist than the specified `MaxRecords`
value, a marker is included in the response so that the remaining
results may be retrieved.
:param max_records: The maximum number of response records to return in
each call. If the number of remaining response records exceeds the
specified `MaxRecords` value, a value is returned in a `marker`
field of the response. You can retrieve the next set of records by
retrying the command with the returned marker value.
Default: `100`
Constraints: minimum 20, maximum 100.
:type marker: string
:param marker: An optional marker returned by a previous
DescribeReservedNodes request to indicate the first parameter group
that the current request will return.
:param marker: An optional parameter that specifies the starting point
to return a set of response records. When the results of a
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 = {}
@@ -2199,16 +2259,17 @@ class RedshiftConnection(AWSQueryConnection):
preferred_maintenance_window=None,
cluster_version=None, allow_version_upgrade=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
another security or parameter group, update the preferred
maintenance window, or change the master user password.
Resetting a cluster password or modifying the security groups
associated with a cluster do not need a reboot. However,
modifying parameter group requires a reboot for parameters to
take effect. For more information about managing clusters, go
to `Amazon Redshift Clusters`_ in the Amazon Redshift
modifying a parameter group requires a reboot for parameters
to take effect. For more information about managing clusters,
go to `Amazon Redshift Clusters`_ in the Amazon Redshift
Management Guide
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
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
: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.
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.
Constraints:
@@ -2280,7 +2342,7 @@ class RedshiftConnection(AWSQueryConnection):
+ Cannot end with a hyphen or contain two consecutive hyphens
: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.
:type master_user_password: string
@@ -2290,10 +2352,6 @@ class RedshiftConnection(AWSQueryConnection):
request and the completion of the request, the `MasterUserPassword`
element exists in the `PendingModifiedValues` element of the
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.
@@ -2323,7 +2381,7 @@ class RedshiftConnection(AWSQueryConnection):
you can still create manual snapshots when you want with
CreateClusterSnapshot.
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.
Default: Uses existing setting.
@@ -2376,6 +2434,20 @@ class RedshiftConnection(AWSQueryConnection):
configuration that contains the information the Amazon Redshift
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, }
if cluster_type is not None:
@@ -2409,6 +2481,8 @@ class RedshiftConnection(AWSQueryConnection):
params['HsmClientCertificateIdentifier'] = hsm_client_certificate_identifier
if hsm_configuration_identifier is not None:
params['HsmConfigurationIdentifier'] = hsm_configuration_identifier
if new_cluster_identifier is not None:
params['NewClusterIdentifier'] = new_cluster_identifier
return self._make_request(
action='ModifyCluster',
verb='POST',
@@ -2434,6 +2508,9 @@ class RedshiftConnection(AWSQueryConnection):
parameter name and parameter value; other name-value pairs of the
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, }
self.build_complex_list_params(
@@ -2694,7 +2771,12 @@ class RedshiftConnection(AWSQueryConnection):
owner_account=None,
hsm_client_certificate_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
the resulting cluster with the same configuration as the
@@ -2705,11 +2787,8 @@ class RedshiftConnection(AWSQueryConnection):
different security group and different parameter group with
the restored cluster.
If a snapshot is taken of a cluster in VPC, you can restore it
only in VPC. In this case, you must provide a cluster subnet
group where you want the cluster restored. If snapshot is
taken of a cluster outside VPC, then you can restore it only
outside VPC.
If you restore a cluster into a VPC, you must provide a
cluster subnet group where you want the cluster restored.
For more information about working with snapshots, go to
`Amazon Redshift Snapshots`_ in the Amazon Redshift Management
@@ -2787,6 +2866,68 @@ class RedshiftConnection(AWSQueryConnection):
:type elastic_ip: string
: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 = {
'ClusterIdentifier': cluster_identifier,
@@ -2814,6 +2955,20 @@ class RedshiftConnection(AWSQueryConnection):
params['HsmConfigurationIdentifier'] = hsm_configuration_identifier
if elastic_ip is not None:
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(
action='RestoreFromClusterSnapshot',
verb='POST',
@@ -2829,7 +2984,7 @@ class RedshiftConnection(AWSQueryConnection):
for a previously authorized IP range or Amazon EC2 security
group. To add an ingress rule, see
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 .
: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()
if not service_name in endpoints:
if service_name not in endpoints:
raise BotoClientError(
"Service '%s' not found in endpoints." % service_name
)

View File

@@ -1,9 +1,11 @@
import sys
from datetime import datetime
from threading import Thread
import Queue
from boto.utils import RequestHook
from boto.compat import long_type
class RequestLogger(RequestHook):
"""
@@ -14,18 +16,16 @@ class RequestLogger(RequestHook):
self.request_log_file = open(filename, 'w')
self.request_log_queue = Queue.Queue(100)
Thread(target=self._request_log_worker).start()
def handle_request_data(self, request, response, error=False):
len = 0 if error else response.getheader('Content-Length')
now = datetime.now()
time = now.strftime('%Y-%m-%d %H:%M:%S')
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
self.request_log_queue.put("'%s', '%s', '%s', '%s', '%s'\n" % (time, response.status, duration, len, request.params['Action']))
def _request_log_worker(self):
while True:
@@ -35,5 +35,5 @@ class RequestLogger(RequestHook):
self.request_log_file.flush()
self.request_log_queue.task_done()
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
class ResultSet(list):
"""
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,
with some additional methods for parsing XML results from AWS.
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
that it's quite fast and efficient but it makes some things rather
with some additional methods for parsing XML results from AWS.
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
that it's quite fast and efficient but it makes some things rather
difficult.
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_upload_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.is_truncated = False
self.next_token = None
@@ -132,6 +133,7 @@ class ResultSet(list):
else:
setattr(self, name, value)
class BooleanResult(object):
def __init__(self, marker_elem=None):

View File

@@ -47,7 +47,7 @@ HZXML = """<?xml version="1.0" encoding="UTF-8"?>
</HostedZoneConfig>
</CreateHostedZoneRequest>"""
#boto.set_stream_logger('dns')
# boto.set_stream_logger('dns')
class Route53Connection(AWSAuthConnection):
@@ -65,13 +65,14 @@ class Route53Connection(AWSAuthConnection):
host=DefaultHost, debug=0, security_token=None,
validate_certs=True, https_connection_factory=None,
profile_name=None):
super(Route53Connection, self).__init__(host,
aws_access_key_id, aws_secret_access_key,
True, port, proxy, proxy_port, debug=debug,
security_token=security_token,
validate_certs=validate_certs,
https_connection_factory=https_connection_factory,
profile_name=profile_name)
super(Route53Connection, self).__init__(
host,
aws_access_key_id, aws_secret_access_key,
True, port, proxy, proxy_port, debug=debug,
security_token=security_token,
validate_certs=validate_certs,
https_connection_factory=https_connection_factory,
profile_name=profile_name)
def _required_auth_capability(self):
return ['route53']
@@ -84,9 +85,9 @@ class Route53Connection(AWSAuthConnection):
continue
pairs.append(key + '=' + urllib.parse.quote(str(val)))
path += '?' + '&'.join(pairs)
return super(Route53Connection, self).make_request(action, path,
headers, data,
retry_handler=self._retry_handler)
return super(Route53Connection, self).make_request(
action, path, headers, data,
retry_handler=self._retry_handler)
# Hosted Zones
@@ -103,7 +104,7 @@ class Route53Connection(AWSAuthConnection):
if start_marker:
params = {'marker': start_marker}
response = self.make_request('GET', '/%s/hostedzone' % self.Version,
params=params)
params=params)
body = response.read()
boto.log.debug(body)
if response.status >= 300:
@@ -157,7 +158,7 @@ class Route53Connection(AWSAuthConnection):
hosted_zone_name += '.'
all_hosted_zones = self.get_all_hosted_zones()
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:
return self.get_hosted_zone(zone['Id'].split('/')[-1])
@@ -233,7 +234,6 @@ class Route53Connection(AWSAuthConnection):
h.parse(body)
return e
# Health checks
POSTHCXMLBody = """<CreateHealthCheckRequest xmlns="%(xmlns)s">
@@ -327,7 +327,6 @@ class Route53Connection(AWSAuthConnection):
h.parse(body)
return e
# Resource Record Sets
def get_all_rrsets(self, hosted_zone_id, type=None,
@@ -383,7 +382,7 @@ class Route53Connection(AWSAuthConnection):
"""
params = {'type': type, 'name': name,
'Identifier': identifier, 'maxitems': maxitems}
'identifier': identifier, 'maxitems': maxitems}
uri = '/%s/hostedzone/%s/rrset' % (self.Version, hosted_zone_id)
response = self.make_request('GET', uri, params=params)
body = response.read()
@@ -522,12 +521,18 @@ class Route53Connection(AWSAuthConnection):
if response.status == 400:
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
# Route53 returns this. See
# 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" % (
'PriorRequestNotComplete',
error,
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
# 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,
# 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 DNSServerError(BotoServerError):
class DNSServerError(BotoServerError):
pass

View File

@@ -141,6 +141,6 @@ class HealthCheck(object):
params['ip_addr_part'] = self.XMLIpAddrPart % {'ip_addr': self.ip_addr}
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

View File

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

View File

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

View File

@@ -60,7 +60,7 @@ class Zone(object):
return response['ChangeResourceRecordSetsResponse']['ChangeInfo']
def _new_record(self, changes, resource_type, name, value, ttl, identifier,
comment=""):
comment=""):
"""
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
# 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
region = None

View File

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

View File

@@ -19,6 +19,8 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
from boto.compat import urllib, six
def bucket_lister(bucket, prefix='', delimiter='', marker='', headers=None,
encoding_type=None):
"""
@@ -34,6 +36,10 @@ def bucket_lister(bucket, prefix='', delimiter='', marker='', headers=None,
yield k
if k:
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
class BucketListResultSet(object):

View File

@@ -89,10 +89,16 @@ class Key(object):
# x-amz-meta).
base_user_settable_fields = set(["cache-control", "content-disposition",
"content-encoding", "content-language",
"content-md5", "content-type"])
"content-md5", "content-type",
"x-robots-tag", "expires"])
_underscore_base_user_settable_fields = set()
for f in base_user_settable_fields:
_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):
if self.bucket:
return '<Key: %s,%s>' % (self.bucket.name, self.name)
name = u'<Key: %s,%s>' % (self.bucket.name, self.name)
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):
return self
@@ -302,20 +314,8 @@ class Key(object):
elif name.lower() == 'content-range':
end_range = re.sub('.*/(.*)', '\\1', value)
self.size = int(end_range)
elif name.lower() == 'etag':
self.etag = 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
elif name.lower() in Key.base_fields:
self.__dict__[name.lower().replace('-', '_')] = value
self.handle_version_headers(self.resp)
self.handle_encryption_headers(self.resp)
self.handle_restore_headers(self.resp)
@@ -555,6 +555,8 @@ class Key(object):
self.metadata['Content-MD5'] = value
else:
self.metadata[name] = value
if name.lower() in Key.base_user_settable_fields:
self.__dict__[name.lower().replace('-', '_')] = value
def update_metadata(self, d):
self.metadata.update(d)
@@ -932,7 +934,10 @@ class Key(object):
# the auth mechanism (because closures). Detect if it's SigV4 & embelish
# while we can before the auth calculations occur.
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 = boto.utils.merge_meta(headers, self.metadata, provider)
resp = self.bucket.connection.make_request(

View File

@@ -48,13 +48,13 @@ class SESConnection(AWSAuthConnection):
self.DefaultRegionEndpoint)
self.region = region
super(SESConnection, self).__init__(self.region.endpoint,
aws_access_key_id, aws_secret_access_key,
is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass, debug,
https_connection_factory, path,
security_token=security_token,
validate_certs=validate_certs,
profile_name=profile_name)
aws_access_key_id, aws_secret_access_key,
is_secure, port, proxy, proxy_port,
proxy_user, proxy_pass, debug,
https_connection_factory, path,
security_token=security_token,
validate_certs=validate_certs,
profile_name=profile_name)
def _required_auth_capability(self):
return ['ses']
@@ -260,18 +260,18 @@ class SESConnection(AWSAuthConnection):
raise ValueError("No text or html body found for mail")
self._build_list_params(params, to_addresses,
'Destination.ToAddresses.member')
'Destination.ToAddresses.member')
if cc_addresses:
self._build_list_params(params, cc_addresses,
'Destination.CcAddresses.member')
'Destination.CcAddresses.member')
if bcc_addresses:
self._build_list_params(params, bcc_addresses,
'Destination.BccAddresses.member')
'Destination.BccAddresses.member')
if reply_addresses:
self._build_list_params(params, reply_addresses,
'ReplyToAddresses.member')
'ReplyToAddresses.member')
return self._make_request('SendEmail', params)
@@ -318,7 +318,7 @@ class SESConnection(AWSAuthConnection):
if destinations:
self._build_list_params(params, destinations,
'Destinations.member')
'Destinations.member')
return self._make_request('SendRawEmail', params)
@@ -475,7 +475,7 @@ class SESConnection(AWSAuthConnection):
"""
params = {}
self._build_list_params(params, identities,
'Identities.member')
'Identities.member')
return self._make_request('GetIdentityVerificationAttributes', params)
def verify_domain_identity(self, domain):
@@ -531,17 +531,17 @@ class SESConnection(AWSAuthConnection):
:param identity: An email address or domain name.
: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.
Valid Values: Bounce | Complaint | Delivery
:type sns_topic: string or None
:param sns_topic: The Amazon Resource Name (ARN) of the Amazon Simple
Notification Service (Amazon SNS) topic.
:param sns_topic: The Amazon Resource Name (ARN) of the Amazon Simple
Notification Service (Amazon SNS) topic.
"""
params = {
'Identity': identity,
'NotificationType': notification_type
'Identity': identity,
'NotificationType': notification_type
}
if sns_topic:
params['SnsTopic'] = sns_topic
@@ -560,7 +560,6 @@ class SESConnection(AWSAuthConnection):
:param forwarding_enabled: Specifies whether or not to enable feedback forwarding.
"""
return self._make_request('SetIdentityFeedbackForwardingEnabled', {
'Identity': identity,
'ForwardingEnabled': 'true' if forwarding_enabled else 'false'
'Identity': identity,
'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
class SESError(BotoServerError):
"""
Sub-class all SES-related errors from here. Don't raise this error
@@ -13,24 +14,26 @@ class SESError(BotoServerError):
pass
class SESAddressNotVerifiedError(SESError):
"""
Raised when a "Reply-To" address has not been validated in SES yet.
"""
pass
class SESIdentityNotVerifiedError(SESError):
"""
Raised when an identity (domain or address) has not been verified in SES yet.
"""
pass
class SESDomainNotConfirmedError(SESError):
"""
"""
pass
class SESAddressBlacklistedError(SESError):
"""
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