Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Add support for tagged RDS DBInstances #1061

Closed
wants to merge 9 commits into
from
View
@@ -25,6 +25,7 @@
from boto.resultset import ResultSet
from boto.iam.summarymap import SummaryMap
from boto.connection import AWSQueryConnection
+from boto.iam.user import User
ASSUME_ROLE_POLICY_DOCUMENT = json.dumps({
@@ -354,11 +355,14 @@ def get_user(self, user_name=None):
:type user_name: string
:param user_name: The name of the user to delete.
If not specified, defaults to user making request.
+
+ :rtype: :class:`boto.iam.user.User`
+ :return: The requested User instance
"""
params = {}
if user_name:
params['UserName'] = user_name
- return self.get_response('GetUser', params)
+ return self.get_object('GetUser', params, User)
def update_user(self, user_name, new_user_name=None, new_path=None):
"""
View
@@ -0,0 +1,61 @@
+# Copyright (c) 2012 Byte BV, http://byte.nl
+#
+# 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 User(object):
+ """
+ Represents an IAM User
+
+ :ivar arn: The Amazon Resource Name (ARN) specifying the user
+ :ivar create_date: The date when the user was created
+ :ivar id: The stable and unique string identifying the user
+ :ivar name: The name identifying the user
+ :ivar path: Path to the user
+ """
+
+ def __init__(self, connection=None, name=None):
+ self.connection = connection
+ self.name = name
+ self.arn = None
+ self.create_date = None
+ self.id = None
+ self.path = None
+
+ def __repr__(self):
+ return 'User:%s' % self.name
+
+ def startElement(self, name, attrs, connection):
+ pass
+
+ def endElement(self, name, value, connection):
+ if name == 'Arn':
+ self.arn = value
+ elif name == 'Path':
+ self.path = value
+ elif name == 'UserId':
+ self.id = value
+ elif name == 'UserName':
+ self.name = value
+ elif name == 'CreateDate':
+ self.create_date = value
+ else:
+ setattr(self, name, value)
+
View
@@ -28,6 +28,7 @@
from boto.rds.dbsnapshot import DBSnapshot
from boto.rds.event import Event
from boto.rds.regioninfo import RDSRegionInfo
+from boto.rds.tag import RDSTag
def regions():
@@ -1182,3 +1183,70 @@ def get_all_events(self, source_identifier=None, source_type=None,
if marker:
params['Marker'] = marker
return self.get_list('DescribeEvents', params, [('Event', Event)])
+
+ def create_tags(self, dbinstance, tags):
+ """
+ Create new metadata tags for the specified RDS DBInstance.
+
+ At this moment an instance of :class:`boto.rds.dbinstance.DBInstance`
+ must be specified, since the AddTagsToResource API call needs an ARN.
+
+ :type dbinstance: :class:`boto.rds.dbinstance.DBInstance`
+ :param dbinstance: The dbinstance to get the tags for
+
+ :type tags: dict
+ :param tags: A dictionary containing the name/value pairs.
+ If you want to create only a tag name, the
+ value for that tag should be the empty string
+ (e.g. '').
+ """
+ assert isinstance(dbinstance, DBInstance)
+
+ params = {'ResourceName': dbinstance.arn}
+
+ for i, (key, value) in enumerate(tags.items()):
+ params['Tags.member.%d.Key' % (i + 1)] = key
+ params['Tags.member.%d.Value' % (i + 1)] = value
+
+ return self.get_status('AddTagsToResource', params, verb='POST')
+
+ def delete_tags(self, dbinstance, tags):
+ """
+ Delete metadata tags for the specified RDS DBInstance id.
+
+ At this moment an instance of :class:`boto.rds.dbinstance.DBInstance`
+ must be specified, since the AddTagsToResource API call needs an ARN.
+
+ :type dbinstance: :class:`boto.rds.dbinstance.DBInstance`
+ :param dbinstance: The dbinstance to get the tags for
+
+ :type tags: list
+ :param tags: A list containing the names of the tags to delete
+ """
+
+ assert isinstance(dbinstance, DBInstance)
+
+ params = {'ResourceName': dbinstance.arn}
+
+ for i, key in enumerate(tags):
+ params['TagKeys.member.%d' % (i + 1)] = key
+
+ return self.get_status('RemoveTagsFromResource', params, verb='POST')
+
+ def get_dbinstance_tags(self, dbinstance):
+ """
+ Get all metadata tags for the specified RDS DBInstance.
+
+ At this moment an instance of :class:`boto.rds.dbinstance.DBInstance`
+ must be specified, since the ListTagsForResource API call needs an ARN.
+
+ :type dbinstance: :class:`boto.rds.dbinstance.DBInstance`
+ :param dbinstance: The dbinstance to get the tags for
+ """
+ assert isinstance(dbinstance, DBInstance)
+
+ params = {'ResourceName': dbinstance.arn}
+
+ return self.get_list('ListTagsForResource', params,
+ [('Tag', RDSTag)])
+
View
@@ -66,6 +66,9 @@ class DBInstance(object):
:ivar pending_modified_values: Specifies that changes to the
DB Instance are pending. This element is only included when changes
are pending. Specific changes are identified by subelements.
+ :ivar arn: The Amazon Resource Name for this DBInstance. Retrieved by
+ querying IAM for the account-id of the current connection.
+ :ivar tags: A dict with tags associated with this DBInstance.
"""
def __init__(self, connection=None, id=None):
@@ -91,6 +94,8 @@ def __init__(self, connection=None, id=None):
self._in_endpoint = False
self._port = None
self._address = None
+ self._arn = None
+ self._tags = None
def __repr__(self):
return 'DBInstance:%s' % self.id
@@ -306,6 +311,104 @@ def modify(self, param_group=None, security_groups=None,
apply_immediately,
iops)
+ @property
+ def arn(self):
+
+ # Current RDS-API does not supply the ARN (Amazon Resource Name) for
+ # database instances. However, new functionality like tags for RDS
+ # resources, works with ARN's. This getter for DBInstance.arn gets
+ # the account-id from IAM, and creates an ARN for this DBInstance. If
+ # the Amazon-API does supply the ARN in the future, this code should
+ # be compatible (and could be removed, after renaming DBInstance._arn
+ # to DBInstance.arn
+
+ if self._arn is None:
+ import boto
+ iam = boto.connect_iam(
+ self.connection.aws_access_key_id,
+ self.connection.aws_secret_access_key)
+ if iam:
+ user = iam.get_user()
+ if user:
+ account_id = user.arn.split(':')[4]
+ self.arn = ('arn:aws:rds:%s:%s:db:%s' %
+ (self.connection.region.name, account_id, self.id))
+
+ return self._arn
+
+ @arn.setter
+ def arn(self, value):
+ self._arn = value
+
+ @property
+ def tags(self):
+
+ if self._tags is None:
+ mytags = {}
+ tag_rs = self.connection.get_dbinstance_tags(self)
+
+ # Translate the boto.ResultSet to a dict
+ #
+ for tag in tag_rs:
+ mytags[tag.name] = tag.value
+
+ self.tags = mytags
+
+ return self._tags
+
+ @tags.setter
+ def tags(self, value):
+ self._tags = value
+
+ def add_tag(self, key, value=''):
+ """
+ Add a tag to this object. The local tag-list (dbinstance.tags) will be
+ updated with the newly created or modified tag, without checking with
+ AWS. You could do that yourself with:
+
+ dbinstance.tags = None
+ tags = dbinstance.tags
+
+ This will query AWS for the current tags and update the local tags.
+
+ :type key: str
+ :param key: The key or name of the tag being stored.
+
+ :type value: str
+ :param value: An optional value that can be stored with the tag.
+ If you want only the tag name and no value, the
+ value should be the empty string.
+ """
+ self.connection.create_tags(self, {key: value})
+
+ # What to do: only update local tags with newly added tag, or update
+ # local tags with remote? Since it is uncertain if new tag is already
+ # added (AWS-API is asynchronous), we'll only update our local tags.
+ #
+ self.tags[key] = value
+
+ def remove_tag(self, key):
+ """
+ Remove a tag from this object. The tag will also be removed from the
+ local tag-list (dbinstance.tags), without checking with AWS. You could
+ do that yourself with:
+
+ dbinstance.tags = None
+ tags = dbinstance.tags
+
+ This will query AWS for the current tags and update the local tags.
+
+ Contrary to EC2-tags, RDS does not support removing tags with a key and
+ a matching value.
+
+ :type key: str
+ :param key: The key or name of the tag to remove.
+ """
+ self.connection.delete_tags(self, [key])
+
+ if key in self.tags:
+ del self.tags[key]
+
class PendingModifiedValues(dict):
View
@@ -0,0 +1,46 @@
+# Copyright (c) 2010 Mitch Garnaat http://garnaat.org/
+# Copyright (c) 2010, Eucalyptus Systems, Inc.
+# Copyright (c) 2012, Byte BV, http://byte.nl
+#
+# 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 RDSTag(object):
+
+ def __init__(self, connection=None, name=None, value=None):
+ self.connection = connection
+ self.name = name
+ self.value = value
+
+ def __repr__(self):
+ return 'Tag:%s' % self.name
+
+ def startElement(self, name, attrs, connection):
+ return None
+
+ def endElement(self, name, value, connection):
+ if name == 'Key':
+ self.name = value
+ elif name == 'Value':
+ self.value = value
+ else:
+ setattr(self, name, value)
+
+