From 297a3e0fc4b132ef315d3fe811f15b2332fa8fbd Mon Sep 17 00:00:00 2001 From: Mitch Garnaat Date: Wed, 14 Jul 2010 06:57:10 -0400 Subject: [PATCH 1/2] Checking in initial support for cluster instance type and placement groups. To be merged to master upon completion. --- boto/ec2/connection.py | 72 ++++++++++++++++++++++++++++++++++---- boto/ec2/instance.py | 4 +++ boto/ec2/placementgroup.py | 51 +++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 6 deletions(-) create mode 100644 boto/ec2/placementgroup.py diff --git a/boto/ec2/connection.py b/boto/ec2/connection.py index 8109db10a5..e2840e11aa 100644 --- a/boto/ec2/connection.py +++ b/boto/ec2/connection.py @@ -1,4 +1,4 @@ -# Copyright (c) 2006-2009 Mitch Garnaat http://garnaat.org/ +# Copyright (c) 2006-2010 Mitch Garnaat http://garnaat.org/ # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the @@ -46,13 +46,14 @@ from boto.ec2.spotpricehistory import SpotPriceHistory from boto.ec2.spotdatafeedsubscription import SpotDatafeedSubscription from boto.ec2.bundleinstance import BundleInstanceTask +from boto.ec2.placementgroup import PlacementGroup from boto.exception import EC2ResponseError #boto.set_stream_logger('ec2') class EC2Connection(AWSQueryConnection): - APIVersion = boto.config.get('Boto', 'ec2_version', '2009-11-30') + APIVersion = boto.config.get('Boto', 'ec2_version', '2010-06-15') DefaultRegionName = boto.config.get('Boto', 'ec2_region_name', 'us-east-1') DefaultRegionEndpoint = boto.config.get('Boto', 'ec2_region_endpoint', 'ec2.amazonaws.com') @@ -69,7 +70,7 @@ def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, B{Note:} The host argument is overridden by the host specified in the boto configuration file. """ if not region: - region = RegionInfo(self, self.DefaultRegionName, self.DefaultRegionEndpoint, EC2Connection) + region = RegionInfo(self, self.DefaultRegionName, self.DefaultRegionEndpoint) self.region = region AWSQueryConnection.__init__(self, aws_access_key_id, aws_secret_access_key, @@ -382,7 +383,8 @@ def run_instances(self, image_id, min_count=1, max_count=1, monitoring_enabled=False, subnet_id=None, block_device_map=None, disable_api_termination=False, - instance_initiated_shutdown_behavior=None): + instance_initiated_shutdown_behavior=None, + placement_group=None): """ Runs an image on EC2. @@ -428,8 +430,9 @@ def run_instances(self, image_id, min_count=1, max_count=1, with the Image. :type disable_api_termination: bool - :param disable_api_termination: If True, the instances will be locked and will - not be able to be terminated via the API. + :param disable_api_termination: If True, the instances will be locked + and will not be able to be terminated + via the API. :type instance_initiated_shutdown_behavior: string :param instance_initiated_shutdown_behavior: Specifies whether the instance's @@ -439,6 +442,10 @@ def run_instances(self, image_id, min_count=1, max_count=1, owner. Valid values are: stop | terminate + :type placement_group: string + :param placement_group: If specified, this is the name of the placement + group in which the instance(s) will be launched. + :rtype: Reservation :return: The :class:`boto.ec2.instance.Reservation` associated with the request for machines """ @@ -463,6 +470,8 @@ def run_instances(self, image_id, min_count=1, max_count=1, params['InstanceType'] = instance_type if placement: params['Placement.AvailabilityZone'] = placement + if placement_group: + params['Placement.GroupName'] = placement_group if kernel_id: params['KernelId'] = kernel_id if ramdisk_id: @@ -1617,3 +1626,54 @@ def get_password_data(self, instance_id): rs = self.get_object('GetPasswordData', params, ResultSet) return rs.passwordData + # + # Cluster Placement Groups + # + + def get_all_placement_groups(self, groupnames=None): + """ + Get all placement groups associated with your account in a region. + + :type groupnames: list + :param groupnames: A list of the names of placement groups to retrieve. + If not provided, all placement groups will be returned. + + :rtype: list + :return: A list of :class:`boto.ec2.placementgroup.PlacementGroup` + """ + params = {} + if groupnames: + self.build_list_params(params, groupnames, 'GroupName') + return self.get_list('DescribePlacementGroups', params, [('item', PlacementGroup)]) + + def create_placement_group(self, name, strategy='cluster'): + """ + Create a new placement group for your account. + This will create the placement group within the region you + are currently connected to. + + :type name: string + :param name: The name of the new placement group + + :type strategy: string + :param strategy: The placement strategy of the new placement group. + Currently, the only acceptable value is "cluster". + + :rtype: :class:`boto.ec2.placementgroup.PlacementGroup` + :return: The newly created :class:`boto.ec2.keypair.KeyPair`. + """ + params = {'GroupName':name, 'Strategy':strategy} + group = self.get_status('CreatePlacementGroup', params) + return group + + def delete_placement_group(self, name): + """ + Delete a placement group from your account. + + :type key_name: string + :param key_name: The name of the keypair to delete + """ + params = {'GroupName':name} + return self.get_status('DeletePlacementGroup', params) + + diff --git a/boto/ec2/instance.py b/boto/ec2/instance.py index 78bf55d5d4..54dfe4ae92 100644 --- a/boto/ec2/instance.py +++ b/boto/ec2/instance.py @@ -100,6 +100,7 @@ def __init__(self, connection=None): self.root_device_type = None self.block_device_mapping = None self.state_reason = None + self.group_name = None def __repr__(self): return 'Instance:%s' % self.id @@ -185,6 +186,9 @@ def endElement(self, name, value, connection): self.persistent = True else: self.persistent = False + elif name == 'groupName': + if self._in_monitoring_element: + self.group_name = value else: setattr(self, name, value) diff --git a/boto/ec2/placementgroup.py b/boto/ec2/placementgroup.py new file mode 100644 index 0000000000..e1bbea621c --- /dev/null +++ b/boto/ec2/placementgroup.py @@ -0,0 +1,51 @@ +# Copyright (c) 2010 Mitch Garnaat http://garnaat.org/ +# +# Permission is hereby granted, free of charge, to any person obtaining a +# copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, dis- +# tribute, sublicense, and/or sell copies of the Software, and to permit +# persons to whom the Software is furnished to do so, subject to the fol- +# lowing conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +# OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- +# ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +# SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +""" +Represents an EC2 Placement Group +""" +from boto.ec2.ec2object import EC2Object +from boto.exception import BotoClientError + +class PlacementGroup(EC2Object): + + def __init__(self, connection=None, name=None, strategy=None, state=None): + EC2Object.__init__(self, connection) + self.name = name + self.strategy = strategy + self.state = state + + def __repr__(self): + return 'PlacementGroup:%s' % self.name + + def endElement(self, name, value, connection): + if name == 'groupName': + self.name = value + elif name == 'strategy': + self.strategy = value + elif name == 'state': + self.state = value + else: + setattr(self, name, value) + + def delete(self): + return self.connection.delete_placement_group(self.name) + + From 1c8a28b95f079f78c3715f8f09e45a866da7541f Mon Sep 17 00:00:00 2001 From: Mitch Garnaat Date: Wed, 21 Jul 2010 06:13:22 -0400 Subject: [PATCH 2/2] Improved some doc strings and added cluster paerams to Image.run method. --- boto/ec2/connection.py | 5 ++++- boto/ec2/image.py | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 6 deletions(-) diff --git a/boto/ec2/connection.py b/boto/ec2/connection.py index e2840e11aa..a0fcc4a6fa 100644 --- a/boto/ec2/connection.py +++ b/boto/ec2/connection.py @@ -407,7 +407,10 @@ def run_instances(self, image_id, min_count=1, max_count=1, :param user_data: The user data passed to the launched instances :type instance_type: string - :param instance_type: The type of instance to run (m1.small, m1.large, m1.xlarge) + :param instance_type: The type of instance to run. Current choices are: + m1.small | m1.large | m1.xlarge | c1.medium | + c1.xlarge | m2.xlarge | m2.2xlarge | + m2.4xlarge | cc1.4xlarge :type placement: string :param placement: The availability zone in which to launch the instances diff --git a/boto/ec2/image.py b/boto/ec2/image.py index c9b7fec725..8766f63cd2 100644 --- a/boto/ec2/image.py +++ b/boto/ec2/image.py @@ -117,7 +117,10 @@ def run(self, min_count=1, max_count=1, key_name=None, addressing_type=None, instance_type='m1.small', placement=None, kernel_id=None, ramdisk_id=None, monitoring_enabled=False, subnet_id=None, - block_device_map=None): + block_device_map=None, + disable_api_termination=False, + instance_initiated_shutdown_behavior=None, + placement_group=None): """ Runs this instance. @@ -140,10 +143,13 @@ def run(self, min_count=1, max_count=1, key_name=None, :param daddressing_type: :type instance_type: string - :param instance_type: The type of instance to run (m1.small, m1.large, m1.xlarge) + :param instance_type: The type of instance to run. Current choices are: + m1.small | m1.large | m1.xlarge | c1.medium | + c1.xlarge | m2.xlarge | m2.2xlarge | + m2.4xlarge | cc1.4xlarge - :type placement: - :param placement: + :type placement: string + :param placement: The availability zone in which to launch the instances :type kernel_id: string :param kernel_id: The ID of the kernel with which to launch the instances @@ -162,6 +168,23 @@ def run(self, min_count=1, max_count=1, key_name=None, describing the EBS volumes associated with the Image. + :type disable_api_termination: bool + :param disable_api_termination: If True, the instances will be locked + and will not be able to be terminated + via the API. + + :type instance_initiated_shutdown_behavior: string + :param instance_initiated_shutdown_behavior: Specifies whether the instance's + EBS volues are stopped (i.e. detached) + or terminated (i.e. deleted) when + the instance is shutdown by the + owner. Valid values are: + stop | terminate + + :type placement_group: string + :param placement_group: If specified, this is the name of the placement + group in which the instance(s) will be launched. + :rtype: Reservation :return: The :class:`boto.ec2.instance.Reservation` associated with the request for machines """ @@ -171,7 +194,9 @@ def run(self, min_count=1, max_count=1, key_name=None, instance_type, placement, kernel_id, ramdisk_id, monitoring_enabled, subnet_id, - block_device_map) + block_device_map, disable_api_termination, + instance_initiated_shutdown_behavior, + placement_group) def deregister(self): return self.connection.deregister_image(self.id)