Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Add support for tagged RDS DBInstances #1050

Closed
wants to merge 5 commits into from

1 participant

Gertjan Oude Lohuis
Gertjan Oude Lohuis

These commits add support for listing, creating and removing tags to and from RDS-DBInstances (ListTagsForResource, AddTagsToResource and RemoveTagsFromResource API calls).

Amazon requires an ARN to reference the DBInstance on which the actions are performed, but doesn't supply this ARN, afaik. I added a property to the DBInstance-class, which uses IAM (for the account-id) to construct the ARN itself when asked for. It's not very clean, but I didn't see another way. If Amazon does supply the ARN in another way, or in the future, this could easily be removed while retaining backwards compatibility.

I'm hoping to add support for a TaggedRDSResource, similar to the TaggedEC2Resource.

I welcome your thought and suggestions!

Gertjan Oude Lohuis gertjanol closed this
Gertjan Oude Lohuis

Created a new pullrequest with the same commits, but in it's own featurebranch (#1061)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
6 boto/iam/connection.py
View
@@ -29,6 +29,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({
@@ -358,11 +359,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):
"""
61 boto/iam/user.py
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)
+
68 boto/rds/__init__.py
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)])
+
30 boto/rds/dbinstance.py
View
@@ -91,6 +91,7 @@ def __init__(self, connection=None, id=None):
self._in_endpoint = False
self._port = None
self._address = None
+ self._arn = None
def __repr__(self):
return 'DBInstance:%s' % self.id
@@ -304,6 +305,35 @@ def modify(self, param_group=None, security_groups=None,
iops,
apply_immediately)
+ @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
+
class PendingModifiedValues(dict):
46 boto/rds/tag.py
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)
+
+
Something went wrong with that request. Please try again.