Browse files

Merge branch 'release-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.
  ...
  • Loading branch information...
2 parents ad40af1 + 0efd52e commit 7b70eafa9c91dfd5842eb6dfa7b7da0fee8074ff @toastdriven toastdriven committed Sep 12, 2013
Showing with 2,333 additions and 600 deletions.
  1. +7 −10 README.rst
  2. +1 −1 bin/dynamodb_load
  3. +1 −1 boto/__init__.py
  4. +1 −0 boto/dynamodb2/layer1.py
  5. +45 −6 boto/dynamodb2/table.py
  6. +25 −8 boto/ec2/address.py
  7. +1 −1 boto/ec2/blockdevicemapping.py
  8. +756 −124 boto/ec2/connection.py
  9. +14 −6 boto/ec2/ec2object.py
  10. +46 −24 boto/ec2/image.py
  11. +60 −36 boto/ec2/instance.py
  12. +9 −9 boto/ec2/keypair.py
  13. +40 −7 boto/ec2/networkinterface.py
  14. +7 −4 boto/ec2/placementgroup.py
  15. +117 −2 boto/ec2/reservedinstance.py
  16. +46 −21 boto/ec2/securitygroup.py
  17. +39 −22 boto/ec2/snapshot.py
  18. +6 −4 boto/ec2/spotdatafeedsubscription.py
  19. +5 −2 boto/ec2/spotinstancerequest.py
  20. +36 −16 boto/ec2/volume.py
  21. +198 −71 boto/opsworks/layer1.py
  22. +2 −2 boto/sdb/db/manager/__init__.py
  23. +11 −7 boto/sdb/db/manager/sdbmanager.py
  24. +14 −11 boto/sdb/db/model.py
  25. +1 −1 boto/sns/connection.py
  26. +8 −14 boto/swf/layer2.py
  27. +248 −47 boto/vpc/__init__.py
  28. +5 −2 boto/vpc/vpc.py
  29. +5 −2 boto/vpc/vpnconnection.py
  30. +10 −6 boto/vpc/vpngateway.py
  31. +8 −32 docs/source/index.rst
  32. +2 −15 docs/source/ref/contrib.rst
  33. +28 −0 docs/source/ref/opsworks.rst
  34. +0 −7 docs/source/ref/s3.rst
  35. +9 −22 docs/source/ref/sdb_db.rst
  36. +4 −1 docs/source/ref/swf.rst
  37. +21 −0 docs/source/releasenotes/releasenotes_template.rst
  38. +1 −1 docs/source/releasenotes/v2.0.0.rst
  39. +40 −0 docs/source/releasenotes/v2.13.0.rst
  40. +1 −1 docs/source/releasenotes/v2.9.8.rst
  41. +1 −1 docs/source/s3_tut.rst
  42. +27 −0 tests/integration/dynamodb2/test_highlevel.py
  43. +50 −1 tests/integration/ec2/test_connection.py
  44. +46 −1 tests/integration/ec2/vpc/test_connection.py
  45. +83 −0 tests/unit/dynamodb2/test_table.py
  46. +6 −6 tests/unit/ec2/autoscale/test_group.py
  47. +139 −0 tests/unit/ec2/test_connection.py
  48. +103 −45 tests/unit/ec2/test_networkinterface.py
View
17 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
View
2 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):
View
2 boto/__init__.py
@@ -36,7 +36,7 @@
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' % (
View
1 boto/dynamodb2/layer1.py
@@ -1491,6 +1491,7 @@ def make_request(self, action, body):
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)
View
51 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 @@ def __init__(self, table):
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 @@ def flush(self):
}
})
- 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)
+ )
View
33 boto/ec2/address.py
@@ -71,33 +71,50 @@ def endElement(self, name, value, connection):
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
+ )
View
2 boto/ec2/blockdevicemapping.py
@@ -118,7 +118,7 @@ def endElement(self, name, value, connection):
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:
View
880 boto/ec2/connection.py
756 additions, 124 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
View
20 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 @@ def startElement(self, name, attrs, connection):
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 @@ def startElement(self, name, attrs, connection):
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 @@ def add_tag(self, key, value=''):
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 @@ def remove_tag(self, key, value=None):
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]
View
70 boto/ec2/image.py
@@ -130,7 +130,7 @@ def endElement(self, name, value, connection):
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 @@ def update(self, validate=False):
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 @@ def run(self, min_count=1, max_count=1, key_name=None,
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 @@ def run(self, min_count=1, max_count=1, key_name=None,
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:
View
96 boto/ec2/instance.py
@@ -149,9 +149,9 @@ def endElement(self, name, value, connection):
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 @@ def endElement(self, name, value, connection):
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 @@ def update(self, validate=False):
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 @@ def update(self, validate=False):
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 @@ def stop(self, force=False):
: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 @@ def use_ip(self, ip_address):
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 @@ def get_attribute(self, attribute):
: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 @@ def modify_attribute(self, attribute, value):
: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 @@ def reset_attribute(self, attribute):
: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 @@ def create_image(
: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:
View
18 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.exception import BotoClientError
class KeyPair(EC2Object):
-
+
def __init__(self, connection=None):
EC2Object.__init__(self, connection)
self.name = None
@@ -48,20 +48,20 @@ def endElement(self, name, value, connection):
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 @@ def save(self, directory_path):
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 @@ def save(self, directory_path):
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 @@ def copy_to_region(self, region):
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
View
47 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 @@ def endElement(self, name, value, connection):
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 @@ def __init__(self, *interfaces):
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 @@ def build_list_params(self, params, prefix=''):
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):
View
11 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.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 @@ def endElement(self, name, value, connection):
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
+ )
View
119 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 @@ def describe(self):
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 @@ def endElement(self, name, value, connection):
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)
View
67 boto/ec2/securitygroup.py
@@ -82,14 +82,21 @@ def endElement(self, name, value, connection):
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 @@ def add_rule(self, ip_protocol, from_port, to_port,
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 @@ def remove_rule(self, ip_protocol, from_port, to_port,
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 @@ def authorize(self, ip_protocol=None, from_port=None, to_port=None,
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 @@ def revoke(self, ip_protocol=None, from_port=None, to_port=None,
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,21 +265,26 @@ def copy_to_region(self, region, name=None):
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:
grant_nom = grant.name or grant.group_id
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 @@ def instances(self):
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
View
61 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.zone import Zone
class Snapshot(TaggedEC2Object):
-
+
AttrName = 'createVolumePermission'
-
+
def __init__(self, connection=None):
TaggedEC2Object.__init__(self, connection)
self.id = None
@@ -72,7 +72,7 @@ def _update(self, updated):
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 @@ def update(self, validate=False):
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 @@ def create_volume(self, zone, size=None, volume_type=None, iops=None):
"""
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 @@ def endElement(self, name, value, connection):
setattr(self, name, value)
-
+
View
10 boto/ec2/spotdatafeedsubscription.py
@@ -26,7 +26,7 @@
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 @@ def startElement(self, name, attrs, connection):
return self.fault
else:
return None
-
+
def endElement(self, name, value, connection):
if name == 'ownerId':
self.owner_id = value
@@ -58,6 +58,8 @@ def endElement(self, name, value, connection):
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
+ )
View
7 boto/ec2/spotinstancerequest.py
@@ -184,5 +184,8 @@ def endElement(self, name, value, connection):
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
+ )
View
52 boto/ec2/volume.py
@@ -98,7 +98,7 @@ def endElement(self, name, value, connection):
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,24 +110,27 @@ def update(self, validate=False):
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])
elif validate:
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 @@ def attach(self, instance_id, device):
: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 @@ def detach(self, force=False):
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 @@ def create_snapshot(self, description=None):
: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 @@ def attachment_state(self):
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 @@ def snapshots(self, owner=None, restorable_by=None):
: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:
View
269 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 @@ def attach_elastic_load_balancer(self, elastic_load_balancer_name,
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 @@ def clone_stack(self, source_stack_id, service_role_arn, name=None,
: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 @@ def clone_stack(self, source_stack_id, service_role_arn, name=None,
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 @@ def clone_stack(self, source_stack_id, service_role_arn, name=None,
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 @@ def clone_stack(self, source_stack_id, service_role_arn, name=None,
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 @@ def clone_stack(self, source_stack_id, service_role_arn, name=None,
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 @@ def clone_stack(self, source_stack_id, service_role_arn, name=None,
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 @@ def create_deployment(self, stack_id, command, app_id=None,
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 @@ def create_instance(self, stack_id, layer_ids, instance_type,
: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 @@ def create_instance(self, stack_id, layer_ids, instance_type,
: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 @@ def create_instance(self, stack_id, layer_ids, instance_type,
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 @@ def create_layer(self, stack_id, type, name, shortname, attributes=None,
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 @@ def create_layer(self, stack_id, type, name, shortname, attributes=None,
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 @@ def create_stack(self, name, region, service_role_arn,
: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 @@ def create_stack(self, name, region, service_role_arn,
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 @@ def create_stack(self, name, region, service_role_arn,
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 @@ def create_stack(self, name, region, service_role_arn,
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 @@ def create_stack(self, name, region, service_role_arn,
'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 @@ def create_stack(self, name, region, service_role_arn,
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 @@ def describe_apps(self, stack_id=None, app_ids=None):
"""
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 @@ def describe_commands(self, deployment_id=None, instance_id=None,
"""
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 @@ def describe_deployments(self, stack_id=None, app_id=None,
"""
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 @@ def describe_deployments(self, stack_id=None, app_id=None,
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 @@ def describe_elastic_load_balancers(self, stack_id=None, layer_ids=None):
"""
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 @@ def describe_elastic_load_balancers(self, stack_id=None, layer_ids=None):
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 @@ def describe_layers(self, stack_id, layer_ids=None):
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 @@ def describe_load_based_auto_scaling(self, layer_ids):
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 @@ def describe_raid_arrays(self, instance_id=None, raid_array_ids=None):
"""
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 @@ def describe_time_based_auto_scaling(self, instance_ids):
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 @@ def describe_volumes(self, instance_id=None, raid_array_id=None,
"""
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 @@ def update_instance(self, instance_id, layer_ids=None,
: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 @@ def update_stack(self, stack_id, name=None, attributes=None,
service_role_arn=None,
default_instance_profile_arn=None, default_os=None,
hostname_theme=None, default_availability_zone=None,
- 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 @@ def update_stack(self, stack_id, name=None, attributes=None,
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 @@ def update_stack(self, stack_id, name=None, attributes=None,
returns a host name based on the current theme.