summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Lindsley <daniel@toastdriven.com>2013-09-12 15:42:59 -0700
committerDaniel Lindsley <daniel@toastdriven.com>2013-09-12 15:42:59 -0700
commit7b70eafa9c91dfd5842eb6dfa7b7da0fee8074ff (patch)
tree2576889bebc71596bfd1f723dc08446bae28d703
parentad40af14b6989f940deac74e0276e8e8a8bd3372 (diff)
parent0efd52ea646fe536822dcf9d85bfe51cf4b7950a (diff)
downloadboto-7b70eafa9c91dfd5842eb6dfa7b7da0fee8074ff.tar.gz
Merge branch 'release-2.13.0'2.13.0
* release-2.13.0: (21 commits) Bumping version to 2.13.0 Updated release notes for everything that's been merged. Added support for modifying reserved instances in EC2. Added ``dry_run`` to. All. The. Things. Fixes #1679, #1566 - Altered DDBv2's ``batch_write`` to appropriately queue & retry unprocessed items. Adding API reference for boto.swf.layer2. Fixes #1709 - Cannot create Launch Configuration with Block Device Mappings Removing duplicate boto.s3.prefix entry to prevent Sphinx build errors. Removed the incomplete copyright notice from the README. More release notes for recent changes. Fixes #1664 - Corrected the behavior of ``dynamodb_load`` when working with sets. document get_all_images magic strings Added release notes for the recent merges. SNS ``publish`` now uses POST. Updated release notes to mention the Opsworks VPC support. Altered SDB to no longer cause errors when building the docs. Added Opsworks to the docs & cleaned up a bunch of Sphinx build errors. Fixed the creation of EC2 VPC instances with public IPs. Added dev release notes back in, as well as a template for future laziness. Updated Opsworks docstrings to latest. ...
-rw-r--r--README.rst17
-rwxr-xr-xbin/dynamodb_load2
-rw-r--r--boto/__init__.py2
-rw-r--r--boto/dynamodb2/layer1.py1
-rw-r--r--boto/dynamodb2/table.py51
-rw-r--r--boto/ec2/address.py33
-rw-r--r--boto/ec2/blockdevicemapping.py2
-rw-r--r--boto/ec2/connection.py880
-rw-r--r--boto/ec2/ec2object.py20
-rw-r--r--boto/ec2/image.py70
-rw-r--r--boto/ec2/instance.py96
-rw-r--r--boto/ec2/keypair.py18
-rw-r--r--boto/ec2/networkinterface.py47
-rw-r--r--boto/ec2/placementgroup.py11
-rw-r--r--boto/ec2/reservedinstance.py119
-rw-r--r--boto/ec2/securitygroup.py67
-rw-r--r--boto/ec2/snapshot.py61
-rw-r--r--boto/ec2/spotdatafeedsubscription.py10
-rw-r--r--boto/ec2/spotinstancerequest.py7
-rw-r--r--boto/ec2/volume.py52
-rw-r--r--boto/opsworks/layer1.py269
-rw-r--r--boto/sdb/db/manager/__init__.py4
-rw-r--r--boto/sdb/db/manager/sdbmanager.py18
-rw-r--r--boto/sdb/db/model.py25
-rw-r--r--boto/sns/connection.py2
-rw-r--r--boto/swf/layer2.py22
-rw-r--r--boto/vpc/__init__.py295
-rw-r--r--boto/vpc/vpc.py7
-rw-r--r--boto/vpc/vpnconnection.py7
-rw-r--r--boto/vpc/vpngateway.py16
-rw-r--r--docs/source/index.rst40
-rw-r--r--docs/source/ref/contrib.rst17
-rw-r--r--docs/source/ref/opsworks.rst28
-rw-r--r--docs/source/ref/s3.rst7
-rw-r--r--docs/source/ref/sdb_db.rst31
-rw-r--r--docs/source/ref/swf.rst5
-rw-r--r--docs/source/releasenotes/releasenotes_template.rst21
-rw-r--r--docs/source/releasenotes/v2.0.0.rst2
-rw-r--r--docs/source/releasenotes/v2.13.0.rst40
-rw-r--r--docs/source/releasenotes/v2.9.8.rst2
-rw-r--r--docs/source/s3_tut.rst2
-rw-r--r--tests/integration/dynamodb2/test_highlevel.py27
-rw-r--r--tests/integration/ec2/test_connection.py51
-rw-r--r--tests/integration/ec2/vpc/test_connection.py47
-rw-r--r--tests/unit/dynamodb2/test_table.py83
-rw-r--r--tests/unit/ec2/autoscale/test_group.py12
-rw-r--r--tests/unit/ec2/test_connection.py139
-rw-r--r--tests/unit/ec2/test_networkinterface.py148
48 files changed, 2333 insertions, 600 deletions
diff --git a/README.rst b/README.rst
index 36ed974e..a853566a 100644
--- a/README.rst
+++ b/README.rst
@@ -1,13 +1,13 @@
####
boto
####
-boto 2.12.0
+boto 2.13.0
-Released: 04-September-2013
+Released: 12-September-2013
.. image:: https://travis-ci.org/boto/boto.png?branch=develop
:target: https://travis-ci.org/boto/boto
-
+
.. image:: https://pypip.in/d/boto/badge.png
:target: https://crate.io/packages/boto/
@@ -41,6 +41,7 @@ At the moment, boto supports:
* AWS Elastic Beanstalk
* AWS CloudFormation
* AWS Data Pipeline
+ * AWS Opsworks
* Identity & Access
@@ -49,6 +50,7 @@ At the moment, boto supports:
* Application Services
* Amazon CloudSearch
+ * Amazon Elastic Transcoder
* Amazon Simple Workflow Service (SWF)
* Amazon Simple Queue Service (SQS)
* Amazon Simple Notification Server (SNS)
@@ -89,9 +91,9 @@ Web Services. In addition, boto provides support for other public
services such as Google Storage in addition to private cloud systems
like Eucalyptus, OpenStack and Open Nebula.
-Boto is developed mainly using Python 2.6.6 and Python 2.7.1 on Mac OSX
+Boto is developed mainly using Python 2.6.6 and Python 2.7.3 on Mac OSX
and Ubuntu Maverick. It is known to work on other Linux distributions
-and on Windows. Boto requires no additional libraries or packages
+and on Windows. Most of Boto requires no additional libraries or packages
other than those that are distributed with Python. Efforts are made
to keep boto compatible with Python 2.5.x but no guarantees are made.
@@ -153,11 +155,6 @@ following environment variables to ascertain your credentials:
Credentials and other boto-related settings can also be stored in a
boto config file. See `this`_ for details.
-Copyright (c) 2006-2012 Mitch Garnaat <mitch@garnaat.com>
-Copyright (c) 2010-2011, Eucalyptus Systems, Inc.
-Copyright (c) 2012 Amazon.com, Inc. or its affiliates.
-All rights reserved.
-
.. _pip: http://www.pip-installer.org/
.. _release notes: https://github.com/boto/boto/wiki
.. _github.com: http://github.com/boto/boto
diff --git a/bin/dynamodb_load b/bin/dynamodb_load
index 21dfa176..46a8d392 100755
--- a/bin/dynamodb_load
+++ b/bin/dynamodb_load
@@ -66,7 +66,7 @@ def load_table(table, in_fd):
data[k] = set(v)
else:
data[k] = v
- table.new_item(attrs=i).put()
+ table.new_item(attrs=data).put()
def dynamodb_load(tables, in_dir, create_tables):
diff --git a/boto/__init__.py b/boto/__init__.py
index cff05957..a498bb32 100644
--- a/boto/__init__.py
+++ b/boto/__init__.py
@@ -36,7 +36,7 @@ import logging.config
import urlparse
from boto.exception import InvalidUriError
-__version__ = '2.12.0'
+__version__ = '2.13.0'
Version = __version__ # for backware compatibility
UserAgent = 'Boto/%s Python/%s %s/%s' % (
diff --git a/boto/dynamodb2/layer1.py b/boto/dynamodb2/layer1.py
index 532e2f6e..039866f5 100644
--- a/boto/dynamodb2/layer1.py
+++ b/boto/dynamodb2/layer1.py
@@ -1491,6 +1491,7 @@ class DynamoDBConnection(AWSQueryConnection):
def _retry_handler(self, response, i, next_sleep):
status = None
+ boto.log.debug("Saw HTTP status: %s" % response.status)
if response.status == 400:
response_body = response.read()
boto.log.debug(response_body)
diff --git a/boto/dynamodb2/table.py b/boto/dynamodb2/table.py
index d552e4af..5d6803ce 100644
--- a/boto/dynamodb2/table.py
+++ b/boto/dynamodb2/table.py
@@ -1,3 +1,4 @@
+import boto
from boto.dynamodb2 import exceptions
from boto.dynamodb2.fields import (HashKey, RangeKey,
AllIndex, KeysOnlyIndex, IncludeIndex)
@@ -1070,17 +1071,19 @@ class BatchTable(object):
self.table = table
self._to_put = []
self._to_delete = []
+ self._unprocessed = []
def __enter__(self):
return self
def __exit__(self, type, value, traceback):
- if not self._to_put and not self._to_delete:
- return False
+ if self._to_put or self._to_delete:
+ # Flush anything that's left.
+ self.flush()
- # Flush anything that's left.
- self.flush()
- return True
+ if self._unprocessed:
+ # Finally, handle anything that wasn't processed.
+ self.resend_unprocessed()
def put_item(self, data, overwrite=False):
self._to_put.append(data)
@@ -1122,7 +1125,43 @@ class BatchTable(object):
}
})
- self.table.connection.batch_write_item(batch_data)
+ resp = self.table.connection.batch_write_item(batch_data)
+ self.handle_unprocessed(resp)
+
self._to_put = []
self._to_delete = []
return True
+
+ def handle_unprocessed(self, resp):
+ if len(resp.get('UnprocessedItems', [])):
+ table_name = self.table.table_name
+ unprocessed = resp['UnprocessedItems'].get(table_name, [])
+
+ # Some items have not been processed. Stow them for now &
+ # re-attempt processing on ``__exit__``.
+ msg = "%s items were unprocessed. Storing for later."
+ boto.log.info(msg % len(unprocessed))
+ self._unprocessed.extend(unprocessed)
+
+ def resend_unprocessed(self):
+ # If there are unprocessed records (for instance, the user was over
+ # their throughput limitations), iterate over them & send until they're
+ # all there.
+ boto.log.info(
+ "Re-sending %s unprocessed items." % len(self._unprocessed)
+ )
+
+ while len(self._unprocessed):
+ # Again, do 25 at a time.
+ to_resend = self._unprocessed[:25]
+ # Remove them from the list.
+ self._unprocessed = self._unprocessed[25:]
+ batch_data = {
+ self.table.table_name: to_resend
+ }
+ boto.log.info("Sending %s items" % len(to_resend))
+ resp = self.table.connection.batch_write_item(batch_data)
+ self.handle_unprocessed(resp)
+ boto.log.info(
+ "%s unprocessed items left" % len(self._unprocessed)
+ ) \ No newline at end of file
diff --git a/boto/ec2/address.py b/boto/ec2/address.py
index 9eadfaa3..27608a4a 100644
--- a/boto/ec2/address.py
+++ b/boto/ec2/address.py
@@ -71,33 +71,50 @@ class Address(EC2Object):
else:
setattr(self, name, value)
- def release(self):
+ def release(self, dry_run=False):
"""
Free up this Elastic IP address.
:see: :meth:`boto.ec2.connection.EC2Connection.release_address`
"""
if self.allocation_id:
- return self.connection.release_address(None, self.allocation_id)
+ return self.connection.release_address(
+ None,
+ self.allocation_id,
+ dry_run=dry_run)
else:
- return self.connection.release_address(self.public_ip)
+ return self.connection.release_address(
+ self.public_ip,
+ dry_run=dry_run
+ )
delete = release
- def associate(self, instance_id):
+ def associate(self, instance_id, dry_run=False):
"""
Associate this Elastic IP address with a currently running instance.
:see: :meth:`boto.ec2.connection.EC2Connection.associate_address`
"""
- return self.connection.associate_address(instance_id, self.public_ip)
+ return self.connection.associate_address(
+ instance_id,
+ self.public_ip,
+ dry_run=dry_run
+ )
- def disassociate(self):
+ def disassociate(self, dry_run=False):
"""
Disassociate this Elastic IP address from a currently running instance.
:see: :meth:`boto.ec2.connection.EC2Connection.disassociate_address`
"""
if self.association_id:
- return self.connection.disassociate_address(None, self.association_id)
+ return self.connection.disassociate_address(
+ None,
+ self.association_id,
+ dry_run=dry_run
+ )
else:
- return self.connection.disassociate_address(self.public_ip)
+ return self.connection.disassociate_address(
+ self.public_ip,
+ dry_run=dry_run
+ )
diff --git a/boto/ec2/blockdevicemapping.py b/boto/ec2/blockdevicemapping.py
index df774ae9..1fb67b53 100644
--- a/boto/ec2/blockdevicemapping.py
+++ b/boto/ec2/blockdevicemapping.py
@@ -118,7 +118,7 @@ class BlockDeviceMapping(dict):
def build_list_params(self, params, prefix=''):
i = 1
for dev_name in self:
- pre = '%sBlockDeviceMapping.%d' % (prefix, i)
+ pre = '%sBlockDeviceMappings.member.%d' % (prefix, i)
params['%s.DeviceName' % pre] = dev_name
block_dev = self[dev_name]
if block_dev.ephemeral_name:
diff --git a/boto/ec2/connection.py b/boto/ec2/connection.py
index 38eae9a0..e3c86826 100644
--- a/boto/ec2/connection.py
+++ b/boto/ec2/connection.py
@@ -48,6 +48,9 @@ from boto.ec2.instanceinfo import InstanceInfo
from boto.ec2.reservedinstance import ReservedInstancesOffering
from boto.ec2.reservedinstance import ReservedInstance
from boto.ec2.reservedinstance import ReservedInstanceListing
+from boto.ec2.reservedinstance import ReservedInstancesConfiguration
+from boto.ec2.reservedinstance import ModifyReservedInstancesResult
+from boto.ec2.reservedinstance import ReservedInstancesModification
from boto.ec2.spotinstancerequest import SpotInstanceRequest
from boto.ec2.spotpricehistory import SpotPriceHistory
from boto.ec2.spotdatafeedsubscription import SpotDatafeedSubscription
@@ -66,7 +69,7 @@ from boto.exception import EC2ResponseError
class EC2Connection(AWSQueryConnection):
- APIVersion = boto.config.get('Boto', 'ec2_version', '2013-02-01')
+ APIVersion = boto.config.get('Boto', 'ec2_version', '2013-07-15')
DefaultRegionName = boto.config.get('Boto', 'ec2_region_name', 'us-east-1')
DefaultRegionEndpoint = boto.config.get('Boto', 'ec2_region_endpoint',
'ec2.us-east-1.amazonaws.com')
@@ -133,7 +136,7 @@ class EC2Connection(AWSQueryConnection):
# Image methods
def get_all_images(self, image_ids=None, owners=None,
- executable_by=None, filters=None):
+ executable_by=None, filters=None, dry_run=False):
"""
Retrieve all the EC2 images available on your account.
@@ -141,7 +144,10 @@ class EC2Connection(AWSQueryConnection):
:param image_ids: A list of strings with the image IDs wanted
:type owners: list
- :param owners: A list of owner IDs
+ :param owners: A list of owner IDs, the special strings 'self',
+ 'amazon', and 'aws-marketplace', may be used to describe
+ images owned by you, Amazon or AWS Marketplace
+ respectively
:type executable_by: list
:param executable_by: Returns AMIs for which the specified
@@ -155,6 +161,9 @@ class EC2Connection(AWSQueryConnection):
names/values is dependent on the request being performed.
Check the EC2 API guide for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.image.Image`
"""
@@ -167,10 +176,12 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, executable_by, 'ExecutableBy')
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeImages', params,
[('item', Image)], verb='POST')
- def get_all_kernels(self, kernel_ids=None, owners=None):
+ def get_all_kernels(self, kernel_ids=None, owners=None, dry_run=False):
"""
Retrieve all the EC2 kernels available on your account.
Constructs a filter to allow the processing to happen server side.
@@ -181,6 +192,9 @@ class EC2Connection(AWSQueryConnection):
:type owners: list
:param owners: A list of owner IDs
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.image.Image`
"""
@@ -191,10 +205,12 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, owners, 'Owner')
filter = {'image-type': 'kernel'}
self.build_filter_params(params, filter)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeImages', params,
[('item', Image)], verb='POST')
- def get_all_ramdisks(self, ramdisk_ids=None, owners=None):
+ def get_all_ramdisks(self, ramdisk_ids=None, owners=None, dry_run=False):
"""
Retrieve all the EC2 ramdisks available on your account.
Constructs a filter to allow the processing to happen server side.
@@ -205,6 +221,9 @@ class EC2Connection(AWSQueryConnection):
:type owners: list
:param owners: A list of owner IDs
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.image.Image`
"""
@@ -215,27 +234,33 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, owners, 'Owner')
filter = {'image-type': 'ramdisk'}
self.build_filter_params(params, filter)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeImages', params,
[('item', Image)], verb='POST')
- def get_image(self, image_id):
+ def get_image(self, image_id, dry_run=False):
"""
Shortcut method to retrieve a specific image (AMI).
:type image_id: string
:param image_id: the ID of the Image to retrieve
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.image.Image`
:return: The EC2 Image specified or None if the image is not found
"""
try:
- return self.get_all_images(image_ids=[image_id])[0]
+ return self.get_all_images(image_ids=[image_id], dry_run=dry_run)[0]
except IndexError: # None of those images available
return None
def register_image(self, name=None, description=None, image_location=None,
architecture=None, kernel_id=None, ramdisk_id=None,
- root_device_name=None, block_device_map=None):
+ root_device_name=None, block_device_map=None,
+ dry_run=False):
"""
Register an image.
@@ -265,6 +290,9 @@ class EC2Connection(AWSQueryConnection):
:param block_device_map: A BlockDeviceMapping data structure
describing the EBS volumes associated with the Image.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: string
:return: The new image id
"""
@@ -285,11 +313,13 @@ class EC2Connection(AWSQueryConnection):
params['RootDeviceName'] = root_device_name
if block_device_map:
block_device_map.build_list_params(params)
+ if dry_run:
+ params['DryRun'] = 'true'
rs = self.get_object('RegisterImage', params, ResultSet, verb='POST')
image_id = getattr(rs, 'imageId', None)
return image_id
- def deregister_image(self, image_id, delete_snapshot=False):
+ def deregister_image(self, image_id, delete_snapshot=False, dry_run=False):
"""
Unregister an AMI.
@@ -300,6 +330,9 @@ class EC2Connection(AWSQueryConnection):
:param delete_snapshot: Set to True if we should delete the
snapshot associated with an EBS volume mounted at /dev/sda1
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -310,15 +343,19 @@ class EC2Connection(AWSQueryConnection):
if key == "/dev/sda1":
snapshot_id = image.block_device_mapping[key].snapshot_id
break
-
+ params = {
+ 'ImageId': image_id,
+ }
+ if dry_run:
+ params['DryRun'] = 'true'
result = self.get_status('DeregisterImage',
- {'ImageId':image_id}, verb='POST')
+ params, verb='POST')
if result and snapshot_id:
return result and self.delete_snapshot(snapshot_id)
return result
def create_image(self, instance_id, name,
- description=None, no_reboot=False):
+ description=None, no_reboot=False, dry_run=False):
"""
Will create an AMI from the instance in the running or stopped
state.
@@ -340,6 +377,9 @@ class EC2Connection(AWSQueryConnection):
responsibility of maintaining file system integrity is
left to the owner of the instance.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: string
:return: The new image id
"""
@@ -349,12 +389,15 @@ class EC2Connection(AWSQueryConnection):
params['Description'] = description
if no_reboot:
params['NoReboot'] = 'true'
+ if dry_run:
+ params['DryRun'] = 'true'
img = self.get_object('CreateImage', params, Image, verb='POST')
return img.id
# ImageAttribute methods
- def get_image_attribute(self, image_id, attribute='launchPermission'):
+ def get_image_attribute(self, image_id, attribute='launchPermission',
+ dry_run=False):
"""
Gets an attribute from an image.
@@ -368,18 +411,23 @@ class EC2Connection(AWSQueryConnection):
* productCodes
* blockDeviceMapping
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.image.ImageAttribute`
:return: An ImageAttribute object representing the value of the
attribute requested
"""
params = {'ImageId': image_id,
'Attribute': attribute}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('DescribeImageAttribute', params,
ImageAttribute, verb='POST')
def modify_image_attribute(self, image_id, attribute='launchPermission',
operation='add', user_ids=None, groups=None,
- product_codes=None):
+ product_codes=None, dry_run=False):
"""
Changes an attribute of an image.
@@ -403,6 +451,10 @@ class EC2Connection(AWSQueryConnection):
:param product_codes: Amazon DevPay product code. Currently only one
product code can be associated with an AMI. Once
set, the product code cannot be changed or reset.
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {'ImageId': image_id,
'Attribute': attribute,
@@ -413,9 +465,12 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, groups, 'UserGroup')
if product_codes:
self.build_list_params(params, product_codes, 'ProductCode')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('ModifyImageAttribute', params, verb='POST')
- def reset_image_attribute(self, image_id, attribute='launchPermission'):
+ def reset_image_attribute(self, image_id, attribute='launchPermission',
+ dry_run=False):
"""
Resets an attribute of an AMI to its default value.
@@ -425,16 +480,21 @@ class EC2Connection(AWSQueryConnection):
:type attribute: string
:param attribute: The attribute to reset
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: Whether the operation succeeded or not
"""
params = {'ImageId': image_id,
'Attribute': attribute}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('ResetImageAttribute', params, verb='POST')
# Instance methods
- def get_all_instances(self, instance_ids=None, filters=None):
+ def get_all_instances(self, instance_ids=None, filters=None, dry_run=False):
"""
Retrieve all the instance reservations associated with your account.
@@ -456,6 +516,9 @@ class EC2Connection(AWSQueryConnection):
names/values is dependent on the request being performed.
Check the EC2 API guide for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.instance.Reservation`
@@ -464,9 +527,10 @@ class EC2Connection(AWSQueryConnection):
'replaced with get_all_reservations.'),
PendingDeprecationWarning)
return self.get_all_reservations(instance_ids=instance_ids,
- filters=filters)
+ filters=filters, dry_run=dry_run)
- def get_only_instances(self, instance_ids=None, filters=None):
+ def get_only_instances(self, instance_ids=None, filters=None,
+ dry_run=False):
# A future release should rename this method to get_all_instances
# and make get_only_instances an alias for that.
"""
@@ -483,15 +547,20 @@ class EC2Connection(AWSQueryConnection):
names/values is dependent on the request being performed.
Check the EC2 API guide for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.instance.Instance`
"""
reservations = self.get_all_reservations(instance_ids=instance_ids,
- filters=filters)
+ filters=filters,
+ dry_run=dry_run)
return [instance for reservation in reservations
for instance in reservation.instances]
- def get_all_reservations(self, instance_ids=None, filters=None):
+ def get_all_reservations(self, instance_ids=None, filters=None,
+ dry_run=False):
"""
Retrieve all the instance reservations associated with your account.
@@ -506,6 +575,9 @@ class EC2Connection(AWSQueryConnection):
names/values is dependent on the request being performed.
Check the EC2 API guide for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.instance.Reservation`
"""
@@ -522,12 +594,14 @@ class EC2Connection(AWSQueryConnection):
"by group name use the 'group-name' filter instead.",
UserWarning)
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeInstances', params,
[('item', Reservation)], verb='POST')
def get_all_instance_status(self, instance_ids=None,
max_results=None, next_token=None,
- filters=None):
+ filters=None, dry_run=False):
"""
Retrieve all the instances in your account scheduled for maintenance.
@@ -552,6 +626,9 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of instances that have maintenance scheduled.
"""
@@ -564,6 +641,8 @@ class EC2Connection(AWSQueryConnection):
params['NextToken'] = next_token
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('DescribeInstanceStatus', params,
InstanceStatusSet, verb='POST')
@@ -581,7 +660,8 @@ class EC2Connection(AWSQueryConnection):
security_group_ids=None,
additional_info=None, instance_profile_name=None,
instance_profile_arn=None, tenancy=None,
- ebs_optimized=False, network_interfaces=None):
+ ebs_optimized=False, network_interfaces=None,
+ dry_run=False):
"""
Runs an image on EC2.
@@ -712,6 +792,9 @@ class EC2Connection(AWSQueryConnection):
:param network_interfaces: A list of
:class:`boto.ec2.networkinterface.NetworkInterfaceSpecification`
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: Reservation
:return: The :class:`boto.ec2.instance.Reservation` associated with
the request for machines
@@ -778,26 +861,33 @@ class EC2Connection(AWSQueryConnection):
params['EbsOptimized'] = 'true'
if network_interfaces:
network_interfaces.build_list_params(params)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('RunInstances', params, Reservation,
verb='POST')
- def terminate_instances(self, instance_ids=None):
+ def terminate_instances(self, instance_ids=None, dry_run=False):
"""
Terminate the instances specified
:type instance_ids: list
:param instance_ids: A list of strings of the Instance IDs to terminate
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of the instances terminated
"""
params = {}
if instance_ids:
self.build_list_params(params, instance_ids, 'InstanceId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('TerminateInstances', params,
[('item', Instance)], verb='POST')
- def stop_instances(self, instance_ids=None, force=False):
+ def stop_instances(self, instance_ids=None, force=False, dry_run=False):
"""
Stop the instances specified
@@ -807,6 +897,9 @@ class EC2Connection(AWSQueryConnection):
:type force: bool
:param force: Forces the instance to stop
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of the instances stopped
"""
@@ -815,62 +908,88 @@ class EC2Connection(AWSQueryConnection):
params['Force'] = 'true'
if instance_ids:
self.build_list_params(params, instance_ids, 'InstanceId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('StopInstances', params,
[('item', Instance)], verb='POST')
- def start_instances(self, instance_ids=None):
+ def start_instances(self, instance_ids=None, dry_run=False):
"""
Start the instances specified
:type instance_ids: list
:param instance_ids: A list of strings of the Instance IDs to start
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of the instances started
"""
params = {}
if instance_ids:
self.build_list_params(params, instance_ids, 'InstanceId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('StartInstances', params,
[('item', Instance)], verb='POST')
- def get_console_output(self, instance_id):
+ def get_console_output(self, instance_id, dry_run=False):
"""
Retrieves the console output for the specified instance.
:type instance_id: string
:param instance_id: The instance ID of a running instance on the cloud.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.instance.ConsoleOutput`
:return: The console output as a ConsoleOutput object
"""
params = {}
self.build_list_params(params, [instance_id], 'InstanceId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('GetConsoleOutput', params,
ConsoleOutput, verb='POST')
- def reboot_instances(self, instance_ids=None):
+ def reboot_instances(self, instance_ids=None, dry_run=False):
"""
Reboot the specified instances.
:type instance_ids: list
:param instance_ids: The instances to terminate and reboot
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {}
if instance_ids:
self.build_list_params(params, instance_ids, 'InstanceId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('RebootInstances', params)
- def confirm_product_instance(self, product_code, instance_id):
+ def confirm_product_instance(self, product_code, instance_id,
+ dry_run=False):
+ """
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
+ """
params = {'ProductCode': product_code,
'InstanceId': instance_id}
+ if dry_run:
+ params['DryRun'] = 'true'
rs = self.get_object('ConfirmProductInstance', params,
ResultSet, verb='POST')
return (rs.status, rs.ownerId)
# InstanceAttribute methods
- def get_instance_attribute(self, instance_id, attribute):
+ def get_instance_attribute(self, instance_id, attribute, dry_run=False):
"""
Gets an attribute from an instance.
@@ -894,6 +1013,9 @@ class EC2Connection(AWSQueryConnection):
* groupSet
* ebsOptimized
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.image.InstanceAttribute`
:return: An InstanceAttribute object representing the value of the
attribute requested
@@ -901,11 +1023,13 @@ class EC2Connection(AWSQueryConnection):
params = {'InstanceId': instance_id}
if attribute:
params['Attribute'] = attribute
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('DescribeInstanceAttribute', params,
InstanceAttribute, verb='POST')
def modify_network_interface_attribute(self, interface_id, attr, value,
- attachment_id=None):
+ attachment_id=None, dry_run=False):
"""
Changes an attribute of a network interface.
@@ -932,6 +1056,10 @@ class EC2Connection(AWSQueryConnection):
:type attachment_id: string
:param attachment_id: If you're modifying DeleteOnTermination you must
specify the attachment_id.
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
bool_reqs = (
'deleteontermination',
@@ -967,10 +1095,13 @@ class EC2Connection(AWSQueryConnection):
else:
raise ValueError('Unknown attribute "%s"' % (attr,))
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status(
'ModifyNetworkInterfaceAttribute', params, verb='POST')
- def modify_instance_attribute(self, instance_id, attribute, value):
+ def modify_instance_attribute(self, instance_id, attribute, value,
+ dry_run=False):
"""
Changes an attribute of an instance
@@ -994,6 +1125,9 @@ class EC2Connection(AWSQueryConnection):
:type value: string
:param value: The new value for the attribute
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: Whether the operation succeeded or not
"""
@@ -1027,9 +1161,11 @@ class EC2Connection(AWSQueryConnection):
attribute = attribute[0].upper() + attribute[1:]
params['%s.Value' % attribute] = value
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('ModifyInstanceAttribute', params, verb='POST')
- def reset_instance_attribute(self, instance_id, attribute):
+ def reset_instance_attribute(self, instance_id, attribute, dry_run=False):
"""
Resets an attribute of an instance to its default value.
@@ -1040,17 +1176,22 @@ class EC2Connection(AWSQueryConnection):
:param attribute: The attribute to reset. Valid values are:
kernel|ramdisk
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: Whether the operation succeeded or not
"""
params = {'InstanceId': instance_id,
'Attribute': attribute}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('ResetInstanceAttribute', params, verb='POST')
# Spot Instances
def get_all_spot_instance_requests(self, request_ids=None,
- filters=None):
+ filters=None, dry_run=False):
"""
Retrieve all the spot instances requests associated with your account.
@@ -1065,6 +1206,9 @@ class EC2Connection(AWSQueryConnection):
names/values is dependent on the request being performed.
Check the EC2 API guide for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of
:class:`boto.ec2.spotinstancerequest.SpotInstanceRequest`
@@ -1082,12 +1226,14 @@ class EC2Connection(AWSQueryConnection):
"group name. Please update your filters accordingly.",
UserWarning)
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeSpotInstanceRequests', params,
[('item', SpotInstanceRequest)], verb='POST')
def get_spot_price_history(self, start_time=None, end_time=None,
instance_type=None, product_description=None,
- availability_zone=None):
+ availability_zone=None, dry_run=False):
"""
Retrieve the recent history of spot instances pricing.
@@ -1118,6 +1264,9 @@ class EC2Connection(AWSQueryConnection):
should be returned. If not specified, data for all
availability zones will be returned.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list tuples containing price and timestamp.
"""
@@ -1132,6 +1281,8 @@ class EC2Connection(AWSQueryConnection):
params['ProductDescription'] = product_description
if availability_zone:
params['AvailabilityZone'] = availability_zone
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeSpotPriceHistory', params,
[('item', SpotPriceHistory)], verb='POST')
@@ -1149,7 +1300,7 @@ class EC2Connection(AWSQueryConnection):
instance_profile_name=None,
security_group_ids=None,
ebs_optimized=False,
- network_interfaces=None):
+ network_interfaces=None, dry_run=False):
"""
Request instances on the spot market at a particular price.
@@ -1256,6 +1407,9 @@ class EC2Connection(AWSQueryConnection):
:param network_interfaces: A list of
:class:`boto.ec2.networkinterface.NetworkInterfaceSpecification`
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: Reservation
:return: The :class:`boto.ec2.spotinstancerequest.SpotInstanceRequest`
associated with the request for machines
@@ -1321,38 +1475,51 @@ class EC2Connection(AWSQueryConnection):
params['%s.EbsOptimized' % ls] = 'true'
if network_interfaces:
network_interfaces.build_list_params(params, prefix=ls + '.')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('RequestSpotInstances', params,
[('item', SpotInstanceRequest)],
verb='POST')
- def cancel_spot_instance_requests(self, request_ids):
+ def cancel_spot_instance_requests(self, request_ids, dry_run=False):
"""
Cancel the specified Spot Instance Requests.
:type request_ids: list
:param request_ids: A list of strings of the Request IDs to terminate
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of the instances terminated
"""
params = {}
if request_ids:
self.build_list_params(params, request_ids, 'SpotInstanceRequestId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('CancelSpotInstanceRequests', params,
[('item', Instance)], verb='POST')
- def get_spot_datafeed_subscription(self):
+ def get_spot_datafeed_subscription(self, dry_run=False):
"""
Return the current spot instance data feed subscription
associated with this account, if any.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.spotdatafeedsubscription.SpotDatafeedSubscription`
:return: The datafeed subscription object or None
"""
+ params = {}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('DescribeSpotDatafeedSubscription',
- None, SpotDatafeedSubscription, verb='POST')
+ params, SpotDatafeedSubscription, verb='POST')
- def create_spot_datafeed_subscription(self, bucket, prefix):
+ def create_spot_datafeed_subscription(self, bucket, prefix, dry_run=False):
"""
Create a spot instance datafeed subscription for this account.
@@ -1366,29 +1533,40 @@ class EC2Connection(AWSQueryConnection):
:param prefix: An optional prefix that will be pre-pended to all
data files written to the bucket.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.spotdatafeedsubscription.SpotDatafeedSubscription`
:return: The datafeed subscription object or None
"""
params = {'Bucket': bucket}
if prefix:
params['Prefix'] = prefix
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateSpotDatafeedSubscription',
params, SpotDatafeedSubscription, verb='POST')
- def delete_spot_datafeed_subscription(self):
+ def delete_spot_datafeed_subscription(self, dry_run=False):
"""
Delete the current spot instance data feed subscription
associated with this account
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
+ params = {}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteSpotDatafeedSubscription',
- None, verb='POST')
+ params, verb='POST')
# Zone methods
- def get_all_zones(self, zones=None, filters=None):
+ def get_all_zones(self, zones=None, filters=None, dry_run=False):
"""
Get all Availability Zones associated with the current region.
@@ -1407,6 +1585,9 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list of :class:`boto.ec2.zone.Zone`
:return: The requested Zone objects
"""
@@ -1415,12 +1596,15 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, zones, 'ZoneName')
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeAvailabilityZones', params,
[('item', Zone)], verb='POST')
# Address methods
- def get_all_addresses(self, addresses=None, filters=None, allocation_ids=None):
+ def get_all_addresses(self, addresses=None, filters=None,
+ allocation_ids=None, dry_run=False):
"""
Get all EIP's associated with the current credentials.
@@ -1444,6 +1628,9 @@ class EC2Connection(AWSQueryConnection):
present, only the Addresses associated with the given
allocation IDs will be returned.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list of :class:`boto.ec2.address.Address`
:return: The requested Address objects
"""
@@ -1454,9 +1641,11 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, allocation_ids, 'AllocationId')
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeAddresses', params, [('item', Address)], verb='POST')
- def allocate_address(self, domain=None):
+ def allocate_address(self, domain=None, dry_run=False):
"""
Allocate a new Elastic IP address and associate it with your account.
@@ -1465,6 +1654,9 @@ class EC2Connection(AWSQueryConnection):
will be allocated to VPC . Will return address object with
allocation_id.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.address.Address`
:return: The newly allocated Address
"""
@@ -1473,12 +1665,15 @@ class EC2Connection(AWSQueryConnection):
if domain is not None:
params['Domain'] = domain
+ if dry_run:
+ params['DryRun'] = 'true'
+
return self.get_object('AllocateAddress', params, Address, verb='POST')
def assign_private_ip_addresses(self, network_interface_id=None,
private_ip_addresses=None,
secondary_private_ip_address_count=None,
- allow_reassignment=False):
+ allow_reassignment=False, dry_run=False):
"""
Assigns one or more secondary private IP addresses to a network
interface in Amazon VPC.
@@ -1501,6 +1696,9 @@ class EC2Connection(AWSQueryConnection):
that is already assigned to another network interface or instance
to be reassigned to the specified network interface.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -1519,11 +1717,15 @@ class EC2Connection(AWSQueryConnection):
if allow_reassignment:
params['AllowReassignment'] = 'true'
+ if dry_run:
+ params['DryRun'] = 'true'
+
return self.get_status('AssignPrivateIpAddresses', params, verb='POST')
def associate_address(self, instance_id=None, public_ip=None,
allocation_id=None, network_interface_id=None,
- private_ip_address=None, allow_reassociation=False):
+ private_ip_address=None, allow_reassociation=False,
+ dry_run=False):
"""
Associate an Elastic IP address with a currently running instance.
This requires one of ``public_ip`` or ``allocation_id`` depending
@@ -1556,6 +1758,9 @@ class EC2Connection(AWSQueryConnection):
or instance to be re-associated with the specified instance or
interface.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -1576,9 +1781,13 @@ class EC2Connection(AWSQueryConnection):
if allow_reassociation:
params['AllowReassociation'] = 'true'
+ if dry_run:
+ params['DryRun'] = 'true'
+
return self.get_status('AssociateAddress', params, verb='POST')
- def disassociate_address(self, public_ip=None, association_id=None):
+ def disassociate_address(self, public_ip=None, association_id=None,
+ dry_run=False):
"""
Disassociate an Elastic IP address from a currently running instance.
@@ -1588,6 +1797,9 @@ class EC2Connection(AWSQueryConnection):
:type association_id: string
:param association_id: The association ID for a VPC based elastic ip.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -1598,9 +1810,13 @@ class EC2Connection(AWSQueryConnection):
elif association_id is not None:
params['AssociationId'] = association_id
+ if dry_run:
+ params['DryRun'] = 'true'
+
return self.get_status('DisassociateAddress', params, verb='POST')
- def release_address(self, public_ip=None, allocation_id=None):
+ def release_address(self, public_ip=None, allocation_id=None,
+ dry_run=False):
"""
Free up an Elastic IP address. Pass a public IP address to
release an EC2 Elastic IP address and an AllocationId to
@@ -1620,6 +1836,9 @@ class EC2Connection(AWSQueryConnection):
:type allocation_id: string
:param allocation_id: The Allocation ID for VPC elastic IPs.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -1630,10 +1849,13 @@ class EC2Connection(AWSQueryConnection):
elif allocation_id is not None:
params['AllocationId'] = allocation_id
+ if dry_run:
+ params['DryRun'] = 'true'
+
return self.get_status('ReleaseAddress', params, verb='POST')
def unassign_private_ip_addresses(self, network_interface_id=None,
- private_ip_addresses=None):
+ private_ip_addresses=None, dry_run=False):
"""
Unassigns one or more secondary private IP addresses from a network
interface in Amazon VPC.
@@ -1646,6 +1868,9 @@ class EC2Connection(AWSQueryConnection):
:param private_ip_addresses: Specifies the secondary private IP
addresses that you want to unassign from the network interface.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -1658,12 +1883,15 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, private_ip_addresses,
'PrivateIpAddress')
+ if dry_run:
+ params['DryRun'] = 'true'
+
return self.get_status('UnassignPrivateIpAddresses', params,
verb='POST')
# Volume methods
- def get_all_volumes(self, volume_ids=None, filters=None):
+ def get_all_volumes(self, volume_ids=None, filters=None, dry_run=False):
"""
Get all Volumes associated with the current credentials.
@@ -1682,6 +1910,9 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list of :class:`boto.ec2.volume.Volume`
:return: The requested Volume objects
"""
@@ -1690,12 +1921,14 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, volume_ids, 'VolumeId')
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeVolumes', params,
[('item', Volume)], verb='POST')
def get_all_volume_status(self, volume_ids=None,
max_results=None, next_token=None,
- filters=None):
+ filters=None, dry_run=False):
"""
Retrieve the status of one or more volumes.
@@ -1720,6 +1953,9 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of volume status.
"""
@@ -1732,10 +1968,12 @@ class EC2Connection(AWSQueryConnection):
params['NextToken'] = next_token
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('DescribeVolumeStatus', params,
VolumeStatusSet, verb='POST')
- def enable_volume_io(self, volume_id):
+ def enable_volume_io(self, volume_id, dry_run=False):
"""
Enables I/O operations for a volume that had I/O operations
disabled because the data on the volume was potentially inconsistent.
@@ -1743,14 +1981,19 @@ class EC2Connection(AWSQueryConnection):
:type volume_id: str
:param volume_id: The ID of the volume.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'VolumeId': volume_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('EnableVolumeIO', params, verb='POST')
def get_volume_attribute(self, volume_id,
- attribute='autoEnableIO'):
+ attribute='autoEnableIO', dry_run=False):
"""
Describes attribute of the volume.
@@ -1762,14 +2005,20 @@ class EC2Connection(AWSQueryConnection):
* autoEnableIO
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list of :class:`boto.ec2.volume.VolumeAttribute`
:return: The requested Volume attribute
"""
params = {'VolumeId': volume_id, 'Attribute': attribute}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('DescribeVolumeAttribute', params,
VolumeAttribute, verb='POST')
- def modify_volume_attribute(self, volume_id, attribute, new_value):
+ def modify_volume_attribute(self, volume_id, attribute, new_value,
+ dry_run=False):
"""
Changes an attribute of an Volume.
@@ -1782,14 +2031,20 @@ class EC2Connection(AWSQueryConnection):
:type new_value: string
:param new_value: The new value of the attribute.
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {'VolumeId': volume_id}
if attribute == 'AutoEnableIO':
params['AutoEnableIO.Value'] = new_value
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('ModifyVolumeAttribute', params, verb='POST')
def create_volume(self, size, zone, snapshot=None,
- volume_type=None, iops=None):
+ volume_type=None, iops=None, dry_run=False):
"""
Create a new EBS Volume.
@@ -1810,6 +2065,10 @@ class EC2Connection(AWSQueryConnection):
:type iops: int
:param iops: The provisioned IOPs you want to associate with
this volume. (optional)
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
if isinstance(zone, Zone):
zone = zone.name
@@ -1824,22 +2083,29 @@ class EC2Connection(AWSQueryConnection):
params['VolumeType'] = volume_type
if iops:
params['Iops'] = str(iops)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateVolume', params, Volume, verb='POST')
- def delete_volume(self, volume_id):
+ def delete_volume(self, volume_id, dry_run=False):
"""
Delete an EBS volume.
:type volume_id: str
:param volume_id: The ID of the volume to be delete.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'VolumeId': volume_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteVolume', params, verb='POST')
- def attach_volume(self, volume_id, instance_id, device):
+ def attach_volume(self, volume_id, instance_id, device, dry_run=False):
"""
Attach an EBS volume to an EC2 instance.
@@ -1854,16 +2120,21 @@ class EC2Connection(AWSQueryConnection):
:param device: The device on the instance through which the
volume will be exposted (e.g. /dev/sdh)
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'InstanceId': instance_id,
'VolumeId': volume_id,
'Device': device}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('AttachVolume', params, verb='POST')
def detach_volume(self, volume_id, instance_id=None,
- device=None, force=False):
+ device=None, force=False, dry_run=False):
"""
Detach an EBS volume from an EC2 instance.
@@ -1888,6 +2159,9 @@ class EC2Connection(AWSQueryConnection):
use this option, you must perform file system check and
repair procedures.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -1898,13 +2172,15 @@ class EC2Connection(AWSQueryConnection):
params['Device'] = device
if force:
params['Force'] = 'true'
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DetachVolume', params, verb='POST')
# Snapshot methods
def get_all_snapshots(self, snapshot_ids=None,
owner=None, restorable_by=None,
- filters=None):
+ filters=None, dry_run=False):
"""
Get all EBS Snapshots associated with the current credentials.
@@ -1935,6 +2211,9 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list of :class:`boto.ec2.snapshot.Snapshot`
:return: The requested Snapshot objects
"""
@@ -1947,10 +2226,12 @@ class EC2Connection(AWSQueryConnection):
params['RestorableBy'] = restorable_by
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeSnapshots', params,
[('item', Snapshot)], verb='POST')
- def create_snapshot(self, volume_id, description=None):
+ def create_snapshot(self, volume_id, description=None, dry_run=False):
"""
Create a snapshot of an existing EBS Volume.
@@ -1961,26 +2242,38 @@ class EC2Connection(AWSQueryConnection):
:param description: A description of the snapshot.
Limited to 255 characters.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.snapshot.Snapshot`
:return: The created Snapshot object
"""
params = {'VolumeId': volume_id}
if description:
params['Description'] = description[0:255]
+ if dry_run:
+ params['DryRun'] = 'true'
snapshot = self.get_object('CreateSnapshot', params,
Snapshot, verb='POST')
- volume = self.get_all_volumes([volume_id])[0]
+ volume = self.get_all_volumes([volume_id], dry_run=dry_run)[0]
volume_name = volume.tags.get('Name')
if volume_name:
snapshot.add_tag('Name', volume_name)
return snapshot
- def delete_snapshot(self, snapshot_id):
+ def delete_snapshot(self, snapshot_id, dry_run=False):
+ """
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
+ """
params = {'SnapshotId': snapshot_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteSnapshot', params, verb='POST')
def copy_snapshot(self, source_region, source_snapshot_id,
- description=None):
+ description=None, dry_run=False):
"""
Copies a point-in-time snapshot of an Amazon Elastic Block Store
(Amazon EBS) volume and stores it in Amazon Simple Storage Service
@@ -1999,6 +2292,9 @@ class EC2Connection(AWSQueryConnection):
:type description: str
:param description: A description of the new Amazon EBS snapshot.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: str
:return: The snapshot ID
@@ -2009,6 +2305,8 @@ class EC2Connection(AWSQueryConnection):
}
if description is not None:
params['Description'] = description
+ if dry_run:
+ params['DryRun'] = 'true'
snapshot = self.get_object('CopySnapshot', params, Snapshot,
verb='POST')
return snapshot.id
@@ -2167,7 +2465,8 @@ class EC2Connection(AWSQueryConnection):
snap_found_for_this_time_period = False
def get_snapshot_attribute(self, snapshot_id,
- attribute='createVolumePermission'):
+ attribute='createVolumePermission',
+ dry_run=False):
"""
Get information about an attribute of a snapshot. Only one attribute
can be specified per call.
@@ -2180,18 +2479,24 @@ class EC2Connection(AWSQueryConnection):
* createVolumePermission
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list of :class:`boto.ec2.snapshotattribute.SnapshotAttribute`
:return: The requested Snapshot attribute
"""
params = {'Attribute': attribute}
if snapshot_id:
params['SnapshotId'] = snapshot_id
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('DescribeSnapshotAttribute', params,
SnapshotAttribute, verb='POST')
def modify_snapshot_attribute(self, snapshot_id,
attribute='createVolumePermission',
- operation='add', user_ids=None, groups=None):
+ operation='add', user_ids=None, groups=None,
+ dry_run=False):
"""
Changes an attribute of an image.
@@ -2213,6 +2518,9 @@ class EC2Connection(AWSQueryConnection):
:param groups: The groups to add/remove attributes. The only valid
value at this time is 'all'.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {'SnapshotId': snapshot_id,
'Attribute': attribute,
@@ -2221,10 +2529,13 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, user_ids, 'UserId')
if groups:
self.build_list_params(params, groups, 'UserGroup')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('ModifySnapshotAttribute', params, verb='POST')
def reset_snapshot_attribute(self, snapshot_id,
- attribute='createVolumePermission'):
+ attribute='createVolumePermission',
+ dry_run=False):
"""
Resets an attribute of a snapshot to its default value.
@@ -2234,16 +2545,21 @@ class EC2Connection(AWSQueryConnection):
:type attribute: string
:param attribute: The attribute to reset
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: Whether the operation succeeded or not
"""
params = {'SnapshotId': snapshot_id,
'Attribute': attribute}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('ResetSnapshotAttribute', params, verb='POST')
# Keypair methods
- def get_all_key_pairs(self, keynames=None, filters=None):
+ def get_all_key_pairs(self, keynames=None, filters=None, dry_run=False):
"""
Get all key pairs associated with your account.
@@ -2259,6 +2575,9 @@ class EC2Connection(AWSQueryConnection):
names/values is dependent on the request being performed.
Check the EC2 API guide for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.keypair.KeyPair`
"""
@@ -2267,28 +2586,36 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, keynames, 'KeyName')
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeKeyPairs', params,
[('item', KeyPair)], verb='POST')
- def get_key_pair(self, keyname):
+ def get_key_pair(self, keyname, dry_run=False):
"""
Convenience method to retrieve a specific keypair (KeyPair).
:type keyname: string
:param keyname: The name of the keypair to retrieve
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.keypair.KeyPair`
:return: The KeyPair specified or None if it is not found
"""
try:
- return self.get_all_key_pairs(keynames=[keyname])[0]
+ return self.get_all_key_pairs(
+ keynames=[keyname],
+ dry_run=dry_run
+ )[0]
except self.ResponseError, e:
if e.code == 'InvalidKeyPair.NotFound':
return None
else:
raise
- def create_key_pair(self, key_name):
+ def create_key_pair(self, key_name, dry_run=False):
"""
Create a new key pair for your account.
This will create the key pair within the region you
@@ -2297,25 +2624,36 @@ class EC2Connection(AWSQueryConnection):
:type key_name: string
:param key_name: The name of the new keypair
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.keypair.KeyPair`
:return: The newly created :class:`boto.ec2.keypair.KeyPair`.
The material attribute of the new KeyPair object
will contain the the unencrypted PEM encoded RSA private key.
"""
params = {'KeyName': key_name}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateKeyPair', params, KeyPair, verb='POST')
- def delete_key_pair(self, key_name):
+ def delete_key_pair(self, key_name, dry_run=False):
"""
Delete a key pair from your account.
:type key_name: string
:param key_name: The name of the keypair to delete
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {'KeyName': key_name}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteKeyPair', params, verb='POST')
- def import_key_pair(self, key_name, public_key_material):
+ def import_key_pair(self, key_name, public_key_material, dry_run=False):
"""
mports the public key from an RSA key pair that you created
with a third-party tool.
@@ -2342,6 +2680,9 @@ class EC2Connection(AWSQueryConnection):
the public key material before sending
it to AWS.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.keypair.KeyPair`
:return: A :class:`boto.ec2.keypair.KeyPair` object representing
the newly imported key pair. This object will contain only
@@ -2350,12 +2691,14 @@ class EC2Connection(AWSQueryConnection):
public_key_material = base64.b64encode(public_key_material)
params = {'KeyName': key_name,
'PublicKeyMaterial': public_key_material}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('ImportKeyPair', params, KeyPair, verb='POST')
# SecurityGroup methods
def get_all_security_groups(self, groupnames=None, group_ids=None,
- filters=None):
+ filters=None, dry_run=False):
"""
Get all security groups associated with your account in a region.
@@ -2378,6 +2721,9 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.securitygroup.SecurityGroup`
"""
@@ -2388,11 +2734,13 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, group_ids, 'GroupId')
if filters is not None:
self.build_filter_params(params, filters)
-
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeSecurityGroups', params,
[('item', SecurityGroup)], verb='POST')
- def create_security_group(self, name, description, vpc_id=None):
+ def create_security_group(self, name, description, vpc_id=None,
+ dry_run=False):
"""
Create a new security group for your account.
This will create the security group within the region you
@@ -2408,6 +2756,9 @@ class EC2Connection(AWSQueryConnection):
:param vpc_id: The ID of the VPC to create the security group in,
if any.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.securitygroup.SecurityGroup`
:return: The newly created :class:`boto.ec2.securitygroup.SecurityGroup`.
"""
@@ -2417,6 +2768,9 @@ class EC2Connection(AWSQueryConnection):
if vpc_id is not None:
params['VpcId'] = vpc_id
+ if dry_run:
+ params['DryRun'] = 'true'
+
group = self.get_object('CreateSecurityGroup', params,
SecurityGroup, verb='POST')
group.name = name
@@ -2425,7 +2779,7 @@ class EC2Connection(AWSQueryConnection):
group.vpc_id = vpc_id
return group
- def delete_security_group(self, name=None, group_id=None):
+ def delete_security_group(self, name=None, group_id=None, dry_run=False):
"""
Delete a security group from your account.
@@ -2436,6 +2790,9 @@ class EC2Connection(AWSQueryConnection):
:param group_id: The ID of the security group to delete within
a VPC.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful.
"""
@@ -2446,6 +2803,9 @@ class EC2Connection(AWSQueryConnection):
elif group_id is not None:
params['GroupId'] = group_id
+ if dry_run:
+ params['DryRun'] = 'true'
+
return self.get_status('DeleteSecurityGroup', params, verb='POST')
def authorize_security_group_deprecated(self, group_name,
@@ -2453,7 +2813,7 @@ class EC2Connection(AWSQueryConnection):
src_security_group_owner_id=None,
ip_protocol=None,
from_port=None, to_port=None,
- cidr_ip=None):
+ cidr_ip=None, dry_run=False):
"""
NOTE: This method uses the old-style request parameters
that did not allow a port to be specified when
@@ -2484,6 +2844,9 @@ class EC2Connection(AWSQueryConnection):
:param to_port: The CIDR block you are providing access to.
See http://goo.gl/Yj5QC
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful.
"""
@@ -2500,6 +2863,8 @@ class EC2Connection(AWSQueryConnection):
params['ToPort'] = to_port
if cidr_ip:
params['CidrIp'] = cidr_ip
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('AuthorizeSecurityGroupIngress', params)
def authorize_security_group(self, group_name=None,
@@ -2508,7 +2873,8 @@ class EC2Connection(AWSQueryConnection):
ip_protocol=None,
from_port=None, to_port=None,
cidr_ip=None, group_id=None,
- src_security_group_group_id=None):
+ src_security_group_group_id=None,
+ dry_run=False):
"""
Add a new rule to an existing security group.
You need to pass in either src_security_group_name and
@@ -2551,6 +2917,9 @@ class EC2Connection(AWSQueryConnection):
group you are granting access to. Can be used instead of
src_security_group_name
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful.
"""
@@ -2587,6 +2956,8 @@ class EC2Connection(AWSQueryConnection):
for i, single_cidr_ip in enumerate(cidr_ip):
params['IpPermissions.1.IpRanges.%d.CidrIp' % (i+1)] = \
single_cidr_ip
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('AuthorizeSecurityGroupIngress',
params, verb='POST')
@@ -2597,13 +2968,18 @@ class EC2Connection(AWSQueryConnection):
from_port=None,
to_port=None,
src_group_id=None,
- cidr_ip=None):
+ cidr_ip=None,
+ dry_run=False):
"""
The action adds one or more egress rules to a VPC security
group. Specifically, this action permits instances in a
security group to send traffic to one or more destination
CIDR IP address ranges, or to one or more destination
security groups in the same VPC.
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {
'GroupId': group_id,
@@ -2618,6 +2994,8 @@ class EC2Connection(AWSQueryConnection):
params['IpPermissions.1.Groups.1.GroupId'] = src_group_id
if cidr_ip is not None:
params['IpPermissions.1.IpRanges.1.CidrIp'] = cidr_ip
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('AuthorizeSecurityGroupEgress',
params, verb='POST')
@@ -2627,7 +3005,7 @@ class EC2Connection(AWSQueryConnection):
src_security_group_owner_id=None,
ip_protocol=None,
from_port=None, to_port=None,
- cidr_ip=None):
+ cidr_ip=None, dry_run=False):
"""
NOTE: This method uses the old-style request parameters
that did not allow a port to be specified when
@@ -2664,6 +3042,9 @@ class EC2Connection(AWSQueryConnection):
:param to_port: The CIDR block you are revoking access to.
http://goo.gl/Yj5QC
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful.
"""
@@ -2680,6 +3061,8 @@ class EC2Connection(AWSQueryConnection):
params['ToPort'] = to_port
if cidr_ip:
params['CidrIp'] = cidr_ip
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('RevokeSecurityGroupIngress', params)
def revoke_security_group(self, group_name=None,
@@ -2687,7 +3070,7 @@ class EC2Connection(AWSQueryConnection):
src_security_group_owner_id=None,
ip_protocol=None, from_port=None, to_port=None,
cidr_ip=None, group_id=None,
- src_security_group_group_id=None):
+ src_security_group_group_id=None, dry_run=False):
"""
Remove an existing rule from an existing security group.
You need to pass in either src_security_group_name and
@@ -2730,6 +3113,9 @@ class EC2Connection(AWSQueryConnection):
for which you are revoking access. Can be used instead
of src_security_group_name
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful.
"""
@@ -2760,6 +3146,8 @@ class EC2Connection(AWSQueryConnection):
params['IpPermissions.1.ToPort'] = to_port
if cidr_ip:
params['IpPermissions.1.IpRanges.1.CidrIp'] = cidr_ip
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('RevokeSecurityGroupIngress',
params, verb='POST')
@@ -2769,7 +3157,7 @@ class EC2Connection(AWSQueryConnection):
from_port=None,
to_port=None,
src_group_id=None,
- cidr_ip=None):
+ cidr_ip=None, dry_run=False):
"""
Remove an existing egress rule from an existing VPC security
group. You need to pass in an ip_protocol, from_port and
@@ -2798,6 +3186,9 @@ class EC2Connection(AWSQueryConnection):
:param cidr_ip: The CIDR block you are revoking access to.
See http://goo.gl/Yj5QC
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful.
"""
@@ -2815,6 +3206,8 @@ class EC2Connection(AWSQueryConnection):
params['IpPermissions.1.Groups.1.GroupId'] = src_group_id
if cidr_ip:
params['IpPermissions.1.IpRanges.1.CidrIp'] = cidr_ip
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('RevokeSecurityGroupEgress',
params, verb='POST')
@@ -2822,7 +3215,7 @@ class EC2Connection(AWSQueryConnection):
# Regions
#
- def get_all_regions(self, region_names=None, filters=None):
+ def get_all_regions(self, region_names=None, filters=None, dry_run=False):
"""
Get all available regions for the EC2 service.
@@ -2839,6 +3232,9 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.regioninfo.RegionInfo`
"""
@@ -2847,6 +3243,8 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, region_names, 'RegionName')
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
regions = self.get_list('DescribeRegions', params,
[('item', RegionInfo)], verb='POST')
for region in regions:
@@ -2870,7 +3268,8 @@ class EC2Connection(AWSQueryConnection):
max_duration=None,
max_instance_count=None,
next_token=None,
- max_results=None):
+ max_results=None,
+ dry_run=False):
"""
Describes Reserved Instance offerings that are available for purchase.
@@ -2932,6 +3331,9 @@ class EC2Connection(AWSQueryConnection):
:type max_results: int
:param max_results: Maximum number of offerings to return per call.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of
:class:`boto.ec2.reservedinstance.ReservedInstancesOffering`.
@@ -2968,13 +3370,15 @@ class EC2Connection(AWSQueryConnection):
params['NextToken'] = next_token
if max_results is not None:
params['MaxResults'] = str(max_results)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeReservedInstancesOfferings',
params, [('item', ReservedInstancesOffering)],
verb='POST')
def get_all_reserved_instances(self, reserved_instances_id=None,
- filters=None):
+ filters=None, dry_run=False):
"""
Describes one or more of the Reserved Instances that you purchased.
@@ -2991,6 +3395,9 @@ class EC2Connection(AWSQueryConnection):
names/values is dependent on the request being performed.
Check the EC2 API guide for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.reservedinstance.ReservedInstance`
"""
@@ -3000,12 +3407,15 @@ class EC2Connection(AWSQueryConnection):
'ReservedInstancesId')
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeReservedInstances',
params, [('item', ReservedInstance)], verb='POST')
def purchase_reserved_instance_offering(self,
reserved_instances_offering_id,
- instance_count=1, limit_price=None):
+ instance_count=1, limit_price=None,
+ dry_run=False):
"""
Purchase a Reserved Instance for use with your account.
** CAUTION **
@@ -3025,6 +3435,9 @@ class EC2Connection(AWSQueryConnection):
Must be a tuple of (amount, currency_code), for example:
(100.0, 'USD').
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.reservedinstance.ReservedInstance`
:return: The newly created Reserved Instance
"""
@@ -3034,11 +3447,14 @@ class EC2Connection(AWSQueryConnection):
if limit_price is not None:
params['LimitPrice.Amount'] = str(limit_price[0])
params['LimitPrice.CurrencyCode'] = str(limit_price[1])
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('PurchaseReservedInstancesOffering', params,
ReservedInstance, verb='POST')
- def create_reserved_instances_listing(self, reserved_instances_id, instance_count,
- price_schedules, client_token):
+ def create_reserved_instances_listing(self, reserved_instances_id,
+ instance_count, price_schedules,
+ client_token, dry_run=False):
"""Creates a new listing for Reserved Instances.
Creates a new listing for Amazon EC2 Reserved Instances that will be
@@ -3084,6 +3500,9 @@ class EC2Connection(AWSQueryConnection):
:param client_token: Unique, case-sensitive identifier you provide
to ensure idempotency of the request. Maximum 64 ASCII characters.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of
:class:`boto.ec2.reservedinstance.ReservedInstanceListing`
@@ -3098,17 +3517,23 @@ class EC2Connection(AWSQueryConnection):
price, term = schedule
params['PriceSchedules.%s.Price' % i] = str(price)
params['PriceSchedules.%s.Term' % i] = str(term)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('CreateReservedInstancesListing',
params, [('item', ReservedInstanceListing)], verb='POST')
- def cancel_reserved_instances_listing(
- self, reserved_instances_listing_ids=None):
+ def cancel_reserved_instances_listing(self,
+ reserved_instances_listing_ids=None,
+ dry_run=False):
"""Cancels the specified Reserved Instance listing.
:type reserved_instances_listing_ids: List of strings
:param reserved_instances_listing_ids: The ID of the
Reserved Instance listing to be cancelled.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of
:class:`boto.ec2.reservedinstance.ReservedInstanceListing`
@@ -3118,29 +3543,120 @@ class EC2Connection(AWSQueryConnection):
if reserved_instances_listing_ids is not None:
self.build_list_params(params, reserved_instances_listing_ids,
'ReservedInstancesListingId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('CancelReservedInstancesListing',
params, [('item', ReservedInstanceListing)], verb='POST')
+ def build_configurations_param_list(self, params, target_configurations):
+ for offset, tc in enumerate(target_configurations):
+ prefix = 'ReservedInstancesConfigurationSetItemType.%d.' % offset
+ if tc.availability_zone is not None:
+ params[prefix + 'AvailabilityZone'] = tc.availability_zone
+ if tc.platform is not None:
+ params[prefix + 'Platform'] = tc.platform
+ if tc.instance_count is not None:
+ params[prefix + 'InstanceCount'] = tc.instance_count
+
+ def modify_reserved_instances(self, client_token, reserved_instance_ids,
+ target_configurations):
+ """
+ Modifies the specified Reserved Instances.
+
+ :type client_token: string
+ :param client_token: A unique, case-sensitive, token you provide to
+ ensure idempotency of your modification request.
+
+ :type reserved_instance_ids: List of strings
+ :param reserved_instance_ids: The IDs of the Reserved Instances to
+ modify.
+
+ :type target_configurations: List of :class:`boto.ec2.reservedinstance.ReservedInstancesConfiguration`
+ :param target_configurations: The configuration settings for the
+ modified Reserved Instances.
+
+ :rtype: string
+ :return: The unique ID for the submitted modification request.
+ """
+ params = {
+ 'ClientToken': client_token,
+ }
+ if reserved_instance_ids is not None:
+ self.build_list_params(params, reserved_instance_ids,
+ 'ReservedInstancesId')
+ if target_configurations is not None:
+ self.build_configurations_param_list(params, target_configurations)
+ mrir = self.get_object(
+ 'ModifyReservedInstances',
+ params,
+ ModifyReservedInstancesResult,
+ verb='POST'
+ )
+ return mrir.modification_id
+
+ def describe_reserved_instances_modifications(self,
+ reserved_instances_modification_ids=None, next_token=None,
+ filters=None):
+ """
+ A request to describe the modifications made to Reserved Instances in
+ your account.
+
+ :type reserved_instances_modification_ids: list
+ :param reserved_instances_modification_ids: An optional list of
+ Reserved Instances modification IDs to describe.
+
+ :type next_token: str
+ :param next_token: A string specifying the next paginated set
+ of results to return.
+
+ :type filters: dict
+ :param filters: Optional filters that can be used to limit the
+ results returned. Filters are provided in the form of a
+ dictionary consisting of filter names as the key and
+ filter values as the value. The set of allowable filter
+ names/values is dependent on the request being performed.
+ Check the EC2 API guide for details.
+
+ :rtype: list
+ :return: A list of :class:`boto.ec2.reservedinstance.ReservedInstance`
+ """
+ params = {}
+ if reserved_instances_modification_ids:
+ self.build_list_params(params, reserved_instances_modification_ids,
+ 'ReservedInstancesModificationId')
+ if next_token:
+ params['NextToken'] = next_token
+ if filters:
+ self.build_filter_params(params, filters)
+ return self.get_list('DescribeReservedInstancesModifications',
+ params, [('item', ReservedInstancesModification)],
+ verb='POST')
+
#
# Monitoring
#
- def monitor_instances(self, instance_ids):
+ def monitor_instances(self, instance_ids, dry_run=False):
"""
Enable CloudWatch monitoring for the supplied instances.
:type instance_id: list of strings
:param instance_id: The instance ids
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.instanceinfo.InstanceInfo`
"""
params = {}
self.build_list_params(params, instance_ids, 'InstanceId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('MonitorInstances', params,
[('item', InstanceInfo)], verb='POST')
- def monitor_instance(self, instance_id):
+ def monitor_instance(self, instance_id, dry_run=False):
"""
Deprecated Version, maintained for backward compatibility.
Enable CloudWatch monitoring for the supplied instance.
@@ -3148,27 +3664,35 @@ class EC2Connection(AWSQueryConnection):
:type instance_id: string
:param instance_id: The instance id
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.instanceinfo.InstanceInfo`
"""
- return self.monitor_instances([instance_id])
+ return self.monitor_instances([instance_id], dry_run=dry_run)
- def unmonitor_instances(self, instance_ids):
+ def unmonitor_instances(self, instance_ids, dry_run=False):
"""
Disable CloudWatch monitoring for the supplied instance.
:type instance_id: list of string
:param instance_id: The instance id
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.instanceinfo.InstanceInfo`
"""
params = {}
self.build_list_params(params, instance_ids, 'InstanceId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('UnmonitorInstances', params,
[('item', InstanceInfo)], verb='POST')
- def unmonitor_instance(self, instance_id):
+ def unmonitor_instance(self, instance_id, dry_run=False):
"""
Deprecated Version, maintained for backward compatibility.
Disable CloudWatch monitoring for the supplied instance.
@@ -3176,10 +3700,13 @@ class EC2Connection(AWSQueryConnection):
:type instance_id: string
:param instance_id: The instance id
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.instanceinfo.InstanceInfo`
"""
- return self.unmonitor_instances([instance_id])
+ return self.unmonitor_instances([instance_id], dry_run=dry_run)
#
# Bundle Windows Instances
@@ -3188,7 +3715,7 @@ class EC2Connection(AWSQueryConnection):
def bundle_instance(self, instance_id,
s3_bucket,
s3_prefix,
- s3_upload_policy):
+ s3_upload_policy, dry_run=False):
"""
Bundle Windows instance.
@@ -3205,6 +3732,10 @@ class EC2Connection(AWSQueryConnection):
:param s3_upload_policy: Base64 encoded policy that specifies condition
and permissions for Amazon EC2 to upload the
user's image into Amazon S3.
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {'InstanceId': instance_id,
@@ -3216,10 +3747,13 @@ class EC2Connection(AWSQueryConnection):
params['Storage.S3.AWSAccessKeyId'] = self.aws_access_key_id
signature = s3auth.sign_string(s3_upload_policy)
params['Storage.S3.UploadPolicySignature'] = signature
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('BundleInstance', params,
BundleInstanceTask, verb='POST')
- def get_all_bundle_tasks(self, bundle_ids=None, filters=None):
+ def get_all_bundle_tasks(self, bundle_ids=None, filters=None,
+ dry_run=False):
"""
Retrieve current bundling tasks. If no bundle id is specified, all
tasks are retrieved.
@@ -3238,38 +3772,52 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
- """
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+ """
params = {}
if bundle_ids:
self.build_list_params(params, bundle_ids, 'BundleId')
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeBundleTasks', params,
[('item', BundleInstanceTask)], verb='POST')
- def cancel_bundle_task(self, bundle_id):
+ def cancel_bundle_task(self, bundle_id, dry_run=False):
"""
Cancel a previously submitted bundle task
:type bundle_id: string
:param bundle_id: The identifier of the bundle task to cancel.
- """
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
+ """
params = {'BundleId': bundle_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CancelBundleTask', params,
BundleInstanceTask, verb='POST')
- def get_password_data(self, instance_id):
+ def get_password_data(self, instance_id, dry_run=False):
"""
Get encrypted administrator password for a Windows instance.
:type instance_id: string
:param instance_id: The identifier of the instance to retrieve the
password for.
- """
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
+ """
params = {'InstanceId': instance_id}
+ if dry_run:
+ params['DryRun'] = 'true'
rs = self.get_object('GetPasswordData', params, ResultSet, verb='POST')
return rs.passwordData
@@ -3277,7 +3825,8 @@ class EC2Connection(AWSQueryConnection):
# Cluster Placement Groups
#
- def get_all_placement_groups(self, groupnames=None, filters=None):
+ def get_all_placement_groups(self, groupnames=None, filters=None,
+ dry_run=False):
"""
Get all placement groups associated with your account in a region.
@@ -3296,6 +3845,9 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.placementgroup.PlacementGroup`
"""
@@ -3304,10 +3856,12 @@ class EC2Connection(AWSQueryConnection):
self.build_list_params(params, groupnames, 'GroupName')
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribePlacementGroups', params,
[('item', PlacementGroup)], verb='POST')
- def create_placement_group(self, name, strategy='cluster'):
+ def create_placement_group(self, name, strategy='cluster', dry_run=False):
"""
Create a new placement group for your account.
This will create the placement group within the region you
@@ -3320,21 +3874,32 @@ class EC2Connection(AWSQueryConnection):
:param strategy: The placement strategy of the new placement group.
Currently, the only acceptable value is "cluster".
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'GroupName':name, 'Strategy':strategy}
+ if dry_run:
+ params['DryRun'] = 'true'
group = self.get_status('CreatePlacementGroup', params, verb='POST')
return group
- def delete_placement_group(self, name):
+ def delete_placement_group(self, name, dry_run=False):
"""
Delete a placement group from your account.
:type key_name: string
:param key_name: The name of the keypair to delete
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {'GroupName':name}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeletePlacementGroup', params, verb='POST')
# Tag methods
@@ -3349,7 +3914,7 @@ class EC2Connection(AWSQueryConnection):
params['Tag.%d.Value'%i] = value
i += 1
- def get_all_tags(self, filters=None):
+ def get_all_tags(self, filters=None, dry_run=False):
"""
Retrieve all the metadata tags associated with your account.
@@ -3363,16 +3928,21 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.tag.Tag` objects
"""
params = {}
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeTags', params,
[('item', Tag)], verb='POST')
- def create_tags(self, resource_ids, tags):
+ def create_tags(self, resource_ids, tags, dry_run=False):
"""
Create new metadata tags for the specified resource ids.
@@ -3385,13 +3955,18 @@ class EC2Connection(AWSQueryConnection):
value for that tag should be the empty string
(e.g. '').
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {}
self.build_list_params(params, resource_ids, 'ResourceId')
self.build_tag_param_list(params, tags)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('CreateTags', params, verb='POST')
- def delete_tags(self, resource_ids, tags):
+ def delete_tags(self, resource_ids, tags, dry_run=False):
"""
Delete metadata tags for the specified resource ids.
@@ -3407,17 +3982,22 @@ class EC2Connection(AWSQueryConnection):
for the tag value, all tags with that name will
be deleted.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
if isinstance(tags, list):
tags = {}.fromkeys(tags, None)
params = {}
self.build_list_params(params, resource_ids, 'ResourceId')
self.build_tag_param_list(params, tags)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteTags', params, verb='POST')
# Network Interface methods
- def get_all_network_interfaces(self, filters=None):
+ def get_all_network_interfaces(self, filters=None, dry_run=False):
"""
Retrieve all of the Elastic Network Interfaces (ENI's)
associated with your account.
@@ -3432,17 +4012,22 @@ class EC2Connection(AWSQueryConnection):
being performed. Check the EC2 API guide
for details.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.ec2.networkinterface.NetworkInterface`
"""
params = {}
if filters:
self.build_filter_params(params, filters)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeNetworkInterfaces', params,
[('item', NetworkInterface)], verb='POST')
def create_network_interface(self, subnet_id, private_ip_address=None,
- description=None, groups=None):
+ description=None, groups=None, dry_run=False):
"""
Creates a network interface in the specified subnet.
@@ -3463,6 +4048,9 @@ class EC2Connection(AWSQueryConnection):
This can be either a list of group ID's or a list of
:class:`boto.ec2.securitygroup.SecurityGroup` objects.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: :class:`boto.ec2.networkinterface.NetworkInterface`
:return: The newly created network interface.
"""
@@ -3479,11 +4067,13 @@ class EC2Connection(AWSQueryConnection):
else:
ids.append(group)
self.build_list_params(params, ids, 'SecurityGroupId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateNetworkInterface', params,
NetworkInterface, verb='POST')
def attach_network_interface(self, network_interface_id,
- instance_id, device_index):
+ instance_id, device_index, dry_run=False):
"""
Attaches a network interface to an instance.
@@ -3497,13 +4087,20 @@ class EC2Connection(AWSQueryConnection):
:type device_index: int
:param device_index: The index of the device for the network
interface attachment on the instance.
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {'NetworkInterfaceId': network_interface_id,
'InstanceId': instance_id,
'DeviceIndex': device_index}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('AttachNetworkInterface', params, verb='POST')
- def detach_network_interface(self, attachment_id, force=False):
+ def detach_network_interface(self, attachment_id, force=False,
+ dry_run=False):
"""
Detaches a network interface from an instance.
@@ -3513,21 +4110,31 @@ class EC2Connection(AWSQueryConnection):
:type force: bool
:param force: Set to true to force a detachment.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {'AttachmentId': attachment_id}
if force:
params['Force'] = 'true'
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DetachNetworkInterface', params, verb='POST')
- def delete_network_interface(self, network_interface_id):
+ def delete_network_interface(self, network_interface_id, dry_run=False):
"""
Delete the specified network interface.
:type network_interface_id: str
:param network_interface_id: The ID of the network interface to delete.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {'NetworkInterfaceId': network_interface_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteNetworkInterface', params, verb='POST')
def get_all_vmtypes(self):
@@ -3541,7 +4148,12 @@ class EC2Connection(AWSQueryConnection):
return self.get_list('DescribeVmTypes', params, [('euca:item', VmType)], verb='POST')
def copy_image(self, source_region, source_image_id, name,
- description=None, client_token=None):
+ description=None, client_token=None, dry_run=False):
+ """
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
+ """
params = {
'SourceRegion': source_region,
'SourceImageId': source_image_id,
@@ -3551,29 +4163,48 @@ class EC2Connection(AWSQueryConnection):
params['Description'] = description
if client_token is not None:
params['ClientToken'] = client_token
- image = self.get_object('CopyImage', params, CopyImage,
+ if dry_run:
+ params['DryRun'] = 'true'
+ return self.get_object('CopyImage', params, CopyImage,
verb='POST')
- return image
- def describe_account_attributes(self, attribute_names=None):
+ def describe_account_attributes(self, attribute_names=None, dry_run=False):
+ """
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
+ """
params = {}
if attribute_names is not None:
self.build_list_params(params, attribute_names, 'AttributeName')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeAccountAttributes', params,
[('item', AccountAttribute)], verb='POST')
- def describe_vpc_attribute(self, vpc_id, attribute=None):
+ def describe_vpc_attribute(self, vpc_id, attribute=None, dry_run=False):
+ """
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
+ """
params = {
'VpcId': vpc_id
}
if attribute is not None:
params['Attribute'] = attribute
- attr = self.get_object('DescribeVpcAttribute', params,
+ if dry_run:
+ params['DryRun'] = 'true'
+ return self.get_object('DescribeVpcAttribute', params,
VPCAttribute, verb='POST')
- return attr
def modify_vpc_attribute(self, vpc_id, enable_dns_support=None,
- enable_dns_hostnames=None):
+ enable_dns_hostnames=None, dry_run=False):
+ """
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
+ """
params = {
'VpcId': vpc_id
}
@@ -3583,5 +4214,6 @@ class EC2Connection(AWSQueryConnection):
if enable_dns_hostnames is not None:
params['EnableDnsHostnames.Value'] = (
'true' if enable_dns_hostnames else 'false')
- result = self.get_status('ModifyVpcAttribute', params, verb='POST')
- return result
+ if dry_run:
+ params['DryRun'] = 'true'
+ return self.get_status('ModifyVpcAttribute', params, verb='POST')
diff --git a/boto/ec2/ec2object.py b/boto/ec2/ec2object.py
index 7756bee7..265678c6 100644
--- a/boto/ec2/ec2object.py
+++ b/boto/ec2/ec2object.py
@@ -15,7 +15,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.
@@ -40,7 +40,7 @@ class EC2Object(object):
def endElement(self, name, value, connection):
setattr(self, name, value)
-
+
class TaggedEC2Object(EC2Object):
"""
Any EC2 resource that can be tagged should be represented
@@ -62,7 +62,7 @@ class TaggedEC2Object(EC2Object):
else:
return None
- def add_tag(self, key, value=''):
+ 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
to organize and filter resources. Adding a tag involves a round-trip
@@ -76,12 +76,16 @@ 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})
+ 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
- def remove_tag(self, key, value=None):
+ def remove_tag(self, key, value=None, dry_run=False):
"""
Remove a tag from this object. Removing a tag involves a round-trip
to the EC2 service.
@@ -102,6 +106,10 @@ class TaggedEC2Object(EC2Object):
tags = {key : value}
else:
tags = [key]
- status = self.connection.delete_tags([self.id], tags)
+ status = self.connection.delete_tags(
+ [self.id],
+ tags,
+ dry_run=dry_run
+ )
if key in self.tags:
del self.tags[key]
diff --git a/boto/ec2/image.py b/boto/ec2/image.py
index 376fc869..6b6d9ce9 100644
--- a/boto/ec2/image.py
+++ b/boto/ec2/image.py
@@ -130,7 +130,7 @@ class Image(TaggedEC2Object):
def _update(self, updated):
self.__dict__.update(updated.__dict__)
- def update(self, validate=False):
+ def update(self, validate=False, dry_run=False):
"""
Update the image's state information by making a call to fetch
the current image attributes from the service.
@@ -142,7 +142,7 @@ class Image(TaggedEC2Object):
raise a ValueError exception if no data is
returned from EC2.
"""
- rs = self.connection.get_all_images([self.id])
+ rs = self.connection.get_all_images([self.id], dry_run=dry_run)
if len(rs) > 0:
img = rs[0]
if img.id == self.id:
@@ -162,7 +162,7 @@ class Image(TaggedEC2Object):
private_ip_address=None,
placement_group=None, security_group_ids=None,
additional_info=None, instance_profile_name=None,
- instance_profile_arn=None, tenancy=None):
+ instance_profile_arn=None, tenancy=None, dry_run=False):
"""
Runs this instance.
@@ -295,40 +295,62 @@ class Image(TaggedEC2Object):
additional_info=additional_info,
instance_profile_name=instance_profile_name,
instance_profile_arn=instance_profile_arn,
- tenancy=tenancy)
-
- def deregister(self, delete_snapshot=False):
- return self.connection.deregister_image(self.id, delete_snapshot)
-
- def get_launch_permissions(self):
- img_attrs = self.connection.get_image_attribute(self.id,
- 'launchPermission')
+ tenancy=tenancy, dry_run=dry_run)
+
+ def deregister(self, delete_snapshot=False, dry_run=False):
+ return self.connection.deregister_image(
+ self.id,
+ delete_snapshot,
+ dry_run=dry_run
+ )
+
+ def get_launch_permissions(self, dry_run=False):
+ img_attrs = self.connection.get_image_attribute(
+ self.id,
+ 'launchPermission',
+ dry_run=dry_run
+ )
return img_attrs.attrs
- def set_launch_permissions(self, user_ids=None, group_names=None):
+ def set_launch_permissions(self, user_ids=None, group_names=None,
+ dry_run=False):
return self.connection.modify_image_attribute(self.id,
'launchPermission',
'add',
user_ids,
- group_names)
+ group_names,
+ dry_run=dry_run)
- def remove_launch_permissions(self, user_ids=None, group_names=None):
+ def remove_launch_permissions(self, user_ids=None, group_names=None,
+ dry_run=False):
return self.connection.modify_image_attribute(self.id,
'launchPermission',
'remove',
user_ids,
- group_names)
-
- def reset_launch_attributes(self):
- return self.connection.reset_image_attribute(self.id,
- 'launchPermission')
-
- def get_kernel(self):
- img_attrs =self.connection.get_image_attribute(self.id, 'kernel')
+ group_names,
+ dry_run=dry_run)
+
+ def reset_launch_attributes(self, dry_run=False):
+ return self.connection.reset_image_attribute(
+ self.id,
+ 'launchPermission',
+ dry_run=dry_run
+ )
+
+ def get_kernel(self, dry_run=False):
+ img_attrs =self.connection.get_image_attribute(
+ self.id,
+ 'kernel',
+ dry_run=dry_run
+ )
return img_attrs.kernel
- def get_ramdisk(self):
- img_attrs = self.connection.get_image_attribute(self.id, 'ramdisk')
+ def get_ramdisk(self, dry_run=False):
+ img_attrs = self.connection.get_image_attribute(
+ self.id,
+ 'ramdisk',
+ dry_run=dry_run
+ )
return img_attrs.ramdisk
class ImageAttribute:
diff --git a/boto/ec2/instance.py b/boto/ec2/instance.py
index e0137705..254fe230 100644
--- a/boto/ec2/instance.py
+++ b/boto/ec2/instance.py
@@ -149,9 +149,9 @@ class Reservation(EC2Object):
else:
setattr(self, name, value)
- def stop_all(self):
+ def stop_all(self, dry_run=False):
for instance in self.instances:
- instance.stop()
+ instance.stop(dry_run=dry_run)
class Instance(TaggedEC2Object):
@@ -406,7 +406,7 @@ class Instance(TaggedEC2Object):
def _update(self, updated):
self.__dict__.update(updated.__dict__)
- def update(self, validate=False):
+ def update(self, validate=False, dry_run=False):
"""
Update the instance's state information by making a call to fetch
the current instance attributes from the service.
@@ -418,7 +418,7 @@ class Instance(TaggedEC2Object):
raise a ValueError exception if no data is
returned from EC2.
"""
- rs = self.connection.get_all_reservations([self.id])
+ rs = self.connection.get_all_reservations([self.id], dry_run=dry_run)
if len(rs) > 0:
r = rs[0]
for i in r.instances:
@@ -428,15 +428,15 @@ class Instance(TaggedEC2Object):
raise ValueError('%s is not a valid Instance ID' % self.id)
return self.state
- def terminate(self):
+ def terminate(self, dry_run=False):
"""
Terminate the instance
"""
- rs = self.connection.terminate_instances([self.id])
+ rs = self.connection.terminate_instances([self.id], dry_run=dry_run)
if len(rs) > 0:
self._update(rs[0])
- def stop(self, force=False):
+ def stop(self, force=False, dry_run=False):
"""
Stop the instance
@@ -446,34 +446,38 @@ class Instance(TaggedEC2Object):
:rtype: list
:return: A list of the instances stopped
"""
- rs = self.connection.stop_instances([self.id], force)
+ rs = self.connection.stop_instances([self.id], force, dry_run=dry_run)
if len(rs) > 0:
self._update(rs[0])
- def start(self):
+ def start(self, dry_run=False):
"""
Start the instance.
"""
- rs = self.connection.start_instances([self.id])
+ rs = self.connection.start_instances([self.id], dry_run=dry_run)
if len(rs) > 0:
self._update(rs[0])
- def reboot(self):
- return self.connection.reboot_instances([self.id])
+ def reboot(self, dry_run=False):
+ return self.connection.reboot_instances([self.id], dry_run=dry_run)
- def get_console_output(self):
+ def get_console_output(self, dry_run=False):
"""
Retrieves the console output for the instance.
:rtype: :class:`boto.ec2.instance.ConsoleOutput`
:return: The console output as a ConsoleOutput object
"""
- return self.connection.get_console_output(self.id)
+ return self.connection.get_console_output(self.id, dry_run=dry_run)
- def confirm_product(self, product_code):
- return self.connection.confirm_product_instance(self.id, product_code)
+ def confirm_product(self, product_code, dry_run=False):
+ return self.connection.confirm_product_instance(
+ self.id,
+ product_code,
+ dry_run=dry_run
+ )
- def use_ip(self, ip_address):
+ def use_ip(self, ip_address, dry_run=False):
"""
Associates an Elastic IP to the instance.
@@ -488,15 +492,19 @@ class Instance(TaggedEC2Object):
if isinstance(ip_address, Address):
ip_address = ip_address.public_ip
- return self.connection.associate_address(self.id, ip_address)
+ return self.connection.associate_address(
+ self.id,
+ ip_address,
+ dry_run=dry_run
+ )
- def monitor(self):
- return self.connection.monitor_instance(self.id)
+ def monitor(self, dry_run=False):
+ return self.connection.monitor_instance(self.id, dry_run=dry_run)
- def unmonitor(self):
- return self.connection.unmonitor_instance(self.id)
+ def unmonitor(self, dry_run=False):
+ return self.connection.unmonitor_instance(self.id, dry_run=dry_run)
- def get_attribute(self, attribute):
+ def get_attribute(self, attribute, dry_run=False):
"""
Gets an attribute from this instance.
@@ -521,9 +529,13 @@ class Instance(TaggedEC2Object):
:return: An InstanceAttribute object representing the value of the
attribute requested
"""
- return self.connection.get_instance_attribute(self.id, attribute)
+ return self.connection.get_instance_attribute(
+ self.id,
+ attribute,
+ dry_run=dry_run
+ )
- def modify_attribute(self, attribute, value):
+ def modify_attribute(self, attribute, value, dry_run=False):
"""
Changes an attribute of this instance
@@ -546,10 +558,14 @@ class Instance(TaggedEC2Object):
:rtype: bool
:return: Whether the operation succeeded or not
"""
- return self.connection.modify_instance_attribute(self.id, attribute,
- value)
-
- def reset_attribute(self, attribute):
+ return self.connection.modify_instance_attribute(
+ self.id,
+ attribute,
+ value,
+ dry_run=dry_run
+ )
+
+ def reset_attribute(self, attribute, dry_run=False):
"""
Resets an attribute of this instance to its default value.
@@ -560,12 +576,14 @@ class Instance(TaggedEC2Object):
:rtype: bool
:return: Whether the operation succeeded or not
"""
- return self.connection.reset_instance_attribute(self.id, attribute)
-
- def create_image(
- self, name,
- description=None, no_reboot=False
- ):
+ return self.connection.reset_instance_attribute(
+ self.id,
+ attribute,
+ dry_run=dry_run
+ )
+
+ def create_image(self, name, description=None, no_reboot=False,
+ dry_run=False):
"""
Will create an AMI from the instance in the running or stopped
state.
@@ -587,7 +605,13 @@ class Instance(TaggedEC2Object):
:rtype: string
:return: The new image id
"""
- return self.connection.create_image(self.id, name, description, no_reboot)
+ return self.connection.create_image(
+ self.id,
+ name,
+ description,
+ no_reboot,
+ dry_run=dry_run
+ )
class ConsoleOutput:
diff --git a/boto/ec2/keypair.py b/boto/ec2/keypair.py
index 65c95908..c15a0984 100644
--- a/boto/ec2/keypair.py
+++ b/boto/ec2/keypair.py
@@ -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.
@@ -28,7 +28,7 @@ from boto.ec2.ec2object import EC2Object
from boto.exception import BotoClientError
class KeyPair(EC2Object):
-
+
def __init__(self, connection=None):
EC2Object.__init__(self, connection)
self.name = None
@@ -48,20 +48,20 @@ class KeyPair(EC2Object):
else:
setattr(self, name, value)
- def delete(self):
+ def delete(self, dry_run=False):
"""
Delete the KeyPair.
-
+
:rtype: bool
:return: True if successful, otherwise False.
"""
- return self.connection.delete_key_pair(self.name)
+ return self.connection.delete_key_pair(self.name, dry_run=dry_run)
def save(self, directory_path):
"""
Save the material (the unencrypted PEM encoded RSA private key)
of a newly created KeyPair to a local file.
-
+
:type directory_path: string
:param directory_path: The fully qualified path to the directory
in which the keypair will be saved. The
@@ -71,7 +71,7 @@ class KeyPair(EC2Object):
name already exists in the directory, an
exception will be raised and the old file
will not be overwritten.
-
+
:rtype: bool
:return: True if successful.
"""
@@ -88,7 +88,7 @@ class KeyPair(EC2Object):
else:
raise BotoClientError('KeyPair contains no material')
- def copy_to_region(self, region):
+ def copy_to_region(self, region, dry_run=False):
"""
Create a new key pair of the same new in another region.
Note that the new key pair will use a different ssh
@@ -106,7 +106,7 @@ class KeyPair(EC2Object):
raise BotoClientError('Unable to copy to the same Region')
conn_params = self.connection.get_params()
rconn = region.connect(**conn_params)
- kp = rconn.create_key_pair(self.name)
+ kp = rconn.create_key_pair(self.name, dry_run=dry_run)
return kp
diff --git a/boto/ec2/networkinterface.py b/boto/ec2/networkinterface.py
index 6ffc79af..98368050 100644
--- a/boto/ec2/networkinterface.py
+++ b/boto/ec2/networkinterface.py
@@ -23,6 +23,7 @@
"""
Represents an EC2 Elastic Network Interface
"""
+from boto.exception import BotoClientError
from boto.ec2.ec2object import TaggedEC2Object
from boto.resultset import ResultSet
from boto.ec2.group import Group
@@ -165,8 +166,11 @@ class NetworkInterface(TaggedEC2Object):
else:
setattr(self, name, value)
- def delete(self):
- return self.connection.delete_network_interface(self.id)
+ def delete(self, dry_run=False):
+ return self.connection.delete_network_interface(
+ self.id,
+ dry_run=dry_run
+ )
class PrivateIPAddress(object):
@@ -196,13 +200,15 @@ class NetworkInterfaceCollection(list):
def build_list_params(self, params, prefix=''):
for i, spec in enumerate(self):
- full_prefix = '%sNetworkInterface.%s.' % (prefix, i+1)
+ full_prefix = '%sNetworkInterface.%s.' % (prefix, i)
if spec.network_interface_id is not None:
params[full_prefix + 'NetworkInterfaceId'] = \
str(spec.network_interface_id)
if spec.device_index is not None:
params[full_prefix + 'DeviceIndex'] = \
str(spec.device_index)
+ else:
+ params[full_prefix + 'DeviceIndex'] = 0
if spec.subnet_id is not None:
params[full_prefix + 'SubnetId'] = str(spec.subnet_id)
if spec.description is not None:
@@ -218,20 +224,47 @@ class NetworkInterfaceCollection(list):
str(spec.private_ip_address)
if spec.groups is not None:
for j, group_id in enumerate(spec.groups):
- query_param_key = '%sSecurityGroupId.%s' % (full_prefix, j+1)
+ query_param_key = '%sSecurityGroupId.%s' % (full_prefix, j)
params[query_param_key] = str(group_id)
if spec.private_ip_addresses is not None:
for k, ip_addr in enumerate(spec.private_ip_addresses):
query_param_key_prefix = (
- '%sPrivateIpAddresses.%s' % (full_prefix, k+1))
+ '%sPrivateIpAddresses.%s' % (full_prefix, k))
params[query_param_key_prefix + '.PrivateIpAddress'] = \
str(ip_addr.private_ip_address)
if ip_addr.primary is not None:
params[query_param_key_prefix + '.Primary'] = \
'true' if ip_addr.primary else 'false'
+
+ # Associating Public IPs have special logic around them:
+ #
+ # * Only assignable on an device_index of ``0``
+ # * Only on one interface
+ # * Only if there are no other interfaces being created
+ # * Only if it's a new interface (which we can't really guard
+ # against)
+ #
+ # More details on http://docs.aws.amazon.com/AWSEC2/latest/APIReference/ApiReference-query-RunInstances.html
if spec.associate_public_ip_address is not None:
- params[full_prefix + 'AssociatePublicIpAddress'] = \
- 'true' if spec.associate_public_ip_address else 'false'
+ 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'."
+ )
+
+ if len(self) > 1:
+ raise BotoClientError(
+ "Only one interface can be provided when using " + \
+ "'associate_public_ip_address'."
+ )
+
+ key = full_prefix + 'AssociatePublicIpAddress'
+
+ if spec.associate_public_ip_address:
+ params[key] = 'true'
+ else:
+ params[key] = 'false'
class NetworkInterfaceSpecification(object):
diff --git a/boto/ec2/placementgroup.py b/boto/ec2/placementgroup.py
index e1bbea62..79bd4c46 100644
--- a/boto/ec2/placementgroup.py
+++ b/boto/ec2/placementgroup.py
@@ -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,7 +25,7 @@ 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):
EC2Object.__init__(self, connection)
self.name = name
@@ -45,7 +45,10 @@ class PlacementGroup(EC2Object):
else:
setattr(self, name, value)
- def delete(self):
- return self.connection.delete_placement_group(self.name)
+ def delete(self, dry_run=False):
+ return self.connection.delete_placement_group(
+ self.name,
+ dry_run=dry_run
+ )
diff --git a/boto/ec2/reservedinstance.py b/boto/ec2/reservedinstance.py
index d92f1686..b96566b6 100644
--- a/boto/ec2/reservedinstance.py
+++ b/boto/ec2/reservedinstance.py
@@ -21,6 +21,7 @@
from boto.resultset import ResultSet
from boto.ec2.ec2object import EC2Object
+from boto.utils import parse_ts
class ReservedInstancesOffering(EC2Object):
@@ -89,8 +90,12 @@ class ReservedInstancesOffering(EC2Object):
print '\tUsage Price=%s' % self.usage_price
print '\tDescription=%s' % self.description
- def purchase(self, instance_count=1):
- return self.connection.purchase_reserved_instance_offering(self.id, instance_count)
+ def purchase(self, instance_count=1, dry_run=False):
+ return self.connection.purchase_reserved_instance_offering(
+ self.id,
+ instance_count,
+ dry_run=dry_run
+ )
class RecurringCharge(object):
@@ -225,3 +230,113 @@ class PriceSchedule(object):
self.active = True if value == 'true' else False
else:
setattr(self, name, value)
+
+
+class ReservedInstancesConfiguration(object):
+ def __init__(self, connection=None, availability_zone=None, platform=None,
+ instance_count=None):
+ self.connection = connection
+ self.availability_zone = availability_zone
+ self.platform = platform
+ self.instance_count = instance_count
+
+ def startElement(self, name, attrs, connection):
+ return None
+
+ def endElement(self, name, value, connection):
+ if name == 'availabilityZone':
+ self.availability_zone = value
+ elif name == 'platform':
+ self.platform = value
+ elif name == 'instanceCount':
+ self.instance_count = int(value)
+ else:
+ setattr(self, name, value)
+
+
+class ModifyReservedInstancesResult(object):
+ def __init__(self, connection=None, modification_id=None):
+ self.connection = connection
+ self.modification_id = modification_id
+
+ def startElement(self, name, attrs, connection):
+ return None
+
+ def endElement(self, name, value, connection):
+ if name == 'reservedInstancesModificationId':
+ self.modification_id = value
+ else:
+ setattr(self, name, value)
+
+
+class ModificationResult(object):
+ def __init__(self, connection=None, modification_id=None,
+ availability_zone=None, platform=None, instance_count=None):
+ self.connection = connection
+ self.modification_id = modification_id
+ self.availability_zone = availability_zone
+ self.platform = platform
+ self.instance_count = instance_count
+
+ def startElement(self, name, attrs, connection):
+ return None
+
+ def endElement(self, name, value, connection):
+ if name == 'reservedInstancesModificationId':
+ self.modification_id = value
+ elif name == 'availabilityZone':
+ self.availability_zone = value
+ elif name == 'platform':
+ self.platform = value
+ elif name == 'instanceCount':
+ self.instance_count = int(value)
+ else:
+ setattr(self, name, value)
+
+
+class ReservedInstancesModification(object):
+ def __init__(self, connection=None, modification_id=None,
+ reserved_instances=None, modification_results=None,
+ create_date=None, update_date=None, effective_date=None,
+ status=None, status_message=None, client_token=None):
+ self.connection = connection
+ self.modification_id = modification_id
+ self.reserved_instances = reserved_instances
+ self.modification_results = modification_results
+ self.create_date = create_date
+ self.update_date = update_date
+ self.effective_date = effective_date
+ self.status = status
+ self.status_message = status_message
+ self.client_token = client_token
+
+ def startElement(self, name, attrs, connection):
+ if name == 'reservedInstancesSet':
+ self.reserved_instances = ResultSet([
+ ('item', ReservedInstance)
+ ])
+ return self.reserved_instances
+ elif name == 'modificationResultSet':
+ self.modification_results = ResultSet([
+ ('item', ModificationResult)
+ ])
+ return self.modification_results
+ return None
+
+ def endElement(self, name, value, connection):
+ if name == 'reservedInstancesModificationId':
+ self.modification_id = value
+ elif name == 'createDate':
+ self.create_date = parse_ts(value)
+ elif name == 'updateDate':
+ self.update_date = parse_ts(value)
+ elif name == 'effectiveDate':
+ self.effective_date = parse_ts(value)
+ elif name == 'status':
+ self.status = value
+ elif name == 'statusMessage':
+ self.status_message = value
+ elif name == 'clientToken':
+ self.client_token = value
+ else:
+ setattr(self, name, value)
diff --git a/boto/ec2/securitygroup.py b/boto/ec2/securitygroup.py
index 731c2390..3d93faa2 100644
--- a/boto/ec2/securitygroup.py
+++ b/boto/ec2/securitygroup.py
@@ -82,14 +82,21 @@ class SecurityGroup(TaggedEC2Object):
else:
setattr(self, name, value)
- def delete(self):
+ def delete(self, dry_run=False):
if self.vpc_id:
- return self.connection.delete_security_group(group_id=self.id)
+ return self.connection.delete_security_group(
+ group_id=self.id,
+ dry_run=dry_run
+ )
else:
- return self.connection.delete_security_group(self.name)
+ return self.connection.delete_security_group(
+ self.name,
+ dry_run=dry_run
+ )
def add_rule(self, ip_protocol, from_port, to_port,
- src_group_name, src_group_owner_id, cidr_ip, src_group_group_id):
+ src_group_name, src_group_owner_id, cidr_ip,
+ src_group_group_id, dry_run=False):
"""
Add a rule to the SecurityGroup object. Note that this method
only changes the local version of the object. No information
@@ -100,10 +107,17 @@ class SecurityGroup(TaggedEC2Object):
rule.from_port = from_port
rule.to_port = to_port
self.rules.append(rule)
- rule.add_grant(src_group_name, src_group_owner_id, cidr_ip, src_group_group_id)
+ rule.add_grant(
+ src_group_name,
+ src_group_owner_id,
+ cidr_ip,
+ src_group_group_id,
+ dry_run=dry_run
+ )
def remove_rule(self, ip_protocol, from_port, to_port,
- src_group_name, src_group_owner_id, cidr_ip, src_group_group_id):
+ src_group_name, src_group_owner_id, cidr_ip,
+ src_group_group_id, dry_run=False):
"""
Remove a rule to the SecurityGroup object. Note that this method
only changes the local version of the object. No information
@@ -122,12 +136,12 @@ class SecurityGroup(TaggedEC2Object):
if grant.cidr_ip == cidr_ip:
target_grant = grant
if target_grant:
- rule.grants.remove(target_grant)
+ rule.grants.remove(target_grant, dry_run=dry_run)
if len(rule.grants) == 0:
- self.rules.remove(target_rule)
+ self.rules.remove(target_rule, dry_run=dry_run)
def authorize(self, ip_protocol=None, from_port=None, to_port=None,
- cidr_ip=None, src_group=None):
+ cidr_ip=None, src_group=None, dry_run=False):
"""
Add a new rule to this security group.
You need to pass in either src_group_name
@@ -182,17 +196,19 @@ class SecurityGroup(TaggedEC2Object):
to_port,
cidr_ip,
group_id,
- src_group_group_id)
+ src_group_group_id,
+ dry_run=dry_run)
if status:
if not isinstance(cidr_ip, list):
cidr_ip = [cidr_ip]
for single_cidr_ip in cidr_ip:
self.add_rule(ip_protocol, from_port, to_port, src_group_name,
- src_group_owner_id, single_cidr_ip, src_group_group_id)
+ src_group_owner_id, single_cidr_ip,
+ src_group_group_id, dry_run=dry_run)
return status
def revoke(self, ip_protocol=None, from_port=None, to_port=None,
- cidr_ip=None, src_group=None):
+ cidr_ip=None, src_group=None, dry_run=False):
group_name = None
if not self.vpc_id:
group_name = self.name
@@ -220,13 +236,15 @@ class SecurityGroup(TaggedEC2Object):
to_port,
cidr_ip,
group_id,
- src_group_group_id)
+ src_group_group_id,
+ dry_run=dry_run)
if status:
self.remove_rule(ip_protocol, from_port, to_port, src_group_name,
- src_group_owner_id, cidr_ip, src_group_group_id)
+ src_group_owner_id, cidr_ip, src_group_group_id,
+ dry_run=dry_run)
return status
- def copy_to_region(self, region, name=None):
+ def copy_to_region(self, region, name=None, dry_run=False):
"""
Create a copy of this security group in another region.
Note that the new security group will be a separate entity
@@ -247,7 +265,11 @@ class SecurityGroup(TaggedEC2Object):
raise BotoClientError('Unable to copy to the same Region')
conn_params = self.connection.get_params()
rconn = region.connect(**conn_params)
- sg = rconn.create_security_group(name or self.name, self.description)
+ sg = rconn.create_security_group(
+ name or self.name,
+ self.description,
+ dry_run=dry_run
+ )
source_groups = []
for rule in self.rules:
for grant in rule.grants:
@@ -255,13 +277,14 @@ class SecurityGroup(TaggedEC2Object):
if grant_nom:
if grant_nom not in source_groups:
source_groups.append(grant_nom)
- sg.authorize(None, None, None, None, grant)
+ sg.authorize(None, None, None, None, grant,
+ dry_run=dry_run)
else:
sg.authorize(rule.ip_protocol, rule.from_port, rule.to_port,
- grant.cidr_ip)
+ grant.cidr_ip, dry_run=dry_run)
return sg
- def instances(self):
+ def instances(self, dry_run=False):
"""
Find all of the current instances that are running within this
security group.
@@ -272,11 +295,13 @@ class SecurityGroup(TaggedEC2Object):
rs = []
if self.vpc_id:
rs.extend(self.connection.get_all_reservations(
- filters={'instance.group-id': self.id}
+ filters={'instance.group-id': self.id},
+ dry_run=dry_run
))
else:
rs.extend(self.connection.get_all_reservations(
- filters={'group-id': self.id}
+ filters={'group-id': self.id},
+ dry_run=dry_run
))
instances = [i for r in rs for i in r.instances]
return instances
diff --git a/boto/ec2/snapshot.py b/boto/ec2/snapshot.py
index d2c4b2b9..24bffe6b 100644
--- a/boto/ec2/snapshot.py
+++ b/boto/ec2/snapshot.py
@@ -15,7 +15,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.
@@ -27,9 +27,9 @@ from boto.ec2.ec2object import TaggedEC2Object
from boto.ec2.zone import Zone
class Snapshot(TaggedEC2Object):
-
+
AttrName = 'createVolumePermission'
-
+
def __init__(self, connection=None):
TaggedEC2Object.__init__(self, connection)
self.id = None
@@ -72,7 +72,7 @@ class Snapshot(TaggedEC2Object):
self.progress = updated.progress
self.status = updated.status
- def update(self, validate=False):
+ def update(self, validate=False, dry_run=False):
"""
Update the data associated with this snapshot by querying EC2.
@@ -83,39 +83,49 @@ class Snapshot(TaggedEC2Object):
raise a ValueError exception if no data is
returned from EC2.
"""
- rs = self.connection.get_all_snapshots([self.id])
+ rs = self.connection.get_all_snapshots([self.id], dry_run=dry_run)
if len(rs) > 0:
self._update(rs[0])
elif validate:
raise ValueError('%s is not a valid Snapshot ID' % self.id)
return self.progress
-
- def delete(self):
- return self.connection.delete_snapshot(self.id)
- def get_permissions(self):
- attrs = self.connection.get_snapshot_attribute(self.id, self.AttrName)
+ def delete(self, dry_run=False):
+ return self.connection.delete_snapshot(self.id, dry_run=dry_run)
+
+ def get_permissions(self, dry_run=False):
+ attrs = self.connection.get_snapshot_attribute(
+ self.id,
+ self.AttrName,
+ dry_run=dry_run
+ )
return attrs.attrs
- def share(self, user_ids=None, groups=None):
+ def share(self, user_ids=None, groups=None, dry_run=False):
return self.connection.modify_snapshot_attribute(self.id,
self.AttrName,
'add',
user_ids,
- groups)
+ groups,
+ dry_run=dry_run)
- def unshare(self, user_ids=None, groups=None):
+ def unshare(self, user_ids=None, groups=None, dry_run=False):
return self.connection.modify_snapshot_attribute(self.id,
self.AttrName,
'remove',
user_ids,
- groups)
-
- def reset_permissions(self):
- return self.connection.reset_snapshot_attribute(self.id,
- self.AttrName)
-
- def create_volume(self, zone, size=None, volume_type=None, iops=None):
+ groups,
+ dry_run=dry_run)
+
+ def reset_permissions(self, dry_run=False):
+ return self.connection.reset_snapshot_attribute(
+ self.id,
+ self.AttrName,
+ dry_run=dry_run
+ )
+
+ def create_volume(self, zone, size=None, volume_type=None, iops=None,
+ dry_run=False):
"""
Create a new EBS Volume from this Snapshot
@@ -136,7 +146,14 @@ class Snapshot(TaggedEC2Object):
"""
if isinstance(zone, Zone):
zone = zone.name
- return self.connection.create_volume(size, zone, self.id, volume_type, iops)
+ return self.connection.create_volume(
+ size,
+ zone,
+ self.id,
+ volume_type,
+ iops,
+ dry_run=dry_run
+ )
class SnapshotAttribute:
@@ -167,4 +184,4 @@ class SnapshotAttribute:
setattr(self, name, value)
-
+
diff --git a/boto/ec2/spotdatafeedsubscription.py b/boto/ec2/spotdatafeedsubscription.py
index 9b820a3e..1b30a99f 100644
--- a/boto/ec2/spotdatafeedsubscription.py
+++ b/boto/ec2/spotdatafeedsubscription.py
@@ -26,7 +26,7 @@ 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):
EC2Object.__init__(self, connection)
@@ -45,7 +45,7 @@ class SpotDatafeedSubscription(EC2Object):
return self.fault
else:
return None
-
+
def endElement(self, name, value, connection):
if name == 'ownerId':
self.owner_id = value
@@ -58,6 +58,8 @@ class SpotDatafeedSubscription(EC2Object):
else:
setattr(self, name, value)
- def delete(self):
- return self.connection.delete_spot_datafeed_subscription()
+ def delete(self, dry_run=False):
+ return self.connection.delete_spot_datafeed_subscription(
+ dry_run=dry_run
+ )
diff --git a/boto/ec2/spotinstancerequest.py b/boto/ec2/spotinstancerequest.py
index 54fba1d6..c5b8bc95 100644
--- a/boto/ec2/spotinstancerequest.py
+++ b/boto/ec2/spotinstancerequest.py
@@ -184,5 +184,8 @@ class SpotInstanceRequest(TaggedEC2Object):
else:
setattr(self, name, value)
- def cancel(self):
- self.connection.cancel_spot_instance_requests([self.id])
+ def cancel(self, dry_run=False):
+ self.connection.cancel_spot_instance_requests(
+ [self.id],
+ dry_run=dry_run
+ )
diff --git a/boto/ec2/volume.py b/boto/ec2/volume.py
index bc5befc7..2127b260 100644
--- a/boto/ec2/volume.py
+++ b/boto/ec2/volume.py
@@ -98,7 +98,7 @@ class Volume(TaggedEC2Object):
def _update(self, updated):
self.__dict__.update(updated.__dict__)
- def update(self, validate=False):
+ def update(self, validate=False, dry_run=False):
"""
Update the data associated with this volume by querying EC2.
@@ -110,7 +110,10 @@ class Volume(TaggedEC2Object):
returned from EC2.
"""
# Check the resultset since Eucalyptus ignores the volumeId param
- unfiltered_rs = self.connection.get_all_volumes([self.id])
+ unfiltered_rs = self.connection.get_all_volumes(
+ [self.id],
+ dry_run=dry_run
+ )
rs = [x for x in unfiltered_rs if x.id == self.id]
if len(rs) > 0:
self._update(rs[0])
@@ -118,16 +121,16 @@ class Volume(TaggedEC2Object):
raise ValueError('%s is not a valid Volume ID' % self.id)
return self.status
- def delete(self):
+ def delete(self, dry_run=False):
"""
Delete this EBS volume.
:rtype: bool
:return: True if successful
"""
- return self.connection.delete_volume(self.id)
+ return self.connection.delete_volume(self.id, dry_run=dry_run)
- def attach(self, instance_id, device):
+ def attach(self, instance_id, device, dry_run=False):
"""
Attach this EBS volume to an EC2 instance.
@@ -142,9 +145,14 @@ class Volume(TaggedEC2Object):
:rtype: bool
:return: True if successful
"""
- return self.connection.attach_volume(self.id, instance_id, device)
-
- def detach(self, force=False):
+ return self.connection.attach_volume(
+ self.id,
+ instance_id,
+ device,
+ dry_run=dry_run
+ )
+
+ def detach(self, force=False, dry_run=False):
"""
Detach this EBS volume from an EC2 instance.
@@ -167,10 +175,15 @@ class Volume(TaggedEC2Object):
device = None
if self.attach_data:
device = self.attach_data.device
- return self.connection.detach_volume(self.id, instance_id,
- device, force)
-
- def create_snapshot(self, description=None):
+ return self.connection.detach_volume(
+ self.id,
+ instance_id,
+ device,
+ force,
+ dry_run=dry_run
+ )
+
+ def create_snapshot(self, description=None, dry_run=False):
"""
Create a snapshot of this EBS Volume.
@@ -181,7 +194,11 @@ class Volume(TaggedEC2Object):
:rtype: :class:`boto.ec2.snapshot.Snapshot`
:return: The created Snapshot object
"""
- return self.connection.create_snapshot(self.id, description)
+ return self.connection.create_snapshot(
+ self.id,
+ description,
+ dry_run=dry_run
+ )
def volume_state(self):
"""
@@ -198,7 +215,7 @@ class Volume(TaggedEC2Object):
state = self.attach_data.status
return state
- def snapshots(self, owner=None, restorable_by=None):
+ def snapshots(self, owner=None, restorable_by=None, dry_run=False):
"""
Get all snapshots related to this volume. Note that this requires
that all available snapshots for the account be retrieved from EC2
@@ -221,8 +238,11 @@ class Volume(TaggedEC2Object):
:return: The requested Snapshot objects
"""
- rs = self.connection.get_all_snapshots(owner=owner,
- restorable_by=restorable_by)
+ rs = self.connection.get_all_snapshots(
+ owner=owner,
+ restorable_by=restorable_by,
+ dry_run=dry_run
+ )
mine = []
for snap in rs:
if snap.volume_id == self.id:
diff --git a/boto/opsworks/layer1.py b/boto/opsworks/layer1.py
index e3a11cd9..ba147cd0 100644
--- a/boto/opsworks/layer1.py
+++ b/boto/opsworks/layer1.py
@@ -53,7 +53,7 @@ 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 late August 2013.
+ we expect to change the default value to 11.4 in September 2013.
"""
APIVersion = "2013-02-18"
DefaultRegionName = "us-east-1"
@@ -108,13 +108,14 @@ class OpsWorksConnection(AWSQueryConnection):
body=json.dumps(params))
def clone_stack(self, source_stack_id, service_role_arn, name=None,
- region=None, attributes=None,
+ region=None, vpc_id=None, attributes=None,
default_instance_profile_arn=None, default_os=None,
hostname_theme=None, default_availability_zone=None,
- custom_json=None, configuration_manager=None,
- use_custom_cookbooks=None, custom_cookbooks_source=None,
- default_ssh_key_name=None, clone_permissions=None,
- clone_app_ids=None, default_root_device_type=None):
+ default_subnet_id=None, custom_json=None,
+ configuration_manager=None, use_custom_cookbooks=None,
+ custom_cookbooks_source=None, default_ssh_key_name=None,
+ clone_permissions=None, clone_app_ids=None,
+ default_root_device_type=None):
"""
Creates a clone of a specified stack. For more information,
see `Clone a Stack`_.
@@ -129,6 +130,36 @@ class OpsWorksConnection(AWSQueryConnection):
:param region: The cloned stack AWS region, such as "us-east-1". For
more information about AWS regions, see `Regions and Endpoints`_.
+ :type vpc_id: string
+ :param vpc_id: The ID of the VPC that the cloned stack is to be
+ launched into. It must be in the specified region. All instances
+ will be launched into this VPC, and you cannot change the ID later.
+
+ + If your account supports EC2 Classic, the default value is no VPC.
+ + If you account does not support EC2 Classic, the default value is the
+ default VPC for the specified region.
+
+
+ If the VPC ID corresponds to a default VPC and you have specified
+ either the `DefaultAvailabilityZone` or the `DefaultSubnetId`
+ parameter only, AWS OpsWorks infers the value of the other
+ parameter. If you specify neither parameter, AWS OpsWorks sets
+ these parameters to the first valid Availability Zone for the
+ specified region and the corresponding default VPC subnet ID,
+ respectively.
+
+ If you specify a nondefault VPC ID, note the following:
+
+
+ + It must belong to a VPC in your account that is in the specified
+ region.
+ + You must specify a value for `DefaultSubnetId`.
+
+
+ For more information on how to use AWS OpsWorks with a VPC, see
+ `Running a Stack in a VPC`_. For more information on default VPC
+ and EC2 Classic, see `Supported Platforms`_.
+
:type attributes: map
:param attributes: A list of stack attributes and values as key/value
pairs to be added to the cloned stack.
@@ -154,19 +185,9 @@ class OpsWorksConnection(AWSQueryConnection):
information about IAM ARNs, see `Using Identifiers`_.
:type default_os: string
- :param default_os: The cloned stack default operating system, which
- must be set to one of the following.
-
- + Standard operating systems: `Amazon Linux` or `Ubuntu 12.04 LTS`
- + Custom AMIs: `Custom`
-
-
- The default option is `Amazon Linux`. If you set this parameter to
- `Custom`, you must use the CreateInstance action's AmiId parameter
- to specify the custom AMI that you want to use. For more
- information on the standard operating systems, see `Operating
- Systems`_For more information on how to use custom AMIs with
- OpsWorks, see `Using Custom AMIs`_.
+ :param default_os: The cloned stack's default operating system, which
+ must be set to `Amazon Linux` or `Ubuntu 12.04 LTS`. The default
+ option is `Amazon Linux`.
:type hostname_theme: string
:param hostname_theme: The stack's host name theme, with spaces are
@@ -192,8 +213,19 @@ class OpsWorksConnection(AWSQueryConnection):
returns a host name based on the current theme.
:type default_availability_zone: string
- :param default_availability_zone: The cloned stack's Availability Zone.
- For more information, see `Regions and Endpoints`_.
+ :param default_availability_zone: The cloned stack's default
+ Availability Zone, which must be in the specified region. For more
+ information, see `Regions and Endpoints`_. If you also specify a
+ value for `DefaultSubnetId`, the subnet must be in the same zone.
+ For more information, see the `VpcId` parameter description.
+
+ :type default_subnet_id: string
+ :param default_subnet_id: The stack's default subnet ID. All instances
+ will be launched into this subnet unless you specify otherwise when
+ you create the instance. If you also specify a value for
+ `DefaultAvailabilityZone`, the subnet must be in the same zone. For
+ information on default values and when this parameter is required,
+ see the `VpcId` parameter description.
:type custom_json: string
:param custom_json: A string that contains user-defined, custom JSON.
@@ -209,7 +241,7 @@ class OpsWorksConnection(AWSQueryConnection):
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 late August 2013.
+ 11.4 in September 2013.
:type use_custom_cookbooks: boolean
:param use_custom_cookbooks: Whether to use custom cookbooks.
@@ -247,6 +279,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['Name'] = name
if region is not None:
params['Region'] = region
+ if vpc_id is not None:
+ params['VpcId'] = vpc_id
if attributes is not None:
params['Attributes'] = attributes
if default_instance_profile_arn is not None:
@@ -257,6 +291,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['HostnameTheme'] = hostname_theme
if default_availability_zone is not None:
params['DefaultAvailabilityZone'] = default_availability_zone
+ if default_subnet_id is not None:
+ params['DefaultSubnetId'] = default_subnet_id
if custom_json is not None:
params['CustomJson'] = custom_json
if configuration_manager is not None:
@@ -396,8 +432,9 @@ 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, architecture=None,
- root_device_type=None, install_updates_on_boot=None):
+ availability_zone=None, subnet_id=None,
+ architecture=None, root_device_type=None,
+ install_updates_on_boot=None):
"""
Creates an instance in a specified stack. For more
information, see `Adding an Instance to a Layer`_.
@@ -434,8 +471,19 @@ class OpsWorksConnection(AWSQueryConnection):
:param hostname: The instance host name.
:type os: string
- :param os: The instance's operating system, which must be either
- `Amazon Linux` or `Ubuntu 12.04 LTS`.
+ :param os: The instance operating system, which must be set to one of
+ the following.
+
+ + Standard operating systems: `Amazon Linux` or `Ubuntu 12.04 LTS`
+ + Custom AMIs: `Custom`
+
+
+ The default option is `Amazon Linux`. If you set this parameter to
+ `Custom`, you must use the CreateInstance action's AmiId parameter
+ to specify the custom AMI that you want to use. For more
+ information on the standard operating systems, see `Operating
+ Systems`_For more information on how to use custom AMIs with
+ OpsWorks, see `Using Custom AMIs`_.
:type ami_id: string
:param ami_id: A custom AMI ID to be used to create the instance. The
@@ -450,6 +498,12 @@ class OpsWorksConnection(AWSQueryConnection):
:param availability_zone: The instance Availability Zone. For more
information, see `Regions and Endpoints`_.
+ :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
+ stack's default subnet ID value and direct AWS OpsWorks to launch
+ 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
@@ -490,6 +544,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['SshKeyName'] = ssh_key_name
if availability_zone is not None:
params['AvailabilityZone'] = availability_zone
+ if subnet_id is not None:
+ params['SubnetId'] = subnet_id
if architecture is not None:
params['Architecture'] = architecture
if root_device_type is not None:
@@ -509,10 +565,10 @@ class OpsWorksConnection(AWSQueryConnection):
Creates a layer. For more information, see `How to Create a
Layer`_.
- You should use **CreateLayer** for non-custom layer types such
+ 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 non-custom layer; if you attempt to create a second
+ 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.
@@ -620,11 +676,12 @@ class OpsWorksConnection(AWSQueryConnection):
body=json.dumps(params))
def create_stack(self, name, region, service_role_arn,
- default_instance_profile_arn, attributes=None,
- default_os=None, hostname_theme=None,
- default_availability_zone=None, custom_json=None,
- configuration_manager=None, use_custom_cookbooks=None,
- custom_cookbooks_source=None, default_ssh_key_name=None,
+ default_instance_profile_arn, vpc_id=None,
+ 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,
default_root_device_type=None):
"""
Creates a new stack. For more information, see `Create a New
@@ -637,6 +694,36 @@ class OpsWorksConnection(AWSQueryConnection):
:param region: The stack AWS region, such as "us-east-1". For more
information about Amazon regions, see `Regions and Endpoints`_.
+ :type vpc_id: string
+ :param vpc_id: The ID of the VPC that the stack is to be launched into.
+ It must be in the specified region. All instances will be launched
+ into this VPC, and you cannot change the ID later.
+
+ + If your account supports EC2 Classic, the default value is no VPC.
+ + If you account does not support EC2 Classic, the default value is the
+ default VPC for the specified region.
+
+
+ If the VPC ID corresponds to a default VPC and you have specified
+ either the `DefaultAvailabilityZone` or the `DefaultSubnetId`
+ parameter only, AWS OpsWorks infers the value of the other
+ parameter. If you specify neither parameter, AWS OpsWorks sets
+ these parameters to the first valid Availability Zone for the
+ specified region and the corresponding default VPC subnet ID,
+ respectively.
+
+ If you specify a nondefault VPC ID, note the following:
+
+
+ + It must belong to a VPC in your account that is in the specified
+ region.
+ + You must specify a value for `DefaultSubnetId`.
+
+
+ For more information on how to use AWS OpsWorks with a VPC, see
+ `Running a Stack in a VPC`_. For more information on default VPC
+ and EC2 Classic, see `Supported Platforms`_.
+
:type attributes: map
:param attributes: One or more user-defined key/value pairs to be added
to the stack attributes bag.
@@ -654,19 +741,9 @@ class OpsWorksConnection(AWSQueryConnection):
information about IAM ARNs, see `Using Identifiers`_.
:type default_os: string
- :param default_os: The stack default operating system, which must be
- set to one of the following.
-
- + Standard operating systems: `Amazon Linux` or `Ubuntu 12.04 LTS`
- + Custom AMIs: `Custom`
-
-
- The default option is `Amazon Linux`. If you set this parameter to
- `Custom`, you must use the CreateInstance action's AmiId parameter
- to specify the custom AMI that you want to use. For more
- information on the standard operating systems, see `Operating
- Systems`_For more information on how to use custom AMIs with
- OpsWorks, see `Using Custom AMIs`_.
+ :param default_os: The stack's default operating system, which must be
+ set to `Amazon Linux` or `Ubuntu 12.04 LTS`. The default option is
+ `Amazon Linux`.
:type hostname_theme: string
:param hostname_theme: The stack's host name theme, with spaces are
@@ -692,8 +769,19 @@ class OpsWorksConnection(AWSQueryConnection):
returns a host name based on the current theme.
:type default_availability_zone: string
- :param default_availability_zone: The stack default Availability Zone.
- For more information, see `Regions and Endpoints`_.
+ :param default_availability_zone: The stack's default Availability
+ Zone, which must be in the specified region. For more information,
+ see `Regions and Endpoints`_. If you also specify a value for
+ `DefaultSubnetId`, the subnet must be in the same zone. For more
+ information, see the `VpcId` parameter description.
+
+ :type default_subnet_id: string
+ :param default_subnet_id: The stack's default subnet ID. All instances
+ will be launched into this subnet unless you specify otherwise when
+ you create the instance. If you also specify a value for
+ `DefaultAvailabilityZone`, the subnet must be in that zone. For
+ information on default values and when this parameter is required,
+ see the `VpcId` parameter description.
:type custom_json: string
:param custom_json: A string that contains user-defined, custom JSON.
@@ -709,7 +797,7 @@ class OpsWorksConnection(AWSQueryConnection):
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 late August 2013.
+ 11.4 in September 2013.
:type use_custom_cookbooks: boolean
:param use_custom_cookbooks: Whether the stack uses custom cookbooks.
@@ -737,6 +825,8 @@ class OpsWorksConnection(AWSQueryConnection):
'ServiceRoleArn': service_role_arn,
'DefaultInstanceProfileArn': default_instance_profile_arn,
}
+ if vpc_id is not None:
+ params['VpcId'] = vpc_id
if attributes is not None:
params['Attributes'] = attributes
if default_os is not None:
@@ -745,6 +835,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['HostnameTheme'] = hostname_theme
if default_availability_zone is not None:
params['DefaultAvailabilityZone'] = default_availability_zone
+ if default_subnet_id is not None:
+ params['DefaultSubnetId'] = default_subnet_id
if custom_json is not None:
params['CustomJson'] = custom_json
if configuration_manager is not None:
@@ -866,6 +958,8 @@ class OpsWorksConnection(AWSQueryConnection):
"""
Requests a description of a specified set of apps.
+ You must specify at least one of the parameters.
+
:type stack_id: string
:param stack_id: The app stack ID. If you use this parameter,
`DescribeApps` returns a description of the apps in the specified
@@ -890,6 +984,8 @@ class OpsWorksConnection(AWSQueryConnection):
"""
Describes the results of specified commands.
+ You must specify at least one of the parameters.
+
:type deployment_id: string
:param deployment_id: The deployment ID. If you include this parameter,
`DescribeCommands` returns a description of the commands associated
@@ -922,6 +1018,8 @@ class OpsWorksConnection(AWSQueryConnection):
"""
Requests a description of a specified set of deployments.
+ You must specify at least one of the parameters.
+
:type stack_id: string
:param stack_id: The stack ID. If you include this parameter,
`DescribeDeployments` returns a description of the commands
@@ -951,7 +1049,9 @@ class OpsWorksConnection(AWSQueryConnection):
def describe_elastic_ips(self, instance_id=None, ips=None):
"""
- Describes an instance's `Elastic IP addresses`_.
+ Describes `Elastic IP addresses`_.
+
+ You must specify at least one of the parameters.
:type instance_id: string
:param instance_id: The instance ID. If you include this parameter,
@@ -977,6 +1077,8 @@ class OpsWorksConnection(AWSQueryConnection):
"""
Describes a stack's Elastic Load Balancing instances.
+ You must specify at least one of the parameters.
+
:type stack_id: string
:param stack_id: A stack ID. The action describes the Elastic Load
Balancing instances for the stack.
@@ -997,8 +1099,9 @@ class OpsWorksConnection(AWSQueryConnection):
def describe_instances(self, stack_id=None, layer_id=None,
instance_ids=None):
"""
- Requests a description of a set of instances associated with a
- specified ID or IDs.
+ Requests a description of a set of instances.
+
+ You must specify at least one of the parameters.
:type stack_id: string
:param stack_id: A stack ID. If you use this parameter,
@@ -1032,6 +1135,8 @@ class OpsWorksConnection(AWSQueryConnection):
Requests a description of one or more layers in a specified
stack.
+ You must specify at least one of the parameters.
+
:type stack_id: string
:param stack_id: The stack ID.
@@ -1052,6 +1157,8 @@ class OpsWorksConnection(AWSQueryConnection):
Describes load-based auto scaling configurations for specified
layers.
+ You must specify at least one of the parameters.
+
:type layer_ids: list
:param layer_ids: An array of layer IDs.
@@ -1080,6 +1187,8 @@ class OpsWorksConnection(AWSQueryConnection):
"""
Describe an instance's RAID arrays.
+ You must specify at least one of the parameters.
+
:type instance_id: string
:param instance_id: The instance ID. If you use this parameter,
`DescribeRaidArrays` returns descriptions of the RAID arrays
@@ -1153,6 +1262,8 @@ class OpsWorksConnection(AWSQueryConnection):
Describes time-based auto scaling configurations for specified
instances.
+ You must specify at least one of the parameters.
+
:type instance_ids: list
:param instance_ids: An array of instance IDs.
@@ -1179,6 +1290,8 @@ class OpsWorksConnection(AWSQueryConnection):
"""
Describes an instance's Amazon EBS volumes.
+ You must specify at least one of the parameters.
+
:type instance_id: string
:param instance_id: The instance ID. If you use this parameter,
`DescribeVolumes` returns descriptions of the volumes associated
@@ -1492,7 +1605,19 @@ class OpsWorksConnection(AWSQueryConnection):
:param hostname: The instance host name.
:type os: string
- :param os: The instance operating system.
+ :param os: The instance operating system, which must be set to one of
+ the following.
+
+ + Standard operating systems: `Amazon Linux` or `Ubuntu 12.04 LTS`
+ + Custom AMIs: `Custom`
+
+
+ The default option is `Amazon Linux`. If you set this parameter to
+ `Custom`, you must use the CreateInstance action's AmiId parameter
+ to specify the custom AMI that you want to use. For more
+ information on the standard operating systems, see `Operating
+ Systems`_For more information on how to use custom AMIs with
+ OpsWorks, see `Using Custom AMIs`_.
:type ami_id: string
:param ami_id: A custom AMI ID to be used to create the instance. The
@@ -1642,9 +1767,9 @@ class OpsWorksConnection(AWSQueryConnection):
service_role_arn=None,
default_instance_profile_arn=None, default_os=None,
hostname_theme=None, default_availability_zone=None,
- custom_json=None, configuration_manager=None,
- use_custom_cookbooks=None, custom_cookbooks_source=None,
- default_ssh_key_name=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):
"""
Updates a specified stack.
@@ -1678,19 +1803,9 @@ class OpsWorksConnection(AWSQueryConnection):
information about IAM ARNs, see `Using Identifiers`_.
:type default_os: string
- :param default_os: The stack default operating system, which must be
- set to one of the following.
-
- + Standard operating systems: `Amazon Linux` or `Ubuntu 12.04 LTS`
- + Custom AMIs: `Custom`
-
-
- The default option is `Amazon Linux`. If you set this parameter to
- `Custom`, you must use the CreateInstance action's AmiId parameter
- to specify the custom AMI that you want to use. For more
- information on the standard operating systems, see `Operating
- Systems`_For more information on how to use custom AMIs with
- OpsWorks, see `Using Custom AMIs`_.
+ :param default_os: The stack's default operating system, which must be
+ set to `Amazon Linux` or `Ubuntu 12.04 LTS`. The default option is
+ `Amazon Linux`.
:type hostname_theme: string
:param hostname_theme: The stack's new host name theme, with spaces are
@@ -1716,8 +1831,18 @@ class OpsWorksConnection(AWSQueryConnection):
returns a host name based on the current theme.
:type default_availability_zone: string
- :param default_availability_zone: The stack new default Availability
- Zone. For more information, see `Regions and Endpoints`_.
+ :param default_availability_zone: The stack's default Availability
+ Zone, which must be in the specified region. For more information,
+ see `Regions and Endpoints`_. If you also specify a value for
+ `DefaultSubnetId`, the subnet must be in the same zone. For more
+ information, see CreateStack.
+
+ :type default_subnet_id: string
+ :param default_subnet_id: The stack's default subnet ID. All instances
+ will be launched into this subnet unless you specify otherwise when
+ you create the instance. If you also specify a value for
+ `DefaultAvailabilityZone`, the subnet must be in that zone. For
+ more information, see CreateStack.
:type custom_json: string
:param custom_json: A string that contains user-defined, custom JSON.
@@ -1769,6 +1894,8 @@ class OpsWorksConnection(AWSQueryConnection):
params['HostnameTheme'] = hostname_theme
if default_availability_zone is not None:
params['DefaultAvailabilityZone'] = default_availability_zone
+ if default_subnet_id is not None:
+ params['DefaultSubnetId'] = default_subnet_id
if custom_json is not None:
params['CustomJson'] = custom_json
if configuration_manager is not None:
diff --git a/boto/sdb/db/manager/__init__.py b/boto/sdb/db/manager/__init__.py
index 69fc16f7..ded1716c 100644
--- a/boto/sdb/db/manager/__init__.py
+++ b/boto/sdb/db/manager/__init__.py
@@ -74,11 +74,11 @@ def get_manager(cls):
elif hasattr(cls.__bases__[0], "_manager"):
return cls.__bases__[0]._manager
if db_type == 'SimpleDB':
- from sdbmanager import SDBManager
+ from boto.sdb.db.manager.sdbmanager import SDBManager
return SDBManager(cls, db_name, db_user, db_passwd,
db_host, db_port, db_table, sql_dir, enable_ssl)
elif db_type == 'XML':
- from xmlmanager import XMLManager
+ from boto.sdb.db.manager.xmlmanager import XMLManager
return XMLManager(cls, db_name, db_user, db_passwd,
db_host, db_port, db_table, sql_dir, enable_ssl)
else:
diff --git a/boto/sdb/db/manager/sdbmanager.py b/boto/sdb/db/manager/sdbmanager.py
index bce2e4e2..fd9777de 100644
--- a/boto/sdb/db/manager/sdbmanager.py
+++ b/boto/sdb/db/manager/sdbmanager.py
@@ -24,7 +24,6 @@ import re
from boto.utils import find_class
import uuid
from boto.sdb.db.key import Key
-from boto.sdb.db.model import Model
from boto.sdb.db.blob import Blob
from boto.sdb.db.property import ListProperty, MapProperty
from datetime import datetime, date, time
@@ -53,12 +52,17 @@ class SDBConverter(object):
called"encode_<type name>" or "decode_<type name>".
"""
def __init__(self, manager):
+ # Do a delayed import to prevent possible circular import errors.
+ from boto.sdb.db.model import Model
+ self.model_class = Model
self.manager = manager
self.type_map = {bool: (self.encode_bool, self.decode_bool),
int: (self.encode_int, self.decode_int),
long: (self.encode_long, self.decode_long),
float: (self.encode_float, self.decode_float),
- Model: (self.encode_reference, self.decode_reference),
+ self.model_class: (
+ self.encode_reference, self.decode_reference
+ ),
Key: (self.encode_reference, self.decode_reference),
datetime: (self.encode_datetime, self.decode_datetime),
date: (self.encode_date, self.decode_date),
@@ -69,8 +73,8 @@ class SDBConverter(object):
def encode(self, item_type, value):
try:
- if Model in item_type.mro():
- item_type = Model
+ if self.model_class in item_type.mro():
+ item_type = self.model_class
except:
pass
if item_type in self.type_map:
@@ -110,8 +114,8 @@ class SDBConverter(object):
new_value = []
for key in value:
item_type = getattr(prop, "item_type")
- if Model in item_type.mro():
- item_type = Model
+ if self.model_class in item_type.mro():
+ item_type = self.model_class
encoded_value = self.encode(item_type, value[key])
if encoded_value != None:
new_value.append('%s:%s' % (urllib.quote(key), encoded_value))
@@ -159,7 +163,7 @@ class SDBConverter(object):
if ":" in value:
key, value = value.split(':', 1)
key = urllib.unquote(key)
- if Model in item_type.mro():
+ if self.model_class in item_type.mro():
value = item_type(id=value)
else:
value = self.decode(item_type, value)
diff --git a/boto/sdb/db/model.py b/boto/sdb/db/model.py
index a625ad21..3d9a6b5a 100644
--- a/boto/sdb/db/model.py
+++ b/boto/sdb/db/model.py
@@ -14,12 +14,11 @@
# 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.sdb.db.manager import get_manager
from boto.sdb.db.property import Property
from boto.sdb.db.key import Key
from boto.sdb.db.query import Query
@@ -32,6 +31,10 @@ class ModelMeta(type):
super(ModelMeta, cls).__init__(name, bases, dict)
# Make sure this is a subclass of Model - mainly copied from django ModelBase (thanks!)
cls.__sub_classes__ = []
+
+ # Do a delayed import to prevent possible circular import errors.
+ from boto.sdb.db.manager import get_manager
+
try:
if filter(lambda b: issubclass(b, Model), bases):
for base in bases:
@@ -52,7 +55,7 @@ class ModelMeta(type):
# 'Model' isn't defined yet, meaning we're looking at our own
# Model class, defined below.
pass
-
+
class Model(object):
__metaclass__ = ModelMeta
__consistent__ = False # Consistent is set off by default
@@ -67,13 +70,13 @@ class Model(object):
@classmethod
def kind(cls):
return cls.__name__
-
+
@classmethod
def _get_by_id(cls, id, manager=None):
if not manager:
manager = cls._manager
return manager.get_object(cls, id)
-
+
@classmethod
def get_by_id(cls, ids=None, parent=None):
if isinstance(ids, list):
@@ -102,7 +105,7 @@ class Model(object):
@classmethod
def get_or_insert(key_name, **kw):
raise NotImplementedError("get_or_insert not currently supported")
-
+
@classmethod
def properties(cls, hidden=True):
properties = []
@@ -171,7 +174,7 @@ class Model(object):
def __str__(self):
return str(self.id)
-
+
def __eq__(self, other):
return other and isinstance(other, Model) and self.id == other.id
@@ -191,8 +194,8 @@ class Model(object):
"""
Save this object as it is, with an optional expected value
- :param expected_value: Optional tuple of Attribute, and Value that
- must be the same in order to save this object. If this
+ :param expected_value: Optional tuple of Attribute, and Value that
+ must be the same in order to save this object. If this
condition is not met, an SDBResponseError will be raised with a
Confict status code.
:type expected_value: tuple or list
@@ -237,7 +240,7 @@ class Model(object):
return self
save_attributes = put_attributes
-
+
def delete(self):
self._manager.delete_object(self)
@@ -291,4 +294,4 @@ class Expando(Model):
return value
raise AttributeError
-
+
diff --git a/boto/sns/connection.py b/boto/sns/connection.py
index c2c23f54..73f3d9e9 100644
--- a/boto/sns/connection.py
+++ b/boto/sns/connection.py
@@ -252,7 +252,7 @@ class SNSConnection(AWSQueryConnection):
params['TargetArn'] = target_arn
if message_structure is not None:
params['MessageStructure'] = message_structure
- return self._make_request('Publish', params)
+ return self._make_request('Publish', params, '/', 'POST')
def subscribe(self, topic, protocol, endpoint):
"""
diff --git a/boto/swf/layer2.py b/boto/swf/layer2.py
index cb3298e1..dd467b42 100644
--- a/boto/swf/layer2.py
+++ b/boto/swf/layer2.py
@@ -19,15 +19,12 @@ def set_default_credentials(aws_access_key_id, aws_secret_access_key):
class SWFBase(object):
- """SWFBase."""
-
name = None
domain = None
aws_access_key_id = None
aws_secret_access_key = None
def __init__(self, **kwargs):
- """Construct an SWF object."""
# Set default credentials.
for credkey in ('aws_access_key_id', 'aws_secret_access_key'):
if DEFAULT_CREDENTIALS.get(credkey):
@@ -40,7 +37,6 @@ class SWFBase(object):
self.aws_secret_access_key)
def __repr__(self):
- """Generate string representation."""
rep_str = str(self.name)
if hasattr(self, 'version'):
rep_str += '-' + str(getattr(self, 'version'))
@@ -106,9 +102,9 @@ class Domain(SWFBase):
def executions(self, closed=False, **kwargs):
"""List list open/closed executions.
- For more info, try:
- >>> help(boto.swf.layer1.Layer1.list_closed_workflow_executions)
- >>> help(boto.swf.layer1.Layer1.list_open_workflow_executions)
+ For a full list of available parameters refer to
+ :py:func:`boto.swf.layer1.Layer1.list_closed_workflow_executions` and
+ :py:func:`boto.swf.layer1.Layer1.list_open_workflow_executions`
"""
if closed:
executions = self._swf.list_closed_workflow_executions(self.name,
@@ -148,8 +144,6 @@ class Domain(SWFBase):
class Actor(SWFBase):
- """Simple Workflow Actor interface."""
-
task_list = None
last_tasktoken = None
domain = None
@@ -160,7 +154,7 @@ class Actor(SWFBase):
class ActivityWorker(Actor):
- """ActivityWorker."""
+ """Base class for SimpleWorkflow activity workers."""
@wraps(Layer1.respond_activity_task_canceled)
def cancel(self, task_token=None, details=None):
@@ -201,7 +195,7 @@ class ActivityWorker(Actor):
class Decider(Actor):
- """Simple Workflow Decider."""
+ """Base class for SimpleWorkflow deciders."""
@wraps(Layer1.respond_decision_task_completed)
def complete(self, task_token=None, decisions=None, **kwargs):
@@ -226,7 +220,7 @@ class Decider(Actor):
class WorkflowType(SWFBase):
- """WorkflowType."""
+ """A versioned workflow type."""
version = None
task_list = None
@@ -274,7 +268,7 @@ class WorkflowType(SWFBase):
class WorkflowExecution(SWFBase):
- """WorkflowExecution."""
+ """An instance of a workflow."""
workflowId = None
runId = None
@@ -312,7 +306,7 @@ class WorkflowExecution(SWFBase):
class ActivityType(SWFBase):
- """ActivityType."""
+ """A versioned activity type."""
version = None
diff --git a/boto/vpc/__init__.py b/boto/vpc/__init__.py
index 0e5eb3fb..24a93a74 100644
--- a/boto/vpc/__init__.py
+++ b/boto/vpc/__init__.py
@@ -83,7 +83,7 @@ class VPCConnection(EC2Connection):
# VPC methods
- def get_all_vpcs(self, vpc_ids=None, filters=None):
+ def get_all_vpcs(self, vpc_ids=None, filters=None, dry_run=False):
"""
Retrieve information about your VPCs. You can filter results to
return information only about those VPCs that match your search
@@ -102,6 +102,9 @@ class VPCConnection(EC2Connection):
* *cidrBlock* - a list CIDR blocks of the VPC
* *dhcpOptionsId* - a list of IDs of a set of DHCP options
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.vpc.vpc.VPC`
"""
@@ -110,37 +113,49 @@ class VPCConnection(EC2Connection):
self.build_list_params(params, vpc_ids, 'VpcId')
if filters:
self.build_filter_params(params, dict(filters))
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeVpcs', params, [('item', VPC)])
- def create_vpc(self, cidr_block):
+ def create_vpc(self, cidr_block, dry_run=False):
"""
Create a new Virtual Private Cloud.
:type cidr_block: str
:param cidr_block: A valid CIDR block
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: The newly created VPC
:return: A :class:`boto.vpc.vpc.VPC` object
"""
params = {'CidrBlock' : cidr_block}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateVpc', params, VPC)
- def delete_vpc(self, vpc_id):
+ def delete_vpc(self, vpc_id, dry_run=False):
"""
Delete a Virtual Private Cloud.
:type vpc_id: str
:param vpc_id: The ID of the vpc to be deleted.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'VpcId': vpc_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteVpc', params)
def modify_vpc_attribute(self, vpc_id,
enable_dns_support=None,
- enable_dns_hostnames=None):
+ enable_dns_hostnames=None, dry_run=False):
"""
Modifies the specified attribute of the specified VPC.
You can only modify one attribute at a time.
@@ -157,6 +172,10 @@ class VPCConnection(EC2Connection):
provided for the instances launched in this VPC. You can only
set this attribute to ``true`` if EnableDnsSupport
is also ``true``.
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {'VpcId': vpc_id}
if enable_dns_support is not None:
@@ -169,11 +188,14 @@ class VPCConnection(EC2Connection):
params['EnableDnsHostnames.Value'] = 'true'
else:
params['EnableDnsHostnames.Value'] = 'false'
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('ModifyVpcAttribute', params)
# Route Tables
- def get_all_route_tables(self, route_table_ids=None, filters=None):
+ def get_all_route_tables(self, route_table_ids=None, filters=None,
+ dry_run=False):
"""
Retrieve information about your routing tables. You can filter results
to return information only about those route tables that match your
@@ -188,6 +210,9 @@ class VPCConnection(EC2Connection):
:param filters: A list of tuples containing filters. Each tuple
consists of a filter key and a filter value.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.vpc.routetable.RouteTable`
"""
@@ -196,10 +221,12 @@ class VPCConnection(EC2Connection):
self.build_list_params(params, route_table_ids, "RouteTableId")
if filters:
self.build_filter_params(params, dict(filters))
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeRouteTables', params,
[('item', RouteTable)])
- def associate_route_table(self, route_table_id, subnet_id):
+ def associate_route_table(self, route_table_id, subnet_id, dry_run=False):
"""
Associates a route table with a specific subnet.
@@ -209,6 +236,9 @@ class VPCConnection(EC2Connection):
:type subnet_id: str
:param subnet_id: The ID of the subnet to associate with.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: str
:return: The ID of the association created
"""
@@ -216,11 +246,12 @@ class VPCConnection(EC2Connection):
'RouteTableId': route_table_id,
'SubnetId': subnet_id
}
-
+ if dry_run:
+ params['DryRun'] = 'true'
result = self.get_object('AssociateRouteTable', params, ResultSet)
return result.associationId
- def disassociate_route_table(self, association_id):
+ def disassociate_route_table(self, association_id, dry_run=False):
"""
Removes an association from a route table. This will cause all subnets
that would've used this association to now use the main routing
@@ -229,40 +260,55 @@ class VPCConnection(EC2Connection):
:type association_id: str
:param association_id: The ID of the association to disassociate.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = { 'AssociationId': association_id }
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DisassociateRouteTable', params)
- def create_route_table(self, vpc_id):
+ def create_route_table(self, vpc_id, dry_run=False):
"""
Creates a new route table.
:type vpc_id: str
:param vpc_id: The VPC ID to associate this route table with.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: The newly created route table
:return: A :class:`boto.vpc.routetable.RouteTable` object
"""
params = { 'VpcId': vpc_id }
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateRouteTable', params, RouteTable)
- def delete_route_table(self, route_table_id):
+ def delete_route_table(self, route_table_id, dry_run=False):
"""
Delete a route table.
:type route_table_id: str
:param route_table_id: The ID of the route table to delete.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = { 'RouteTableId': route_table_id }
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteRouteTable', params)
def create_route(self, route_table_id, destination_cidr_block,
- gateway_id=None, instance_id=None):
+ gateway_id=None, instance_id=None, dry_run=False):
"""
Creates a new route in the route table within a VPC. The route's target
can be either a gateway attached to the VPC or a NAT instance in the
@@ -281,6 +327,9 @@ class VPCConnection(EC2Connection):
:type instance_id: str
:param instance_id: The ID of a NAT instance in your VPC.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -293,11 +342,14 @@ class VPCConnection(EC2Connection):
params['GatewayId'] = gateway_id
elif instance_id is not None:
params['InstanceId'] = instance_id
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('CreateRoute', params)
def replace_route(self, route_table_id, destination_cidr_block,
- gateway_id=None, instance_id=None, interface_id=None):
+ gateway_id=None, instance_id=None, interface_id=None,
+ dry_run=False):
"""
Replaces an existing route within a route table in a VPC.
@@ -317,6 +369,9 @@ class VPCConnection(EC2Connection):
:type interface_id: str
:param interface_id: Allows routing to network interface attachments.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -331,10 +386,13 @@ class VPCConnection(EC2Connection):
params['InstanceId'] = instance_id
elif interface_id is not None:
params['NetworkInterfaceId'] = interface_id
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('ReplaceRoute', params)
- def delete_route(self, route_table_id, destination_cidr_block):
+ def delete_route(self, route_table_id, destination_cidr_block,
+ dry_run=False):
"""
Deletes a route from a route table within a VPC.
@@ -345,6 +403,9 @@ class VPCConnection(EC2Connection):
:param destination_cidr_block: The CIDR address block used for
destination match.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -352,13 +413,14 @@ class VPCConnection(EC2Connection):
'RouteTableId': route_table_id,
'DestinationCidrBlock': destination_cidr_block
}
-
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteRoute', params)
# Internet Gateways
def get_all_internet_gateways(self, internet_gateway_ids=None,
- filters=None):
+ filters=None, dry_run=False):
"""
Get a list of internet gateways. You can filter results to return information
about only those gateways that you're interested in.
@@ -369,6 +431,10 @@ class VPCConnection(EC2Connection):
:type filters: list of tuples
:param filters: A list of tuples containing filters. Each tuple
consists of a filter key and a filter value.
+
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
"""
params = {}
@@ -377,32 +443,46 @@ class VPCConnection(EC2Connection):
'InternetGatewayId')
if filters:
self.build_filter_params(params, dict(filters))
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeInternetGateways', params,
[('item', InternetGateway)])
- def create_internet_gateway(self):
+ def create_internet_gateway(self, dry_run=False):
"""
Creates an internet gateway for VPC.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: Newly created internet gateway.
:return: `boto.vpc.internetgateway.InternetGateway`
"""
- return self.get_object('CreateInternetGateway', {}, InternetGateway)
+ params = {}
+ if dry_run:
+ params['DryRun'] = 'true'
+ return self.get_object('CreateInternetGateway', params, InternetGateway)
- def delete_internet_gateway(self, internet_gateway_id):
+ def delete_internet_gateway(self, internet_gateway_id, dry_run=False):
"""
Deletes an internet gateway from the VPC.
:type internet_gateway_id: str
:param internet_gateway_id: The ID of the internet gateway to delete.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: Bool
:return: True if successful
"""
params = { 'InternetGatewayId': internet_gateway_id }
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteInternetGateway', params)
- def attach_internet_gateway(self, internet_gateway_id, vpc_id):
+ def attach_internet_gateway(self, internet_gateway_id, vpc_id,
+ dry_run=False):
"""
Attach an internet gateway to a specific VPC.
@@ -412,6 +492,9 @@ class VPCConnection(EC2Connection):
:type vpc_id: str
:param vpc_id: The ID of the VPC to attach to.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: Bool
:return: True if successful
"""
@@ -419,10 +502,12 @@ class VPCConnection(EC2Connection):
'InternetGatewayId': internet_gateway_id,
'VpcId': vpc_id
}
-
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('AttachInternetGateway', params)
- def detach_internet_gateway(self, internet_gateway_id, vpc_id):
+ def detach_internet_gateway(self, internet_gateway_id, vpc_id,
+ dry_run=False):
"""
Detach an internet gateway from a specific VPC.
@@ -432,6 +517,9 @@ class VPCConnection(EC2Connection):
:type vpc_id: str
:param vpc_id: The ID of the VPC to attach to.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: Bool
:return: True if successful
"""
@@ -439,13 +527,14 @@ class VPCConnection(EC2Connection):
'InternetGatewayId': internet_gateway_id,
'VpcId': vpc_id
}
-
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DetachInternetGateway', params)
# Customer Gateways
def get_all_customer_gateways(self, customer_gateway_ids=None,
- filters=None):
+ filters=None, dry_run=False):
"""
Retrieve information about your CustomerGateways. You can filter
results to return information only about those CustomerGateways that
@@ -467,6 +556,9 @@ class VPCConnection(EC2Connection):
- *ipAddress* the IP address of customer gateway's
internet-routable external inteface
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.vpc.customergateway.CustomerGateway`
"""
@@ -477,10 +569,13 @@ class VPCConnection(EC2Connection):
if filters:
self.build_filter_params(params, dict(filters))
+ if dry_run:
+ params['DryRun'] = 'true'
+
return self.get_list('DescribeCustomerGateways', params,
[('item', CustomerGateway)])
- def create_customer_gateway(self, type, ip_address, bgp_asn):
+ def create_customer_gateway(self, type, ip_address, bgp_asn, dry_run=False):
"""
Create a new Customer Gateway
@@ -495,30 +590,41 @@ class VPCConnection(EC2Connection):
:param bgp_asn: Customer gateway's Border Gateway Protocol (BGP)
Autonomous System Number (ASN)
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: The newly created CustomerGateway
:return: A :class:`boto.vpc.customergateway.CustomerGateway` object
"""
params = {'Type' : type,
'IpAddress' : ip_address,
'BgpAsn' : bgp_asn}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateCustomerGateway', params, CustomerGateway)
- def delete_customer_gateway(self, customer_gateway_id):
+ def delete_customer_gateway(self, customer_gateway_id, dry_run=False):
"""
Delete a Customer Gateway.
:type customer_gateway_id: str
:param customer_gateway_id: The ID of the customer_gateway to be deleted.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'CustomerGatewayId': customer_gateway_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteCustomerGateway', params)
# VPN Gateways
- def get_all_vpn_gateways(self, vpn_gateway_ids=None, filters=None):
+ def get_all_vpn_gateways(self, vpn_gateway_ids=None, filters=None,
+ dry_run=False):
"""
Retrieve information about your VpnGateways. You can filter results to
return information only about those VpnGateways that match your search
@@ -539,6 +645,9 @@ class VPCConnection(EC2Connection):
- *availabilityZone*, a list of Availability zones the
VPN gateway is in.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.vpc.customergateway.VpnGateway`
"""
@@ -547,10 +656,12 @@ class VPCConnection(EC2Connection):
self.build_list_params(params, vpn_gateway_ids, 'VpnGatewayId')
if filters:
self.build_filter_params(params, dict(filters))
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeVpnGateways', params,
[('item', VpnGateway)])
- def create_vpn_gateway(self, type, availability_zone=None):
+ def create_vpn_gateway(self, type, availability_zone=None, dry_run=False):
"""
Create a new Vpn Gateway
@@ -560,28 +671,38 @@ class VPCConnection(EC2Connection):
:type availability_zone: str
:param availability_zone: The Availability Zone where you want the VPN gateway.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: The newly created VpnGateway
:return: A :class:`boto.vpc.vpngateway.VpnGateway` object
"""
params = {'Type' : type}
if availability_zone:
params['AvailabilityZone'] = availability_zone
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateVpnGateway', params, VpnGateway)
- def delete_vpn_gateway(self, vpn_gateway_id):
+ def delete_vpn_gateway(self, vpn_gateway_id, dry_run=False):
"""
Delete a Vpn Gateway.
:type vpn_gateway_id: str
:param vpn_gateway_id: The ID of the vpn_gateway to be deleted.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'VpnGatewayId': vpn_gateway_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteVpnGateway', params)
- def attach_vpn_gateway(self, vpn_gateway_id, vpc_id):
+ def attach_vpn_gateway(self, vpn_gateway_id, vpc_id, dry_run=False):
"""
Attaches a VPN gateway to a VPC.
@@ -591,16 +712,21 @@ class VPCConnection(EC2Connection):
:type vpc_id: str
:param vpc_id: The ID of the VPC you want to attach the gateway to.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: An attachment
:return: a :class:`boto.vpc.vpngateway.Attachment`
"""
params = {'VpnGatewayId': vpn_gateway_id,
'VpcId' : vpc_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('AttachVpnGateway', params, Attachment)
# Subnets
- def get_all_subnets(self, subnet_ids=None, filters=None):
+ def get_all_subnets(self, subnet_ids=None, filters=None, dry_run=False):
"""
Retrieve information about your Subnets. You can filter results to
return information only about those Subnets that match your search
@@ -623,6 +749,9 @@ class VPCConnection(EC2Connection):
the subnet is in.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.vpc.subnet.Subnet`
"""
@@ -631,9 +760,12 @@ class VPCConnection(EC2Connection):
self.build_list_params(params, subnet_ids, 'SubnetId')
if filters:
self.build_filter_params(params, dict(filters))
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeSubnets', params, [('item', Subnet)])
- def create_subnet(self, vpc_id, cidr_block, availability_zone=None):
+ def create_subnet(self, vpc_id, cidr_block, availability_zone=None,
+ dry_run=False):
"""
Create a new Subnet
@@ -646,6 +778,9 @@ class VPCConnection(EC2Connection):
:type availability_zone: str
:param availability_zone: The AZ you want the subnet in
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: The newly created Subnet
:return: A :class:`boto.vpc.customergateway.Subnet` object
"""
@@ -653,43 +788,55 @@ class VPCConnection(EC2Connection):
'CidrBlock' : cidr_block}
if availability_zone:
params['AvailabilityZone'] = availability_zone
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateSubnet', params, Subnet)
- def delete_subnet(self, subnet_id):
+ def delete_subnet(self, subnet_id, dry_run=False):
"""
Delete a subnet.
:type subnet_id: str
:param subnet_id: The ID of the subnet to be deleted.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'SubnetId': subnet_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteSubnet', params)
# DHCP Options
- def get_all_dhcp_options(self, dhcp_options_ids=None):
+ def get_all_dhcp_options(self, dhcp_options_ids=None, dry_run=False):
"""
Retrieve information about your DhcpOptions.
:type dhcp_options_ids: list
:param dhcp_options_ids: A list of strings with the desired DhcpOption ID's
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.vpc.dhcpoptions.DhcpOptions`
"""
params = {}
if dhcp_options_ids:
self.build_list_params(params, dhcp_options_ids, 'DhcpOptionsId')
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeDhcpOptions', params,
[('item', DhcpOptions)])
def create_dhcp_options(self, domain_name=None, domain_name_servers=None,
ntp_servers=None, netbios_name_servers=None,
- netbios_node_type=None):
+ netbios_node_type=None, dry_run=False):
"""
Create a new DhcpOption
@@ -718,6 +865,9 @@ class VPCConnection(EC2Connection):
only use 2 at this time (broadcast and multicast are currently not
supported).
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: The newly created DhcpOption
:return: A :class:`boto.vpc.customergateway.DhcpOption` object
"""
@@ -753,23 +903,30 @@ class VPCConnection(EC2Connection):
if netbios_node_type:
key_counter = insert_option(params,
'netbios-node-type', netbios_node_type)
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateDhcpOptions', params, DhcpOptions)
- def delete_dhcp_options(self, dhcp_options_id):
+ def delete_dhcp_options(self, dhcp_options_id, dry_run=False):
"""
Delete a DHCP Options
:type dhcp_options_id: str
:param dhcp_options_id: The ID of the DHCP Options to be deleted.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'DhcpOptionsId': dhcp_options_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteDhcpOptions', params)
- def associate_dhcp_options(self, dhcp_options_id, vpc_id):
+ def associate_dhcp_options(self, dhcp_options_id, vpc_id, dry_run=False):
"""
Associate a set of Dhcp Options with a VPC.
@@ -779,16 +936,22 @@ class VPCConnection(EC2Connection):
:type vpc_id: str
:param vpc_id: The ID of the VPC.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'DhcpOptionsId': dhcp_options_id,
'VpcId' : vpc_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('AssociateDhcpOptions', params)
# VPN Connection
- def get_all_vpn_connections(self, vpn_connection_ids=None, filters=None):
+ def get_all_vpn_connections(self, vpn_connection_ids=None, filters=None,
+ dry_run=False):
"""
Retrieve information about your VPN_CONNECTIONs. You can filter results to
return information only about those VPN_CONNECTIONs that match your search
@@ -811,6 +974,9 @@ class VPCConnection(EC2Connection):
- *vpnGatewayId*, a list of IDs of the VPN gateway associated
with the VPN connection
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: list
:return: A list of :class:`boto.vpn_connection.vpnconnection.VpnConnection`
"""
@@ -820,10 +986,13 @@ class VPCConnection(EC2Connection):
'Vpn_ConnectionId')
if filters:
self.build_filter_params(params, dict(filters))
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_list('DescribeVpnConnections', params,
[('item', VpnConnection)])
- def create_vpn_connection(self, type, customer_gateway_id, vpn_gateway_id):
+ def create_vpn_connection(self, type, customer_gateway_id, vpn_gateway_id,
+ dry_run=False):
"""
Create a new VPN Connection.
@@ -837,28 +1006,39 @@ class VPCConnection(EC2Connection):
:type vpn_gateway_id: str
:param vpn_gateway_id: The ID of the VPN gateway.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: The newly created VpnConnection
:return: A :class:`boto.vpc.vpnconnection.VpnConnection` object
"""
params = {'Type' : type,
'CustomerGatewayId' : customer_gateway_id,
'VpnGatewayId' : vpn_gateway_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_object('CreateVpnConnection', params, VpnConnection)
- def delete_vpn_connection(self, vpn_connection_id):
+ def delete_vpn_connection(self, vpn_connection_id, dry_run=False):
"""
Delete a VPN Connection.
:type vpn_connection_id: str
:param vpn_connection_id: The ID of the vpn_connection to be deleted.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
params = {'VpnConnectionId': vpn_connection_id}
+ if dry_run:
+ params['DryRun'] = 'true'
return self.get_status('DeleteVpnConnection', params)
- def disable_vgw_route_propagation(self, route_table_id, gateway_id):
+ def disable_vgw_route_propagation(self, route_table_id, gateway_id,
+ dry_run=False):
"""
Disables a virtual private gateway (VGW) from propagating routes to the
routing tables of an Amazon VPC.
@@ -869,6 +1049,9 @@ class VPCConnection(EC2Connection):
:type gateway_id: str
:param gateway_id: The ID of the virtual private gateway.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -876,9 +1059,12 @@ class VPCConnection(EC2Connection):
'RouteTableId': route_table_id,
'GatewayId': gateway_id,
}
- self.get_status('DisableVgwRoutePropagation', params)
+ if dry_run:
+ params['DryRun'] = 'true'
+ return self.get_status('DisableVgwRoutePropagation', params)
- def enable_vgw_route_propagation(self, route_table_id, gateway_id):
+ def enable_vgw_route_propagation(self, route_table_id, gateway_id,
+ dry_run=False):
"""
Enables a virtual private gateway (VGW) to propagate routes to the
routing tables of an Amazon VPC.
@@ -889,6 +1075,9 @@ class VPCConnection(EC2Connection):
:type gateway_id: str
:param gateway_id: The ID of the virtual private gateway.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -896,10 +1085,12 @@ class VPCConnection(EC2Connection):
'RouteTableId': route_table_id,
'GatewayId': gateway_id,
}
- self.get_status('EnableVgwRoutePropagation', params)
+ if dry_run:
+ params['DryRun'] = 'true'
+ return self.get_status('EnableVgwRoutePropagation', params)
def create_vpn_connection_route(self, destination_cidr_block,
- vpn_connection_id):
+ vpn_connection_id, dry_run=False):
"""
Creates a new static route associated with a VPN connection between an
existing virtual private gateway and a VPN customer gateway. The static
@@ -913,6 +1104,9 @@ class VPCConnection(EC2Connection):
:type vpn_connection_id: str
:param vpn_connection_id: The ID of the VPN connection.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -920,10 +1114,12 @@ class VPCConnection(EC2Connection):
'DestinationCidrBlock': destination_cidr_block,
'VpnConnectionId': vpn_connection_id,
}
- self.get_status('CreateVpnConnectionRoute', params)
+ if dry_run:
+ params['DryRun'] = 'true'
+ return self.get_status('CreateVpnConnectionRoute', params)
def delete_vpn_connection_route(self, destination_cidr_block,
- vpn_connection_id):
+ vpn_connection_id, dry_run=False):
"""
Deletes a static route associated with a VPN connection between an
existing virtual private gateway and a VPN customer gateway. The static
@@ -937,6 +1133,9 @@ class VPCConnection(EC2Connection):
:type vpn_connection_id: str
:param vpn_connection_id: The ID of the VPN connection.
+ :type dry_run: bool
+ :param dry_run: Set to True if the operation should not actually run.
+
:rtype: bool
:return: True if successful
"""
@@ -944,4 +1143,6 @@ class VPCConnection(EC2Connection):
'DestinationCidrBlock': destination_cidr_block,
'VpnConnectionId': vpn_connection_id,
}
- self.get_status('DeleteVpnConnectionRoute', params)
+ if dry_run:
+ params['DryRun'] = 'true'
+ return self.get_status('DeleteVpnConnectionRoute', params)
diff --git a/boto/vpc/vpc.py b/boto/vpc/vpc.py
index 8fdaa62f..2eb480d1 100644
--- a/boto/vpc/vpc.py
+++ b/boto/vpc/vpc.py
@@ -72,8 +72,11 @@ class VPC(TaggedEC2Object):
def _update(self, updated):
self.__dict__.update(updated.__dict__)
- def update(self, validate=False):
- vpc_list = self.connection.get_all_vpcs([self.id])
+ def update(self, validate=False, dry_run=False):
+ vpc_list = self.connection.get_all_vpcs(
+ [self.id],
+ dry_run=dry_run
+ )
if len(vpc_list):
updated_vpc = vpc_list[0]
self._update(updated_vpc)
diff --git a/boto/vpc/vpnconnection.py b/boto/vpc/vpnconnection.py
index aa49c36a..c36492f5 100644
--- a/boto/vpc/vpnconnection.py
+++ b/boto/vpc/vpnconnection.py
@@ -197,5 +197,8 @@ class VpnConnection(TaggedEC2Object):
else:
setattr(self, name, value)
- def delete(self):
- return self.connection.delete_vpn_connection(self.id)
+ def delete(self, dry_run=False):
+ return self.connection.delete_vpn_connection(
+ self.id,
+ dry_run=dry_run
+ )
diff --git a/boto/vpc/vpngateway.py b/boto/vpc/vpngateway.py
index 83b912ef..fe476d93 100644
--- a/boto/vpc/vpngateway.py
+++ b/boto/vpc/vpngateway.py
@@ -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.
@@ -33,7 +33,7 @@ class Attachment(object):
def startElement(self, name, attrs, connection):
pass
-
+
def endElement(self, name, value, connection):
if name == 'vpcId':
self.vpc_id = value
@@ -41,7 +41,7 @@ class Attachment(object):
self.state = value
else:
setattr(self, name, value)
-
+
class VpnGateway(TaggedEC2Object):
def __init__(self, connection=None):
@@ -63,7 +63,7 @@ class VpnGateway(TaggedEC2Object):
att = Attachment()
self.attachments.append(att)
return att
-
+
def endElement(self, name, value, connection):
if name == 'vpnGatewayId':
self.id = value
@@ -78,6 +78,10 @@ class VpnGateway(TaggedEC2Object):
else:
setattr(self, name, value)
- def attach(self, vpc_id):
- return self.connection.attach_vpn_gateway(self.id, vpc_id)
+ def attach(self, vpc_id, dry_run=False):
+ return self.connection.attach_vpn_gateway(
+ self.id,
+ vpc_id,
+ dry_run=dry_run
+ )
diff --git a/docs/source/index.rst b/docs/source/index.rst
index d744fc68..c6c20e2d 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -24,8 +24,6 @@ Currently Supported Services
* :doc:`Elastic Compute Cloud (EC2) <ec2_tut>` -- (:doc:`API Reference <ref/ec2>`)
* :doc:`Elastic MapReduce (EMR) <emr_tut>` -- (:doc:`API Reference <ref/emr>`)
* :doc:`Auto Scaling <autoscale_tut>` -- (:doc:`API Reference <ref/autoscale>`)
- * Data Pipeline -- (:doc:`API Reference <ref/datapipeline>`)
- * Elastic Transcoder -- (:doc:`API Reference <ref/elastictranscoder>`)
* **Content Delivery**
@@ -44,6 +42,8 @@ Currently Supported Services
* CloudFormation -- (:doc:`API Reference <ref/cloudformation>`)
* Elastic Beanstalk -- (:doc:`API Reference <ref/beanstalk>`)
+ * Data Pipeline -- (:doc:`API Reference <ref/datapipeline>`)
+ * Opsworks -- (:doc:`API Reference <ref/opsworks>`)
* **Identity & Access**
@@ -52,11 +52,12 @@ Currently Supported Services
* **Application Services**
+ * :doc:`Cloudsearch <cloudsearch_tut>` -- (:doc:`API Reference <ref/cloudsearch>`)
+ * Elastic Transcoder -- (:doc:`API Reference <ref/elastictranscoder>`)
* Simple Workflow Service (SWF) -- (:doc:`API Reference <ref/swf>`)
* :doc:`Simple Queue Service (SQS) <sqs_tut>` -- (:doc:`API Reference <ref/sqs>`)
* Simple Notification Service (SNS) -- (:doc:`API Reference <ref/sns>`)
* :doc:`Simple Email Service (SES) <ses_tut>` -- (:doc:`API Reference <ref/ses>`)
- * :doc:`Cloudsearch <cloudsearch_tut>` -- (:doc:`API Reference <ref/cloudsearch>`)
* **Monitoring**
@@ -144,58 +145,33 @@ Release Notes
.. toctree::
:hidden:
+ :glob:
getting_started
ec2_tut
security_groups
- ref/ec2
emr_tut
- ref/emr
autoscale_tut
- ref/autoscale
cloudfront_tut
- ref/cloudfront
simpledb_tut
- ref/sdb
- ref/sdb_db
dynamodb_tut
- ref/dynamodb
rds_tut
- ref/rds
- ref/cloudformation
- ref/iam
- ref/mws
sqs_tut
- ref/sqs
- ref/sns
ses_tut
- ref/ses
cloudsearch_tut
- ref/cloudsearch
cloudwatch_tut
- ref/cloudwatch
- ref/route53
vpc_tut
- ref/vpc
elb_tut
- ref/elb
- ref/fps
s3_tut
- ref/s3
- ref/mturk
boto_config_tut
- ref/index
documentation
contributing
- ref/datapipeline
- ref/elasticache
- ref/elastictranscoder
- ref/redshift
- ref/dynamodb2
+ commandline
support_tut
- ref/support
dynamodb2_tut
migrations/dynamodb_v1_to_v2
+ ref/*
+ releasenotes/*
Indices and tables
diff --git a/docs/source/ref/contrib.rst b/docs/source/ref/contrib.rst
index 9262a0dc..39ef54f8 100644
--- a/docs/source/ref/contrib.rst
+++ b/docs/source/ref/contrib.rst
@@ -8,25 +8,12 @@ boto.contrib
------------
.. automodule:: boto.contrib
- :members:
- :undoc-members:
-
-boto.contrib.m2helpers
-----------------------
-
-.. note::
-
- This module requires installation of M2Crypto__ in your Python path.
-
- __ http://sandbox.rulemaker.net/ngps/m2/
-
-.. automodule:: boto.contrib.m2helpers
- :members:
+ :members:
:undoc-members:
boto.contrib.ymlmessage
-----------------------
.. automodule:: boto.contrib.ymlmessage
- :members:
+ :members:
:undoc-members: \ No newline at end of file
diff --git a/docs/source/ref/opsworks.rst b/docs/source/ref/opsworks.rst
new file mode 100644
index 00000000..7c761e72
--- /dev/null
+++ b/docs/source/ref/opsworks.rst
@@ -0,0 +1,28 @@
+.. ref-opsworks
+
+========
+Opsworks
+========
+
+boto.opsworks
+------------
+
+.. automodule:: boto.opsworks
+ :members:
+ :undoc-members:
+
+boto.opsworks.layer1
+-------------------
+
+.. automodule:: boto.opsworks.layer1
+ :members:
+ :undoc-members:
+
+boto.opsworks.exceptions
+-----------------------
+
+.. automodule:: boto.opsworks.exceptions
+ :members:
+ :undoc-members:
+
+
diff --git a/docs/source/ref/s3.rst b/docs/source/ref/s3.rst
index ce5c9256..2cad0e95 100644
--- a/docs/source/ref/s3.rst
+++ b/docs/source/ref/s3.rst
@@ -88,13 +88,6 @@ boto.s3.lifecycle
:members:
:undoc-members:
-boto.s3.prefix
------------------
-
-.. automodule:: boto.s3.prefix
- :members:
- :undoc-members:
-
boto.s3.tagging
---------------
diff --git a/docs/source/ref/sdb_db.rst b/docs/source/ref/sdb_db.rst
index 5b77e2bc..a7594db5 100644
--- a/docs/source/ref/sdb_db.rst
+++ b/docs/source/ref/sdb_db.rst
@@ -10,74 +10,61 @@ boto.sdb.db
-----------
.. automodule:: boto.sdb.db
- :members:
+ :members:
:undoc-members:
boto.sdb.db.blob
----------------
.. automodule:: boto.sdb.db.blob
- :members:
+ :members:
:undoc-members:
boto.sdb.db.key
---------------
.. automodule:: boto.sdb.db.key
- :members:
+ :members:
:undoc-members:
boto.sdb.db.manager
-------------------
.. automodule:: boto.sdb.db.manager
- :members:
- :undoc-members:
-
-boto.sdb.db.manager.pgmanager
------------------------------
-
-.. note::
-
- This module requires psycopg2__ to be installed in the Python path.
-
- __ http://initd.org/
-
-.. automodule:: boto.sdb.db.manager.pgmanager
- :members:
+ :members:
:undoc-members:
boto.sdb.db.manager.sdbmanager
------------------------------
.. automodule:: boto.sdb.db.manager.sdbmanager
- :members:
+ :members:
:undoc-members:
boto.sdb.db.manager.xmlmanager
------------------------------
.. automodule:: boto.sdb.db.manager.xmlmanager
- :members:
+ :members:
:undoc-members:
boto.sdb.db.model
-----------------
.. automodule:: boto.sdb.db.model
- :members:
+ :members:
:undoc-members:
boto.sdb.db.property
--------------------
.. automodule:: boto.sdb.db.property
- :members:
+ :members:
:undoc-members:
boto.sdb.db.query
-----------------
.. automodule:: boto.sdb.db.query
- :members:
+ :members:
:undoc-members:
diff --git a/docs/source/ref/swf.rst b/docs/source/ref/swf.rst
index d4b0ca36..892bc07a 100644
--- a/docs/source/ref/swf.rst
+++ b/docs/source/ref/swf.rst
@@ -18,5 +18,8 @@ boto.swf.layer1
:members:
:undoc-members:
+boto.swf.layer2
+--------------------
-
+.. automodule:: boto.swf.layer2
+ :members:
diff --git a/docs/source/releasenotes/releasenotes_template.rst b/docs/source/releasenotes/releasenotes_template.rst
new file mode 100644
index 00000000..39b096eb
--- /dev/null
+++ b/docs/source/releasenotes/releasenotes_template.rst
@@ -0,0 +1,21 @@
+boto v2.xx.x
+============
+
+:date: 2013/xx/xx
+
+This release adds ____.
+
+
+Features
+--------
+
+* . (:issue:``, :sha:``)
+
+
+Bugfixes
+--------
+
+* (:issue:``, :sha:``)
+* Several documentation improvements/fixes:
+
+ * (:issue:``, :sha:``)
diff --git a/docs/source/releasenotes/v2.0.0.rst b/docs/source/releasenotes/v2.0.0.rst
index 47e563a0..28d38536 100644
--- a/docs/source/releasenotes/v2.0.0.rst
+++ b/docs/source/releasenotes/v2.0.0.rst
@@ -10,7 +10,7 @@ There have been many, many changes since the 2.0b4 release. This overview highli
* Fix connection pooling bug: don't close before reading.
* Added AddInstanceGroup and ModifyInstanceGroup to boto.emr
* Merge pull request #246 from chetan/multipart_s3put
-AddInstanceGroupsResponse class to boto.emr.emrobject.
+* AddInstanceGroupsResponse class to boto.emr.emrobject.
* Removed extra print statement
* Merge pull request #244 from ryansb/master
* Added add_instance_groups function to boto.emr.connection. Built some helper methods for it, and added AddInstanceGroupsResponse class to boto.emr.emrobject.
diff --git a/docs/source/releasenotes/v2.13.0.rst b/docs/source/releasenotes/v2.13.0.rst
new file mode 100644
index 00000000..b28e9ba7
--- /dev/null
+++ b/docs/source/releasenotes/v2.13.0.rst
@@ -0,0 +1,40 @@
+boto v2.13.0
+============
+
+:date: 2013/09/12
+
+This release adds support for VPC within AWS Opsworks, added dry-run support &
+the ability to modify reserved instances in EC2 as well as several important
+bugfixes for EC2, SNS & DynamoDBv2.
+
+
+Features
+--------
+
+* Added support for VPC within Opsworks. (:sha:`56e1df3`)
+* Added support for ``dry_run`` within EC2. (:sha:`dd7774c`)
+* Added support for ``modify_reserved_instances`` &
+ ``describe_reserved_instances_modifications`` within EC2. (:sha:`7a08672`)
+
+
+Bugfixes
+--------
+
+* Fixed EC2's ``associate_public_ip`` to work correctly. (:sha:`9db6101`)
+* Fixed a bug with ``dynamodb_load`` when working with sets. (:issue:`1664`,
+ :sha:`ef2d28b`)
+* Changed SNS ``publish`` to use POST. (:sha:`9c11772`)
+* Fixed inability to create LaunchConfigurations when using Block Device
+ Mappings. (:issue:`1709`, :issue:`1710`, :sha:`5fd728e`)
+* Fixed DynamoDBv2's ``batch_write`` to appropriately handle
+ ``UnprocessedItems``. (:issue:`1566`, :issue:`1679`, :issue:`1714`,
+ :sha:`2fc2369`)
+* Several documentation improvements/fixes:
+
+ * Added Opsworks docs to the index. (:sha:`5d48763`)
+ * Added docs on the correct string values for ``get_all_images``.
+ (:issue:`1674`, :sha:`1e4ed2e`)
+ * Removed a duplicate ``boto.s3.prefix`` entry from the docs.
+ (:issue:`1707`, :sha:`b42d34c`)
+ * Added an API reference for ``boto.swf.layer2``. (:issue:`1712`,
+ :sha:`9f7b15f`)
diff --git a/docs/source/releasenotes/v2.9.8.rst b/docs/source/releasenotes/v2.9.8.rst
index 36432813..0398582d 100644
--- a/docs/source/releasenotes/v2.9.8.rst
+++ b/docs/source/releasenotes/v2.9.8.rst
@@ -12,7 +12,7 @@ Features
--------
* Added support for the ``DecodeAuthorizationMessage`` in STS (:sha:`1ada5ac`).
-* Added support for creating/deleting/describing ``OptionGroup``s in RDS.
+* Added support for creating/deleting/describing ``OptionGroup`` in RDS.
(:sha:`d629228` & :sha:`d059a3b`)
* Added ``CancelUpdateStack`` to CloudFormation. (:issue:`1476`, :sha:`5bae130`)
* Added support for getting/setting lifecycle configurations on GS buckets.
diff --git a/docs/source/s3_tut.rst b/docs/source/s3_tut.rst
index 2b40306a..aff667e3 100644
--- a/docs/source/s3_tut.rst
+++ b/docs/source/s3_tut.rst
@@ -384,7 +384,7 @@ under ``logs/*`` to transition to Glacier 30 days after the object is created.
We can now configure the bucket with this lifecycle policy::
>>> bucket.configure_lifecycle(lifecycle)
-True
+ True
You can also retrieve the current lifecycle policy for the bucket::
diff --git a/tests/integration/dynamodb2/test_highlevel.py b/tests/integration/dynamodb2/test_highlevel.py
index 75b2e2d4..c4a8a9a1 100644
--- a/tests/integration/dynamodb2/test_highlevel.py
+++ b/tests/integration/dynamodb2/test_highlevel.py
@@ -314,3 +314,30 @@ class DynamoDBv2Test(unittest.TestCase):
'friends': set([]),
})
self.assertTrue(penny_created)
+
+ def test_unprocessed_batch_writes(self):
+ # Create a very limited table w/ low throughput.
+ users = Table.create('slow_users', schema=[
+ HashKey('user_id'),
+ ], throughput={
+ 'read': 1,
+ 'write': 1,
+ })
+ self.addCleanup(users.delete)
+
+ # Wait for it.
+ time.sleep(60)
+
+ with users.batch_write() as batch:
+ for i in range(500):
+ batch.put_item(data={
+ 'user_id': str(i),
+ 'name': 'Droid #{0}'.format(i),
+ })
+
+ # Before ``__exit__`` runs, we should have a bunch of unprocessed
+ # items.
+ self.assertTrue(len(batch._unprocessed) > 0)
+
+ # Post-__exit__, they should all be gone.
+ self.assertEqual(len(batch._unprocessed), 0)
diff --git a/tests/integration/ec2/test_connection.py b/tests/integration/ec2/test_connection.py
index ef1080b0..fc7137ab 100644
--- a/tests/integration/ec2/test_connection.py
+++ b/tests/integration/ec2/test_connection.py
@@ -32,9 +32,10 @@ import socket
from nose.plugins.attrib import attr
from boto.ec2.connection import EC2Connection
+from boto.exception import EC2ResponseError
-class EC2ConnectionTest (unittest.TestCase):
+class EC2ConnectionTest(unittest.TestCase):
ec2 = True
@attr('notdefault')
@@ -190,3 +191,51 @@ class EC2ConnectionTest (unittest.TestCase):
assert l[0].product_codes[0] == demo_paid_ami_product_code
print '--- tests completed ---'
+
+ def test_dry_run(self):
+ c = EC2Connection()
+ dry_run_msg = 'Request would have succeeded, but DryRun flag is set.'
+
+ try:
+ rs = c.get_all_images(dry_run=True)
+ self.fail("Should have gotten an exception")
+ except EC2ResponseError, e:
+ self.assertTrue(dry_run_msg in str(e))
+
+ try:
+ rs = c.run_instances(
+ image_id='ami-a0cd60c9',
+ instance_type='m1.small',
+ dry_run=True
+ )
+ self.fail("Should have gotten an exception")
+ except EC2ResponseError, e:
+ self.assertTrue(dry_run_msg in str(e))
+
+ # Need an actual instance for the rest of this...
+ rs = c.run_instances(
+ image_id='ami-a0cd60c9',
+ instance_type='m1.small'
+ )
+ time.sleep(120)
+
+ try:
+ rs = c.stop_instances(
+ instance_ids=[rs.instances[0].id],
+ dry_run=True
+ )
+ self.fail("Should have gotten an exception")
+ except EC2ResponseError, e:
+ self.assertTrue(dry_run_msg in str(e))
+
+ try:
+ rs = c.terminate_instances(
+ instance_ids=[rs.instances[0].id],
+ dry_run=True
+ )
+ self.fail("Should have gotten an exception")
+ except EC2ResponseError, e:
+ self.assertTrue(dry_run_msg in str(e))
+
+ # And kill it.
+ rs.instances[0].terminate()
diff --git a/tests/integration/ec2/vpc/test_connection.py b/tests/integration/ec2/vpc/test_connection.py
index 45fd82f4..56bab462 100644
--- a/tests/integration/ec2/vpc/test_connection.py
+++ b/tests/integration/ec2/vpc/test_connection.py
@@ -35,9 +35,14 @@ class TestVPCConnection(unittest.TestCase):
vpc = self.api.create_vpc('10.0.0.0/16')
self.addCleanup(self.api.delete_vpc, vpc.id)
+ # Need time for the VPC to be in place. :/
+ time.sleep(5)
self.subnet = self.api.create_subnet(vpc.id, '10.0.0.0/24')
self.addCleanup(self.api.delete_subnet, self.subnet.id)
+ # Need time for the subnet to be in place.
+ time.sleep(10)
+
def terminate_instance(self, instance):
instance.terminate()
for i in xrange(300):
@@ -77,7 +82,7 @@ class TestVPCConnection(unittest.TestCase):
self.assertEqual(len(retrieved_instance.interfaces), 1)
interface = retrieved_instance.interfaces[0]
-
+
private_ip_addresses = interface.private_ip_addresses
self.assertEqual(len(private_ip_addresses), 4)
self.assertEqual(private_ip_addresses[0].private_ip_address,
@@ -90,6 +95,46 @@ class TestVPCConnection(unittest.TestCase):
self.assertEqual(private_ip_addresses[3].private_ip_address,
'10.0.0.24')
+ def test_associate_public_ip(self):
+ # Supplying basically nothing ought to work.
+ interface = NetworkInterfaceSpecification(
+ associate_public_ip_address=True,
+ subnet_id=self.subnet.id,
+ # Just for testing.
+ delete_on_termination=True
+ )
+ interfaces = NetworkInterfaceCollection(interface)
+
+ reservation = self.api.run_instances(
+ image_id='ami-a0cd60c9',
+ instance_type='m1.small',
+ network_interfaces=interfaces
+ )
+ instance = reservation.instances[0]
+ self.addCleanup(self.terminate_instance, instance)
+
+ # Give it a **LONG** time to start up.
+ # Because the public IP won't be there right away.
+ time.sleep(60)
+
+ retrieved = self.api.get_all_reservations(
+ instance_ids=[
+ instance.id
+ ]
+ )
+ self.assertEqual(len(retrieved), 1)
+ retrieved_instances = retrieved[0].instances
+ self.assertEqual(len(retrieved_instances), 1)
+ retrieved_instance = retrieved_instances[0]
+
+ self.assertEqual(len(retrieved_instance.interfaces), 1)
+ interface = retrieved_instance.interfaces[0]
+
+ # There ought to be a public IP there.
+ # We can't reason about the IP itself, so just make sure it vaguely
+ # resembles an IP (& isn't empty/``None``)...
+ self.assertTrue(interface.publicIp.count('.') >= 3)
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/unit/dynamodb2/test_table.py b/tests/unit/dynamodb2/test_table.py
index be355855..9b247d64 100644
--- a/tests/unit/dynamodb2/test_table.py
+++ b/tests/unit/dynamodb2/test_table.py
@@ -1476,6 +1476,89 @@ class TableTestCase(unittest.TestCase):
self.assertEqual(mock_batch.call_count, 2)
+ def test_batch_write_unprocessed_items(self):
+ unprocessed = {
+ 'UnprocessedItems': {
+ 'users': [
+ {
+ 'PutRequest': {
+ 'username': {
+ 'S': 'jane',
+ },
+ 'date_joined': {
+ 'N': 12342547
+ }
+ },
+ },
+ ],
+ },
+ }
+
+ # Test enqueuing the unprocessed bits.
+ with mock.patch.object(
+ self.users.connection,
+ 'batch_write_item',
+ return_value=unprocessed) as mock_batch:
+ with self.users.batch_write() as batch:
+ self.assertEqual(len(batch._unprocessed), 0)
+
+ # Trash the ``resend_unprocessed`` method so that we don't
+ # infinite loop forever here.
+ batch.resend_unprocessed = lambda: True
+
+ batch.put_item(data={
+ 'username': 'jane',
+ 'date_joined': 12342547
+ })
+ batch.delete_item(username='johndoe')
+ batch.put_item(data={
+ 'username': 'alice',
+ 'date_joined': 12342888
+ })
+
+ self.assertEqual(len(batch._unprocessed), 1)
+
+ # Now test resending those unprocessed items.
+ with mock.patch.object(
+ self.users.connection,
+ 'batch_write_item',
+ return_value={}) as mock_batch:
+ with self.users.batch_write() as batch:
+ self.assertEqual(len(batch._unprocessed), 0)
+
+ # Toss in faked unprocessed items, as though a previous batch
+ # had failed.
+ batch._unprocessed = [
+ {
+ 'PutRequest': {
+ 'username': {
+ 'S': 'jane',
+ },
+ 'date_joined': {
+ 'N': 12342547
+ }
+ },
+ },
+ ]
+
+ batch.put_item(data={
+ 'username': 'jane',
+ 'date_joined': 12342547
+ })
+ batch.delete_item(username='johndoe')
+ batch.put_item(data={
+ 'username': 'alice',
+ 'date_joined': 12342888
+ })
+
+ # Flush, to make sure everything has been processed.
+ # Unprocessed items should still be hanging around.
+ batch.flush()
+ self.assertEqual(len(batch._unprocessed), 1)
+
+ # Post-exit, this should be emptied.
+ self.assertEqual(len(batch._unprocessed), 0)
+
def test__build_filters(self):
filters = self.users._build_filters({
'username__eq': 'johndoe',
diff --git a/tests/unit/ec2/autoscale/test_group.py b/tests/unit/ec2/autoscale/test_group.py
index 659527b0..6500a63a 100644
--- a/tests/unit/ec2/autoscale/test_group.py
+++ b/tests/unit/ec2/autoscale/test_group.py
@@ -233,12 +233,12 @@ class TestLaunchConfiguration(AWSMockServiceTestCase):
self.assert_request_parameters({
'Action': 'CreateLaunchConfiguration',
- 'BlockDeviceMapping.1.DeviceName': '/dev/sdf',
- 'BlockDeviceMapping.1.Ebs.DeleteOnTermination': 'false',
- 'BlockDeviceMapping.1.Ebs.SnapshotId': 'snap-12345',
- 'BlockDeviceMapping.2.DeviceName': '/dev/sdg',
- 'BlockDeviceMapping.2.Ebs.DeleteOnTermination': 'false',
- 'BlockDeviceMapping.2.Ebs.SnapshotId': 'snap-12346',
+ 'BlockDeviceMappings.member.1.DeviceName': '/dev/sdf',
+ 'BlockDeviceMappings.member.1.Ebs.DeleteOnTermination': 'false',
+ 'BlockDeviceMappings.member.1.Ebs.SnapshotId': 'snap-12345',
+ 'BlockDeviceMappings.member.2.DeviceName': '/dev/sdg',
+ 'BlockDeviceMappings.member.2.Ebs.DeleteOnTermination': 'false',
+ 'BlockDeviceMappings.member.2.Ebs.SnapshotId': 'snap-12346',
'EbsOptimized': 'false',
'LaunchConfigurationName': 'launch_config',
'ImageId': '123456',
diff --git a/tests/unit/ec2/test_connection.py b/tests/unit/ec2/test_connection.py
index 43b44db3..eab36160 100644
--- a/tests/unit/ec2/test_connection.py
+++ b/tests/unit/ec2/test_connection.py
@@ -11,6 +11,7 @@ import boto.ec2
from boto.regioninfo import RegionInfo
from boto.ec2.connection import EC2Connection
from boto.ec2.snapshot import Snapshot
+from boto.ec2.reservedinstance import ReservedInstancesConfiguration
class TestEC2ConnectionBase(AWSMockServiceTestCase):
@@ -878,5 +879,143 @@ class TestTrimSnapshots(TestEC2ConnectionBase):
self.ec2.delete_snapshot = orig['delete_snapshot']
+class TestModifyReservedInstances(TestEC2ConnectionBase):
+ def default_body(self):
+ return """<ModifyReservedInstancesResponse xmlns='http://ec2.amazonaws.com/doc/2013-08-15/'>
+ <requestId>bef729b6-0731-4489-8881-2258746ae163</requestId>
+ <reservedInstancesModificationId>rimod-3aae219d-3d63-47a9-a7e9-e764example</reservedInstancesModificationId>
+</ModifyReservedInstancesResponse>"""
+
+ def test_serialized_api_args(self):
+ self.set_http_response(status_code=200)
+ response = self.ec2.modify_reserved_instances(
+ 'a-token-goes-here',
+ reserved_instance_ids=[
+ '2567o137-8a55-48d6-82fb-7258506bb497',
+ ],
+ target_configurations=[
+ ReservedInstancesConfiguration(
+ availability_zone='us-west-2c',
+ platform='EC2-VPC',
+ instance_count=3
+ ),
+ ]
+ )
+ self.assert_request_parameters({
+ 'Action': 'ModifyReservedInstances',
+ 'ClientToken': 'a-token-goes-here',
+ 'ReservedInstancesConfigurationSetItemType.0.AvailabilityZone': 'us-west-2c',
+ 'ReservedInstancesConfigurationSetItemType.0.InstanceCount': 3,
+ 'ReservedInstancesConfigurationSetItemType.0.Platform': 'EC2-VPC',
+ 'ReservedInstancesId.1': '2567o137-8a55-48d6-82fb-7258506bb497'
+ }, ignore_params_values=[
+ 'AWSAccessKeyId', 'SignatureMethod',
+ 'SignatureVersion', 'Timestamp',
+ 'Version'
+ ])
+
+ self.assertEqual(response, 'rimod-3aae219d-3d63-47a9-a7e9-e764example')
+
+
+class TestDescribeReservedInstancesModifications(TestEC2ConnectionBase):
+ def default_body(self):
+ return """<DescribeReservedInstancesModificationsResponse xmlns='http://ec2.amazonaws.com/doc/2013-08-15/'>
+ <requestId>eb4a6e3c-3689-445c-b536-19e38df35898</requestId>
+ <reservedInstancesModificationsSet>
+ <item>
+ <reservedInstancesModificationId>rimod-49b9433e-fdc7-464a-a6e5-9dabcexample</reservedInstancesModificationId>
+ <reservedInstancesSet>
+ <item>
+ <reservedInstancesId>2567o137-8a55-48d6-82fb-7258506bb497</reservedInstancesId>
+ </item>
+ </reservedInstancesSet>
+ <modificationResultSet>
+ <item>
+ <reservedInstancesId>9d5cb137-5d65-4479-b4ac-8c337example</reservedInstancesId>
+ <targetConfiguration>
+ <availabilityZone>us-east-1b</availabilityZone>
+ <platform>EC2-VPC</platform>
+ <instanceCount>1</instanceCount>
+ </targetConfiguration>
+ </item>
+ </modificationResultSet>
+ <createDate>2013-09-02T21:20:19.637Z</createDate>
+ <updateDate>2013-09-02T21:38:24.143Z</updateDate>
+ <effectiveDate>2013-09-02T21:00:00.000Z</effectiveDate>
+ <status>fulfilled</status>
+ <clientToken>token-f5b56c05-09b0-4d17-8d8c-c75d8a67b806</clientToken>
+ </item>
+ </reservedInstancesModificationsSet>
+</DescribeReservedInstancesModificationsResponse>"""
+
+ def test_serialized_api_args(self):
+ self.set_http_response(status_code=200)
+ response = self.ec2.describe_reserved_instances_modifications(
+ reserved_instances_modification_ids=[
+ '2567o137-8a55-48d6-82fb-7258506bb497'
+ ],
+ filters={
+ 'status': 'processing',
+ }
+ )
+ self.assert_request_parameters({
+ 'Action': 'DescribeReservedInstancesModifications',
+ 'Filter.1.Name': 'status',
+ 'Filter.1.Value.1': 'processing',
+ 'ReservedInstancesModificationId.1': '2567o137-8a55-48d6-82fb-7258506bb497'
+ }, ignore_params_values=[
+ 'AWSAccessKeyId', 'SignatureMethod',
+ 'SignatureVersion', 'Timestamp',
+ 'Version'
+ ])
+
+ # Make sure the response was parsed correctly.
+ self.assertEqual(
+ response[0].modification_id,
+ 'rimod-49b9433e-fdc7-464a-a6e5-9dabcexample'
+ )
+ self.assertEqual(
+ response[0].create_date,
+ datetime(2013, 9, 2, 21, 20, 19, 637000)
+ )
+ self.assertEqual(
+ response[0].update_date,
+ datetime(2013, 9, 2, 21, 38, 24, 143000)
+ )
+ self.assertEqual(
+ response[0].effective_date,
+ datetime(2013, 9, 2, 21, 0, 0, 0)
+ )
+ self.assertEqual(
+ response[0].status,
+ 'fulfilled'
+ )
+ self.assertEqual(
+ response[0].status_message,
+ None
+ )
+ self.assertEqual(
+ response[0].client_token,
+ 'token-f5b56c05-09b0-4d17-8d8c-c75d8a67b806'
+ )
+ self.assertEqual(
+ response[0].reserved_instances[0].id,
+ '2567o137-8a55-48d6-82fb-7258506bb497'
+ )
+ self.assertEqual(
+ response[0].modification_results[0].availability_zone,
+ 'us-east-1b'
+ )
+ self.assertEqual(
+ response[0].modification_results[0].platform,
+ 'EC2-VPC'
+ )
+ self.assertEqual(
+ response[0].modification_results[0].instance_count,
+ 1
+ )
+ self.assertEqual(len(response), 1)
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/unit/ec2/test_networkinterface.py b/tests/unit/ec2/test_networkinterface.py
index 4674823d..81fa4aef 100644
--- a/tests/unit/ec2/test_networkinterface.py
+++ b/tests/unit/ec2/test_networkinterface.py
@@ -23,7 +23,7 @@
from tests.unit import unittest
-
+from boto.exception import BotoClientError
from boto.ec2.networkinterface import NetworkInterfaceCollection
from boto.ec2.networkinterface import NetworkInterfaceSpecification
from boto.ec2.networkinterface import PrivateIPAddress
@@ -42,7 +42,8 @@ class TestNetworkInterfaceCollection(unittest.TestCase):
description='description1',
private_ip_address='10.0.0.54', delete_on_termination=False,
private_ip_addresses=[self.private_ip_address1,
- self.private_ip_address2])
+ self.private_ip_address2]
+ )
self.private_ip_address3 = PrivateIPAddress(
private_ip_address='10.0.1.10', primary=False)
@@ -54,8 +55,18 @@ class TestNetworkInterfaceCollection(unittest.TestCase):
groups=['group_id1', 'group_id2'],
private_ip_address='10.0.1.54', delete_on_termination=False,
private_ip_addresses=[self.private_ip_address3,
+ self.private_ip_address4]
+ )
+
+ self.network_interfaces_spec3 = NetworkInterfaceSpecification(
+ device_index=0, subnet_id='subnet_id2',
+ description='description2',
+ groups=['group_id1', 'group_id2'],
+ private_ip_address='10.0.1.54', delete_on_termination=False,
+ private_ip_addresses=[self.private_ip_address3,
self.private_ip_address4],
- associate_public_ip_address=True)
+ associate_public_ip_address=True
+ )
def test_param_serialization(self):
collection = NetworkInterfaceCollection(self.network_interfaces_spec1,
@@ -63,30 +74,29 @@ class TestNetworkInterfaceCollection(unittest.TestCase):
params = {}
collection.build_list_params(params)
self.assertDictEqual(params, {
- 'NetworkInterface.1.DeviceIndex': '1',
- 'NetworkInterface.1.DeleteOnTermination': 'false',
- 'NetworkInterface.1.Description': 'description1',
- 'NetworkInterface.1.PrivateIpAddress': '10.0.0.54',
- 'NetworkInterface.1.SubnetId': 'subnet_id',
- 'NetworkInterface.1.PrivateIpAddresses.1.Primary': 'false',
- 'NetworkInterface.1.PrivateIpAddresses.1.PrivateIpAddress':
+ 'NetworkInterface.0.DeviceIndex': '1',
+ 'NetworkInterface.0.DeleteOnTermination': 'false',
+ 'NetworkInterface.0.Description': 'description1',
+ 'NetworkInterface.0.PrivateIpAddress': '10.0.0.54',
+ 'NetworkInterface.0.SubnetId': 'subnet_id',
+ 'NetworkInterface.0.PrivateIpAddresses.0.Primary': 'false',
+ 'NetworkInterface.0.PrivateIpAddresses.0.PrivateIpAddress':
'10.0.0.10',
- 'NetworkInterface.1.PrivateIpAddresses.2.Primary': 'false',
- 'NetworkInterface.1.PrivateIpAddresses.2.PrivateIpAddress':
+ 'NetworkInterface.0.PrivateIpAddresses.1.Primary': 'false',
+ 'NetworkInterface.0.PrivateIpAddresses.1.PrivateIpAddress':
'10.0.0.11',
- 'NetworkInterface.2.DeviceIndex': '2',
- 'NetworkInterface.2.Description': 'description2',
- 'NetworkInterface.2.DeleteOnTermination': 'false',
- 'NetworkInterface.2.PrivateIpAddress': '10.0.1.54',
- 'NetworkInterface.2.SubnetId': 'subnet_id2',
- 'NetworkInterface.2.AssociatePublicIpAddress': 'true',
- 'NetworkInterface.2.SecurityGroupId.1': 'group_id1',
- 'NetworkInterface.2.SecurityGroupId.2': 'group_id2',
- 'NetworkInterface.2.PrivateIpAddresses.1.Primary': 'false',
- 'NetworkInterface.2.PrivateIpAddresses.1.PrivateIpAddress':
+ 'NetworkInterface.1.DeviceIndex': '2',
+ 'NetworkInterface.1.Description': 'description2',
+ 'NetworkInterface.1.DeleteOnTermination': 'false',
+ 'NetworkInterface.1.PrivateIpAddress': '10.0.1.54',
+ 'NetworkInterface.1.SubnetId': 'subnet_id2',
+ 'NetworkInterface.1.SecurityGroupId.0': 'group_id1',
+ 'NetworkInterface.1.SecurityGroupId.1': 'group_id2',
+ 'NetworkInterface.1.PrivateIpAddresses.0.Primary': 'false',
+ 'NetworkInterface.1.PrivateIpAddresses.0.PrivateIpAddress':
'10.0.1.10',
- 'NetworkInterface.2.PrivateIpAddresses.2.Primary': 'false',
- 'NetworkInterface.2.PrivateIpAddresses.2.PrivateIpAddress':
+ 'NetworkInterface.1.PrivateIpAddresses.1.Primary': 'false',
+ 'NetworkInterface.1.PrivateIpAddresses.1.PrivateIpAddress':
'10.0.1.11',
})
@@ -99,44 +109,92 @@ class TestNetworkInterfaceCollection(unittest.TestCase):
# we're just checking a few keys to make sure we get the proper
# prefix.
self.assertDictEqual(params, {
- 'LaunchSpecification.NetworkInterface.1.DeviceIndex': '1',
- 'LaunchSpecification.NetworkInterface.1.DeleteOnTermination':
+ 'LaunchSpecification.NetworkInterface.0.DeviceIndex': '1',
+ 'LaunchSpecification.NetworkInterface.0.DeleteOnTermination':
'false',
- 'LaunchSpecification.NetworkInterface.1.Description':
+ 'LaunchSpecification.NetworkInterface.0.Description':
'description1',
- 'LaunchSpecification.NetworkInterface.1.PrivateIpAddress':
+ 'LaunchSpecification.NetworkInterface.0.PrivateIpAddress':
'10.0.0.54',
- 'LaunchSpecification.NetworkInterface.1.SubnetId': 'subnet_id',
- 'LaunchSpecification.NetworkInterface.1.PrivateIpAddresses.1.Primary':
+ 'LaunchSpecification.NetworkInterface.0.SubnetId': 'subnet_id',
+ 'LaunchSpecification.NetworkInterface.0.PrivateIpAddresses.0.Primary':
'false',
- 'LaunchSpecification.NetworkInterface.1.PrivateIpAddresses.1.PrivateIpAddress':
+ 'LaunchSpecification.NetworkInterface.0.PrivateIpAddresses.0.PrivateIpAddress':
'10.0.0.10',
- 'LaunchSpecification.NetworkInterface.1.PrivateIpAddresses.2.Primary': 'false',
- 'LaunchSpecification.NetworkInterface.1.PrivateIpAddresses.2.PrivateIpAddress':
+ 'LaunchSpecification.NetworkInterface.0.PrivateIpAddresses.1.Primary': 'false',
+ 'LaunchSpecification.NetworkInterface.0.PrivateIpAddresses.1.PrivateIpAddress':
'10.0.0.11',
- 'LaunchSpecification.NetworkInterface.2.DeviceIndex': '2',
- 'LaunchSpecification.NetworkInterface.2.Description':
+ 'LaunchSpecification.NetworkInterface.1.DeviceIndex': '2',
+ 'LaunchSpecification.NetworkInterface.1.Description':
'description2',
- 'LaunchSpecification.NetworkInterface.2.DeleteOnTermination':
+ 'LaunchSpecification.NetworkInterface.1.DeleteOnTermination':
'false',
- 'LaunchSpecification.NetworkInterface.2.PrivateIpAddress':
+ 'LaunchSpecification.NetworkInterface.1.PrivateIpAddress':
'10.0.1.54',
- 'LaunchSpecification.NetworkInterface.2.SubnetId': 'subnet_id2',
- 'LaunchSpecification.NetworkInterface.2.AssociatePublicIpAddress': 'true',
- 'LaunchSpecification.NetworkInterface.2.SecurityGroupId.1':
+ 'LaunchSpecification.NetworkInterface.1.SubnetId': 'subnet_id2',
+ 'LaunchSpecification.NetworkInterface.1.SecurityGroupId.0':
'group_id1',
- 'LaunchSpecification.NetworkInterface.2.SecurityGroupId.2':
+ 'LaunchSpecification.NetworkInterface.1.SecurityGroupId.1':
'group_id2',
- 'LaunchSpecification.NetworkInterface.2.PrivateIpAddresses.1.Primary':
+ 'LaunchSpecification.NetworkInterface.1.PrivateIpAddresses.0.Primary':
'false',
- 'LaunchSpecification.NetworkInterface.2.PrivateIpAddresses.1.PrivateIpAddress':
+ 'LaunchSpecification.NetworkInterface.1.PrivateIpAddresses.0.PrivateIpAddress':
'10.0.1.10',
- 'LaunchSpecification.NetworkInterface.2.PrivateIpAddresses.2.Primary':
+ 'LaunchSpecification.NetworkInterface.1.PrivateIpAddresses.1.Primary':
'false',
- 'LaunchSpecification.NetworkInterface.2.PrivateIpAddresses.2.PrivateIpAddress':
+ 'LaunchSpecification.NetworkInterface.1.PrivateIpAddresses.1.PrivateIpAddress':
'10.0.1.11',
})
+ def test_cant_use_public_ip(self):
+ collection = NetworkInterfaceCollection(self.network_interfaces_spec3,
+ self.network_interfaces_spec1)
+ params = {}
+
+ # First, verify we can't incorrectly create multiple interfaces with
+ # on having a public IP.
+ with self.assertRaises(BotoClientError):
+ collection.build_list_params(params, prefix='LaunchSpecification.')
+
+ # Next, ensure it can't be on device index 1.
+ self.network_interfaces_spec3.device_index = 1
+ collection = NetworkInterfaceCollection(self.network_interfaces_spec3)
+ params = {}
+
+ with self.assertRaises(BotoClientError):
+ collection.build_list_params(params, prefix='LaunchSpecification.')
+
+ def test_public_ip(self):
+ # With public IP.
+ collection = NetworkInterfaceCollection(self.network_interfaces_spec3)
+ params = {}
+ collection.build_list_params(params, prefix='LaunchSpecification.')
+
+ self.assertDictEqual(params, {
+ 'LaunchSpecification.NetworkInterface.0.AssociatePublicIpAddress':
+ 'true',
+ 'LaunchSpecification.NetworkInterface.0.DeviceIndex': '0',
+ 'LaunchSpecification.NetworkInterface.0.DeleteOnTermination':
+ 'false',
+ 'LaunchSpecification.NetworkInterface.0.Description':
+ 'description2',
+ 'LaunchSpecification.NetworkInterface.0.PrivateIpAddress':
+ '10.0.1.54',
+ 'LaunchSpecification.NetworkInterface.0.SubnetId': 'subnet_id2',
+ 'LaunchSpecification.NetworkInterface.0.PrivateIpAddresses.0.Primary':
+ 'false',
+ 'LaunchSpecification.NetworkInterface.0.PrivateIpAddresses.0.PrivateIpAddress':
+ '10.0.1.10',
+ 'LaunchSpecification.NetworkInterface.0.PrivateIpAddresses.1.Primary':
+ 'false',
+ 'LaunchSpecification.NetworkInterface.0.PrivateIpAddresses.1.PrivateIpAddress':
+ '10.0.1.11',
+ 'LaunchSpecification.NetworkInterface.0.SecurityGroupId.0':
+ 'group_id1',
+ 'LaunchSpecification.NetworkInterface.0.SecurityGroupId.1':
+ 'group_id2',
+ })
+
if __name__ == '__main__':
unittest.main()