diff options
author | Mitch.Garnaat <Mitch.Garnaat@604d75c7-a419-0410-a38f-bde1a0bd1dbf> | 2008-12-10 11:43:15 +0000 |
---|---|---|
committer | Mitch.Garnaat <Mitch.Garnaat@604d75c7-a419-0410-a38f-bde1a0bd1dbf> | 2008-12-10 11:43:15 +0000 |
commit | c829df67b9b852a52956347aaaceab2e21538646 (patch) | |
tree | 1b91e63bc748f9b951e2f36c136b5c89aa2b8f67 /boto/ec2 | |
parent | f091147c248b2c250e6ac002ea622cad3affdd44 (diff) | |
download | boto-c829df67b9b852a52956347aaaceab2e21538646.tar.gz |
Checking in changes to support regions in EC2.
Diffstat (limited to 'boto/ec2')
-rw-r--r-- | boto/ec2/__init__.py | 21 | ||||
-rw-r--r-- | boto/ec2/address.py | 16 | ||||
-rw-r--r-- | boto/ec2/connection.py | 256 | ||||
-rw-r--r-- | boto/ec2/ec2object.py | 41 | ||||
-rw-r--r-- | boto/ec2/image.py | 14 | ||||
-rw-r--r-- | boto/ec2/instance.py | 14 | ||||
-rw-r--r-- | boto/ec2/keypair.py | 67 | ||||
-rw-r--r-- | boto/ec2/regioninfo.py | 60 | ||||
-rw-r--r-- | boto/ec2/securitygroup.py | 97 | ||||
-rw-r--r-- | boto/ec2/snapshot.py | 8 | ||||
-rw-r--r-- | boto/ec2/volume.py | 8 | ||||
-rw-r--r-- | boto/ec2/zone.py | 8 |
12 files changed, 536 insertions, 74 deletions
diff --git a/boto/ec2/__init__.py b/boto/ec2/__init__.py index 449bd162..0cc3277c 100644 --- a/boto/ec2/__init__.py +++ b/boto/ec2/__init__.py @@ -1,4 +1,4 @@ -# Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/ +# Copyright (c) 2006-2008 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 @@ -19,5 +19,22 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. # +""" +This module provides an interface to the Elastic Compute Cloud (EC2) +service from AWS. +""" +from boto.ec2.connection import EC2Connection - +def regions(**kw_params): + """ + Get all available regions for the EC2 service. + You may pass any of the arguments accepted by the EC2Connection + object's constructor as keyword arguments and they will be + passed along to the EC2Connection object. + + @rtype: list + @return: A list of L{RegionInfo<boto.ec2.regioninfo.RegionInfo>} + """ + c = EC2Connection(**kw_params) + return c.get_all_regions() + diff --git a/boto/ec2/address.py b/boto/ec2/address.py index eec06e71..b2af1075 100644 --- a/boto/ec2/address.py +++ b/boto/ec2/address.py @@ -1,4 +1,4 @@ -# Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/ +# Copyright (c) 2006-2008 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 @@ -23,19 +23,19 @@ Represents an EC2 Elastic IP Address """ -class Address: +from boto.ec2.ec2object import EC2Object + +class Address(EC2Object): - def __init__(self, connection=None): + def __init__(self, connection=None, public_ip=None, instance_id=None): + EC2Object.__init__(self, connection) self.connection = connection - self.public_ip = None - self.instance_id = None + self.public_ip = public_ip + self.instance_id = instance_id def __repr__(self): return 'Address:%s' % self.public_ip - def startElement(self, name, attrs, connection): - return None - def endElement(self, name, value, connection): if name == 'publicIp': self.public_ip = value diff --git a/boto/ec2/connection.py b/boto/ec2/connection.py index 397e4154..7d7a8b17 100644 --- a/boto/ec2/connection.py +++ b/boto/ec2/connection.py @@ -1,4 +1,4 @@ -# Copyright (c) 2006,2007 Mitch Garnaat http://garnaat.org/ +# Copyright (c) 2006-2008 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 @@ -38,28 +38,46 @@ from boto.ec2.volume import Volume from boto.ec2.snapshot import Snapshot from boto.ec2.zone import Zone from boto.ec2.securitygroup import SecurityGroup +from boto.ec2.regioninfo import RegionInfo from boto.exception import EC2ResponseError class EC2Connection(AWSQueryConnection): - APIVersion = boto.config.get('Boto', 'ec2_version', '2008-05-05') + APIVersion = boto.config.get('Boto', 'ec2_version', '2008-12-01') + DefaultRegionName = boto.config.get('Boto', 'ec2_region_name', 'us-east-1') + DefaultRegionEndpoint = boto.config.get('Boto', 'ec2_region_endpoint', + 'us-east-1.ec2.amazonaws.com') SignatureVersion = '1' ResponseError = EC2ResponseError def __init__(self, aws_access_key_id=None, aws_secret_access_key=None, is_secure=True, port=None, proxy=None, proxy_port=None, - proxy_user=None, proxy_pass=None, host='ec2.amazonaws.com', debug=0, - https_connection_factory=None): + proxy_user=None, proxy_pass=None, debug=0, + https_connection_factory=None, region=None): """ Init method to create a new connection to EC2. B{Note:} The host argument is overridden by the host specified in the boto configuration file. """ - if config.has_option('Boto', 'ec2_host'): - host = config.get('Boto', 'ec2_host') + if not region: + region = RegionInfo(self, self.DefaultRegionName, self.DefaultRegionEndpoint) + self.region = region AWSQueryConnection.__init__(self, aws_access_key_id, aws_secret_access_key, is_secure, port, proxy, proxy_port, proxy_user, proxy_pass, - host, debug, https_connection_factory) + self.region.endpoint, debug, https_connection_factory) + + def get_params(self): + """ + Returns a dictionary containing the value of of all of the keyword + arguments passed when constructing this connection. + """ + param_names = ['aws_access_key_id', 'aws_secret_access_key', 'is_secure', + 'port', 'proxy', 'proxy_port', 'proxy_user', 'proxy_pass', + 'debug', 'https_connection_factory'] + params = {} + for name in param_names: + params[name] = getattr(self, name) + return params # Image methods @@ -146,11 +164,29 @@ class EC2Connection(AWSQueryConnection): return None def register_image(self, image_location): + """ + Register an image. + + @type image_location: string + @param image_location: Full path to your AMI manifest in Amazon S3 storage. + + @rtype: string + @return: The new image id + """ params = {'ImageLocation':image_location} rs = self.get_object('RegisterImage', params, ResultSet) return rs.imageId def deregister_image(self, image_id): + """ + Unregister an AMI. + + @type image_id: string + @param image_id: the ID of the Image to unregister + + @rtype: bool + @return: True if successful + """ return self.get_status('DeregisterImage', {'ImageId':image_id}) # ImageAttribute methods @@ -205,7 +241,7 @@ class EC2Connection(AWSQueryConnection): def reset_image_attribute(self, image_id, attribute='launchPermission'): """ - Rresets an attribute of an AMI to its default value. + Resets an attribute of an AMI to its default value. See http://docs.amazonwebservices.com/AWSEC2/2008-02-01/DeveloperGuide/ApiReference-Query-ResetImageAttribute.html @type image_id: string @@ -357,6 +393,17 @@ class EC2Connection(AWSQueryConnection): # Zone methods def get_all_zones(self, zones=None): + """ + Get all Availability Zones associated with the current region. + + @type zones: list + @param zones: Optional list of zones. If this list is present, + only the Zones associated with these zone names + will be returned. + + @rtype: list of L{boto.ec2.zone.Zone} + @return: The requested Zone objects + """ params = {} if zones: self.build_list_params(params, zones, 'ZoneName') @@ -365,26 +412,73 @@ class EC2Connection(AWSQueryConnection): # Address methods def get_all_addresses(self, addresses=None): + """ + Get all EIP's associated with the current credentials. + + @type addresses: list + @param addresses: Optional list of addresses. If this list is present, + only the Addresses associated with these addresses + will be returned. + + @rtype: list of L{boto.ec2.address.Address} + @return: The requested Address objects + """ params = {} if addresses: self.build_list_params(params, addresses, 'PublicIp') return self.get_list('DescribeAddresses', params, [('item', Address)]) def allocate_address(self): - return self.get_object('AllocateAddress', None, Address) + """ + Allocate a new Elastic IP address and associate it with your account. - def release_address(self, public_ip): - params = {'PublicIp' : public_ip} - return self.get_status('ReleaseAddress', params) + @rtype: L{boto.ec2.address.Address} + @return: The newly allocated Address + """ + return self.get_object('AllocateAddress', None, Address) def associate_address(self, instance_id, public_ip): + """ + Associate an Elastic IP address with a currently running instance. + + @type instance_id: string + @param instance_id: The ID of the instance + + @type public_ip: string + @param public_ip: The public IP address + + @rtype: bool + @return: True if successful + """ params = {'InstanceId' : instance_id, 'PublicIp' : public_ip} return self.get_status('AssociateAddress', params) def disassociate_address(self, public_ip): + """ + Disassociate an Elastic IP address from a currently running instance. + + @type public_ip: string + @param public_ip: The public IP address + + @rtype: bool + @return: True if successful + """ params = {'PublicIp' : public_ip} return self.get_status('DisassociateAddress', params) + def release_address(self, public_ip): + """ + Free up an Elastic IP address + + @type public_ip: string + @param public_ip: The public IP address + + @rtype: bool + @return: True if successful + """ + params = {'PublicIp' : public_ip} + return self.get_status('ReleaseAddress', params) + # Volume methods def get_all_volumes(self, volume_ids=None): @@ -447,6 +541,17 @@ class EC2Connection(AWSQueryConnection): # Snapshot methods def get_all_snapshots(self, snapshot_ids=None): + """ + Get all EBS Snapshots associated with the current credentials. + + @type snapshot_ids: list + @param snapshot_ids: Optional list of snapshot ids. If this list is present, + only the Snapshots associated with these snapshot ids + will be returned. + + @rtype: list of L{boto.ec2.snapshot.Snapshot} + @return: The requested Snapshot objects + """ params = {} if snapshot_ids: self.build_list_params(params, snapshot_ids, 'SnapshotId') @@ -467,7 +572,8 @@ class EC2Connection(AWSQueryConnection): Get all key pairs associated with your account. @type keynames: list - @param keynames: A list of the names of keypairs to retrieve + @param keynames: A list of the names of keypairs to retrieve. + If not provided, all key pairs will be returned. @rtype: list @return: A list of L{KeyPairs<boto.ec2.keypair.KeyPair>} @@ -489,18 +595,22 @@ class EC2Connection(AWSQueryConnection): """ try: return self.get_all_key_pairs(keynames=[keyname])[0] - except IndexError: # None of those images available + except IndexError: # None of those key pairs available return None def create_key_pair(self, key_name): """ Create a new key pair for your account. + This will create the key pair within the region you + are currently connected to. @type key_name: string @param key_name: The name of the new keypair - @rtype: KeyPair - @return: The newly created L{KeyPair<boto.ec2.keypair.KeyPair>} + @rtype: L{KeyPair<boto.ec2.keypair.KeyPair>} + @return: The newly created L{KeyPair<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} return self.get_object('CreateKeyPair', params, KeyPair) @@ -518,12 +628,36 @@ class EC2Connection(AWSQueryConnection): # SecurityGroup methods def get_all_security_groups(self, groupnames=None): + """ + Get all security groups associated with your account in a region. + + @type groupnames: list + @param groupnames: A list of the names of security groups to retrieve. + If not provided, all security groups will be returned. + + @rtype: list + @return: A list of L{SecurityGroups<boto.ec2.securitygroup.SecurityGroup>} + """ params = {} if groupnames: self.build_list_params(params, groupnames, 'GroupName') return self.get_list('DescribeSecurityGroups', params, [('item', SecurityGroup)]) def create_security_group(self, name, description): + """ + Create a new security group for your account. + This will create the security group within the region you + are currently connected to. + + @type name: string + @param name: The name of the new security group + + @type description: string + @param description: The description of the new security group + + @rtype: L{SecurityGroup<boto.ec2.securitygroup.SecurityGroup>} + @return: The newly created L{KeyPair<boto.ec2.keypair.KeyPair>}. + """ params = {'GroupName':name, 'GroupDescription':description} group = self.get_object('CreateSecurityGroup', params, SecurityGroup) group.name = name @@ -531,6 +665,12 @@ class EC2Connection(AWSQueryConnection): return group def delete_security_group(self, name): + """ + Delete a security 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('DeleteSecurityGroup', params) @@ -538,6 +678,41 @@ class EC2Connection(AWSQueryConnection): src_security_group_owner_id=None, ip_protocol=None, from_port=None, to_port=None, cidr_ip=None): + """ + Add a new rule to an existing security group. + You need to pass in either src_security_group_name and + src_security_group_owner_id OR ip_protocol, from_port, to_port, + and cidr_ip. In other words, either you are authorizing another + group or you are authorizing some ip-based rule. + + @type group_name: string + @param group_name: The name of the security group you are adding + the rule to. + + @type src_security_group_name: string + @param src_security_group_name: The name of the security group you are + granting access to. + + @type src_security_group_owner_id: string + @param src_security_group_owner_id: The ID of the owner of the security group you are + granting access to. + + @type ip_protocol: string + @param ip_protocol: Either tcp | udp | icmp + + @type from_port: int + @param from_port: The beginning port number you are enabling + + @type to_port: int + @param to_port: The ending port number you are enabling + + @type to_port: string + @param to_port: The CIDR block you are providing access to. + See http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing + + @rtype: bool + @return: True if successful. + """ params = {'GroupName':group_name} if src_security_group_name: params['SourceSecurityGroupName'] = src_security_group_name @@ -557,6 +732,41 @@ class EC2Connection(AWSQueryConnection): src_security_group_owner_id=None, ip_protocol=None, from_port=None, to_port=None, cidr_ip=None): + """ + Remove an existing rule from an existing security group. + You need to pass in either src_security_group_name and + src_security_group_owner_id OR ip_protocol, from_port, to_port, + and cidr_ip. In other words, either you are revoking another + group or you are revoking some ip-based rule. + + @type group_name: string + @param group_name: The name of the security group you are removing + the rule from. + + @type src_security_group_name: string + @param src_security_group_name: The name of the security group you are + revoking access to. + + @type src_security_group_owner_id: string + @param src_security_group_owner_id: The ID of the owner of the security group you are + revoking access to. + + @type ip_protocol: string + @param ip_protocol: Either tcp | udp | icmp + + @type from_port: int + @param from_port: The beginning port number you are disabling + + @type to_port: int + @param to_port: The ending port number you are disabling + + @type to_port: string + @param to_port: The CIDR block you are revoking access to. + See http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing + + @rtype: bool + @return: True if successful. + """ params = {'GroupName':group_name} if src_security_group_name: params['SourceSecurityGroupName'] = src_security_group_name @@ -572,3 +782,17 @@ class EC2Connection(AWSQueryConnection): params['CidrIp'] = cidr_ip return self.get_status('RevokeSecurityGroupIngress', params) + # + # Regions + # + + def get_all_regions(self): + """ + Get all available regions for the EC2 service. + + @rtype: list + @return: A list of L{RegionInfo<boto.ec2.regioninfo.RegionInfo>} + """ + return self.get_list('DescribeRegions', None, [('item', RegionInfo)]) + + diff --git a/boto/ec2/ec2object.py b/boto/ec2/ec2object.py new file mode 100644 index 00000000..3a50760a --- /dev/null +++ b/boto/ec2/ec2object.py @@ -0,0 +1,41 @@ +# Copyright (c) 2006-2008 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 Object +""" + +class EC2Object(object): + + def __init__(self, connection=None): + self.connection = connection + if self.connection: + self.region = connection.region + else: + self.region = None + + def startElement(self, name, attrs, connection): + return None + + def endElement(self, name, value, connection): + setattr(self, name, value) + + diff --git a/boto/ec2/image.py b/boto/ec2/image.py index 81ce5c2c..1eedc18e 100644 --- a/boto/ec2/image.py +++ b/boto/ec2/image.py @@ -19,14 +19,15 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -""" -Represents an EC2 Image -""" +from boto.ec2.ec2object import EC2Object -class Image: +class Image(EC2Object): + """ + Represents an EC2 Image + """ def __init__(self, connection=None): - self.connection = connection + EC2Object.__init__(self, connection) self.id = None self.location = None self.state = None @@ -41,9 +42,6 @@ class Image: def __repr__(self): return 'Image:%s' % self.id - def startElement(self, name, attrs, connection): - return None - def endElement(self, name, value, connection): if name == 'imageId': self.id = value diff --git a/boto/ec2/instance.py b/boto/ec2/instance.py index 50a2a7a4..4a74e866 100644 --- a/boto/ec2/instance.py +++ b/boto/ec2/instance.py @@ -1,4 +1,4 @@ -# Copyright (c) 2006, 2007 Mitch Garnaat http://garnaat.org/ +# Copyright (c) 2006-2008 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 @@ -23,14 +23,15 @@ Represents an EC2 Instance """ +from boto.ec2.ec2object import EC2Object from boto.resultset import ResultSet from boto.ec2.address import Address import base64 -class Reservation: +class Reservation(EC2Object): def __init__(self, connection=None): - self.connection = connection + EC2Object.__init__(self, connection) self.id = None self.owner_id = None self.groups = [] @@ -61,10 +62,10 @@ class Reservation: for instance in self.instances: instance.stop() -class Instance: +class Instance(EC2Object): def __init__(self, connection=None): - self.connection = connection + EC2Object.__init__(self, connection) self.id = None self.dns_name = None self.public_dns_name = None @@ -85,9 +86,6 @@ class Instance: def __repr__(self): return 'Instance:%s' % self.id - def startElement(self, name, attrs, connection): - return None - def endElement(self, name, value, connection): if name == 'instanceId': self.id = value diff --git a/boto/ec2/keypair.py b/boto/ec2/keypair.py index 172a80c9..64ab1422 100644 --- a/boto/ec2/keypair.py +++ b/boto/ec2/keypair.py @@ -23,10 +23,14 @@ Represents an EC2 Keypair """ -class KeyPair: +import os +from boto.ec2.ec2object import EC2Object +from boto.exception import BotoClientError + +class KeyPair(EC2Object): def __init__(self, connection=None): - self.connection = connection + EC2Object.__init__(self, connection) self.name = None self.fingerprint = None self.material = None @@ -34,9 +38,6 @@ class KeyPair: def __repr__(self): return 'KeyPair:%s' % self.name - def startElement(self, name, attrs, connection): - return None - def endElement(self, name, value, connection): if name == 'keyName': self.name = value @@ -48,7 +49,63 @@ class KeyPair: setattr(self, name, value) def delete(self): + """ + Delete the KeyPair. + + @rtype: bool + @return: True if successful, otherwise False. + """ return self.connection.delete_key_pair(self.name) + 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 + keypair file will be named using the name + of the keypair as the base name and .pem + for the file extension. If a file of that + 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. + """ + if self.material: + file_path = os.path.join(directory_path, '%s.pem' % self.name) + if os.exists(file_path): + raise BotoClientError('%s already exists, it will not be overwritten' % file_path) + fp = open(file_path, 'wb') + fp.write(self.material) + fp.close() + return True + else: + raise BotoClientError('KeyPair contains no material') + + def copy_to_region(self, region): + """ + Create a new key pair of the same new in another region. + Note that the new key pair will use a different ssh + cert than the this key pair. After doing the copy, + you will need to save the material associated with the + new key pair (use the save method) to a local file. + + @type region: L{RegionInfo<boto.ec2.regioninfo.RegionInfo>} + @param region: The region to which this security group will be copied. + + @rtype: L{KeyPair<boto.ec2.keypair.KeyPair>} + @return: The new key pair + """ + if region.name == 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) + return kp + diff --git a/boto/ec2/regioninfo.py b/boto/ec2/regioninfo.py new file mode 100644 index 00000000..1a8e5866 --- /dev/null +++ b/boto/ec2/regioninfo.py @@ -0,0 +1,60 @@ +# Copyright (c) 2006-2008 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. + +class RegionInfo(object): + """ + Represents an EC2 Region + """ + + def __init__(self, connection=None, name=None, endpoint=None): + self.connection = connection + self.name = name + self.endpoint = endpoint + + def __repr__(self): + return 'RegionInfo:%s' % self.name + + def startElement(self, name, attrs, connection): + return None + + def endElement(self, name, value, connection): + if name == 'regionName': + self.name = value + elif name == 'regionEndpoint': + self.endpoint = value + else: + setattr(self, name, value) + + def connect(self, **kw_params): + """ + Connect to this Region's endpoint. Returns an EC2Connection + object pointing to the endpoint associated with this region. + You may pass any of the arguments accepted by the EC2Connection + object's constructor as keyword arguments and they will be + passed along to the EC2Connection object. + + @rtype: L{EC2Connection<boto.ec2.connection.EC2Connection} + @return: The connection to this regions endpoint + """ + from boto.ec2.connection import EC2Connection + return EC2Connection(region=self, **kw_params) + + diff --git a/boto/ec2/securitygroup.py b/boto/ec2/securitygroup.py index ba951c07..6797d53f 100644 --- a/boto/ec2/securitygroup.py +++ b/boto/ec2/securitygroup.py @@ -22,12 +22,13 @@ """ Represents an EC2 Security Group """ +from boto.ec2.ec2object import EC2Object -class SecurityGroup: +class SecurityGroup(EC2Object): def __init__(self, connection=None, owner_id=None, name=None, description=None): - self.connection = connection + EC2Object.__init__(self, connection) self.owner_id = owner_id self.name = name self.description = description @@ -89,8 +90,8 @@ class SecurityGroup: target_rule = rule target_grant = None for grant in rule.grants: - if grant.group_name == src_group_name: - if grant.user_id == src_group_owner_id: + if grant.name == src_group_name: + if grant.owner_id == src_group_owner_id: if grant.cidr_ip == cidr_ip: target_grant = grant if target_grant: @@ -100,7 +101,41 @@ class SecurityGroup: def authorize(self, ip_protocol=None, from_port=None, to_port=None, cidr_ip=None, src_group=None): + """ + Add a new rule to this security group. + You need to pass in either src_group_name + OR ip_protocol, from_port, to_port, + and cidr_ip. In other words, either you are authorizing another + group or you are authorizing some ip-based rule. + + @type src_security_group_owner_id: string + @param src_security_group_owner_id: The ID of the owner of the security group you are + granting access to. + + @type ip_protocol: string + @param ip_protocol: Either tcp | udp | icmp + + @type from_port: int + @param from_port: The beginning port number you are enabling + + @type to_port: int + @param to_port: The ending port number you are enabling + + @type to_port: string + @param to_port: The CIDR block you are providing access to. + See http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing + + @type src_group: L{SecurityGroup<boto.ec2.securitygroup.SecurityGroup>} or + L{GroupOrCIDR<boto.ec2.securitygroup.GroupOrCIDR} + + @rtype: bool + @return: True if successful. + """ if src_group: + from_port = None + to_port = None + cidr_ip = None + ip_protocol = None src_group_name = src_group.name src_group_owner_id = src_group.owner_id else: @@ -121,6 +156,10 @@ class SecurityGroup: def revoke(self, ip_protocol=None, from_port=None, to_port=None, cidr_ip=None, src_group=None): if src_group: + from_port=None + to_port=None + cidr_ip=None + ip_protocol = None src_group_name = src_group.name src_group_owner_id = src_group.owner_id else: @@ -138,6 +177,40 @@ class SecurityGroup: src_group_owner_id, cidr_ip) return status + def copy_to_region(self, region, name=None): + """ + Create a copy of this security group in another region. + Note that the new security group will be a separate entity + and will not stay in sync automatically after the copy + operation. + + @type region: L{RegionInfo<boto.ec2.regioninfo.RegionInfo>} + @param region: The region to which this security group will be copied. + + @type name: string + @param name: The name of the copy. If not supplied, the copy + will have the same name as this security group. + + @rtype: L{SecurityGroup<boto.ec2.securitygroup.SecurityGroup>} + @return: The new security group. + """ + if region.name == self.region: + 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) + source_groups = [] + for rule in self.rules: + grant = rule.grants[0] + if grant.name: + if grant.name not in source_groups: + source_groups.append(grant.name) + sg.authorize(None, None, None, None, grant) + else: + sg.authorize(rule.ip_protocol, rule.from_port, rule.to_port, + grant.cidr_ip) + return sg + class IPPermissions: def __init__(self, parent=None): @@ -167,10 +240,10 @@ class IPPermissions: else: setattr(self, name, value) - def add_grant(self, user_id=None, group_name=None, cidr_ip=None): + def add_grant(self, owner_id=None, name=None, cidr_ip=None): grant = GroupOrCIDR(self) - grant.user_id = user_id - grant.group_name = group_name + grant.owner_id = owner_id + grant.name = name grant.cidr_ip = cidr_ip self.grants.append(grant) return grant @@ -178,24 +251,24 @@ class IPPermissions: class GroupOrCIDR: def __init__(self, parent=None): - self.user_id = None - self.group_name = None + self.owner_id = None + self.name = None self.cidr_ip = None def __repr__(self): if self.cidr_ip: return '%s' % self.cidr_ip else: - return '%s-%s' % (self.group_name, self.user_id) + return '%s-%s' % (self.name, self.owner_id) def startElement(self, name, attrs, connection): return None def endElement(self, name, value, connection): if name == 'userId': - self.user_id = value + self.owner_id = value elif name == 'groupName': - self.group_name = value + self.name = value if name == 'cidrIp': self.cidr_ip = value else: diff --git a/boto/ec2/snapshot.py b/boto/ec2/snapshot.py index e64fdeff..beefbb97 100644 --- a/boto/ec2/snapshot.py +++ b/boto/ec2/snapshot.py @@ -22,11 +22,12 @@ """ Represents an EC2 Elastic IP Snapshot """ +from boto.ec2.ec2object import EC2Object -class Snapshot: +class Snapshot(EC2Object): def __init__(self, connection=None): - self.connection = connection + EC2Object.__init__(self, connection) self.id = None self.progress = None self.start_time = None @@ -35,9 +36,6 @@ class Snapshot: def __repr__(self): return 'Snapshot:%s' % self.id - def startElement(self, name, attrs, connection): - return None - def endElement(self, name, value, connection): if name == 'snapshotId': self.id = value diff --git a/boto/ec2/volume.py b/boto/ec2/volume.py index 0d85202b..052b3cd4 100644 --- a/boto/ec2/volume.py +++ b/boto/ec2/volume.py @@ -22,11 +22,12 @@ """ Represents an EC2 Elastic IP Volume """ +from boto.ec2.ec2object import EC2Object -class Volume: +class Volume(EC2Object): def __init__(self, connection=None): - self.connection = connection + EC2Object.__init__(self, connection) self.id = None self.instance_id = None self.snapshot_id = None @@ -37,9 +38,6 @@ class Volume: def __repr__(self): return 'Volume:%s' % self.id - def startElement(self, name, attrs, connection): - return None - def endElement(self, name, value, connection): if name == 'volumeId': self.id = value diff --git a/boto/ec2/zone.py b/boto/ec2/zone.py index f8bf3ad7..aec79b2c 100644 --- a/boto/ec2/zone.py +++ b/boto/ec2/zone.py @@ -22,20 +22,18 @@ """ Represents an EC2 Availability Zone """ +from boto.ec2.ec2object import EC2Object -class Zone: +class Zone(EC2Object): def __init__(self, connection=None): - self.connection = connection + EC2Object.__init__(self, connection) self.name = None self.state = None def __repr__(self): return 'Zone:%s' % self.name - def startElement(self, name, attrs, connection): - return None - def endElement(self, name, value, connection): if name == 'zoneName': self.name = value |