From 7559fbe0d160d7d9b9413b7f96df4c406caaae37 Mon Sep 17 00:00:00 2001 From: Steve Pulec Date: Fri, 9 Jan 2015 00:17:20 -0500 Subject: [PATCH] Add RDS Subnet groups --- moto/rds/exceptions.py | 7 +++++ moto/rds/models.py | 53 +++++++++++++++++++++++++++++++++++- moto/rds/responses.py | 50 ++++++++++++++++++++++++++++++++++ tests/test_rds/test_rds.py | 55 +++++++++++++++++++++++++++++++++++++- 4 files changed, 163 insertions(+), 2 deletions(-) diff --git a/moto/rds/exceptions.py b/moto/rds/exceptions.py index 518ff401d..936b979d2 100644 --- a/moto/rds/exceptions.py +++ b/moto/rds/exceptions.py @@ -29,3 +29,10 @@ class DBSecurityGroupNotFoundError(RDSClientError): super(DBSecurityGroupNotFoundError, self).__init__( 'DBSecurityGroupNotFound', "Security Group {0} not found.".format(security_group_name)) + + +class DBSubnetGroupNotFoundError(RDSClientError): + def __init__(self, subnet_group_name): + super(DBSubnetGroupNotFoundError, self).__init__( + 'DBSubnetGroupNotFound', + "Subnet Group {0} not found.".format(subnet_group_name)) diff --git a/moto/rds/models.py b/moto/rds/models.py index 06ada38c4..b7cb8ef5a 100644 --- a/moto/rds/models.py +++ b/moto/rds/models.py @@ -4,7 +4,7 @@ import boto.rds from jinja2 import Template from moto.core import BaseBackend -from .exceptions import DBInstanceNotFoundError, DBSecurityGroupNotFoundError +from .exceptions import DBInstanceNotFoundError, DBSecurityGroupNotFoundError, DBSubnetGroupNotFoundError class Database(object): @@ -118,11 +118,42 @@ class SecurityGroup(object): self.ip_ranges.append(cidr_ip) +class SubnetGroup(object): + def __init__(self, subnet_name, description, subnets): + self.subnet_name = subnet_name + self.description = description + self.subnets = subnets + + self.vpc_id = self.subnets[0].vpc_id + + def to_xml(self): + template = Template(""" + {{ subnet_group.vpc_id }} + Complete + {{ subnet_group.description }} + {{ subnet_group.subnet_name }} + + {% for subnet in subnet_group.subnets %} + + Active + {{ subnet.id }} + + {{ subnet.availability_zone }} + false + + + {% endfor %} + + """) + return template.render(subnet_group=self) + + class RDSBackend(BaseBackend): def __init__(self): self.databases = {} self.security_groups = {} + self.subnet_groups = {} def create_database(self, db_kwargs): database_id = db_kwargs['db_instance_identifier'] @@ -173,6 +204,26 @@ class RDSBackend(BaseBackend): security_group.authorize(cidr_ip) return security_group + def create_subnet_group(self, subnet_name, description, subnets): + subnet_group = SubnetGroup(subnet_name, description, subnets) + self.subnet_groups[subnet_name] = subnet_group + return subnet_group + + def describe_subnet_groups(self, subnet_group_name): + if subnet_group_name: + if subnet_group_name in self.subnet_groups: + return [self.subnet_groups[subnet_group_name]] + else: + raise DBSubnetGroupNotFoundError(subnet_group_name) + return self.subnet_groups.values() + + def delete_subnet_group(self, subnet_name): + if subnet_name in self.subnet_groups: + return self.subnet_groups.pop(subnet_name) + else: + raise DBSubnetGroupNotFoundError(subnet_name) + + rds_backends = {} for region in boto.rds.regions(): rds_backends[region.name] = RDSBackend() diff --git a/moto/rds/responses.py b/moto/rds/responses.py index 4631ff6b0..b115b60a5 100644 --- a/moto/rds/responses.py +++ b/moto/rds/responses.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals from moto.core.responses import BaseResponse +from moto.ec2.models import ec2_backends from .models import rds_backends @@ -94,6 +95,27 @@ class RDSResponse(BaseResponse): template = self.response_template(AUTHORIZE_SECURITY_GROUP_TEMPLATE) return template.render(security_group=security_group) + def create_dbsubnet_group(self): + subnet_name = self._get_param('DBSubnetGroupName') + description = self._get_param('DBSubnetGroupDescription') + subnet_ids = self._get_multi_param('SubnetIds.member') + subnets = [ec2_backends[self.region].get_subnet(subnet_id) for subnet_id in subnet_ids] + subnet_group = self.backend.create_subnet_group(subnet_name, description, subnets) + template = self.response_template(CREATE_SUBNET_GROUP_TEMPLATE) + return template.render(subnet_group=subnet_group) + + def describe_dbsubnet_groups(self): + subnet_name = self._get_param('DBSubnetGroupName') + subnet_groups = self.backend.describe_subnet_groups(subnet_name) + template = self.response_template(DESCRIBE_SUBNET_GROUPS_TEMPLATE) + return template.render(subnet_groups=subnet_groups) + + def delete_dbsubnet_group(self): + subnet_name = self._get_param('DBSubnetGroupName') + subnet_group = self.backend.delete_subnet_group(subnet_name) + template = self.response_template(DELETE_SUBNET_GROUP_TEMPLATE) + return template.render(subnet_group=subnet_group) + CREATE_DATABASE_TEMPLATE = """ @@ -171,3 +193,31 @@ AUTHORIZE_SECURITY_GROUP_TEMPLATE = """6176b5f8-bfed-11d3-f92b-31fa5e8dbc99 """ + +CREATE_SUBNET_GROUP_TEMPLATE = """ + + {{ subnet_group.to_xml() }} + + + 3a401b3f-bb9e-11d3-f4c6-37db295f7674 + +""" + +DESCRIBE_SUBNET_GROUPS_TEMPLATE = """ + + + {% for subnet_group in subnet_groups %} + {{ subnet_group.to_xml() }} + {% endfor %} + + + + b783db3b-b98c-11d3-fbc7-5c0aad74da7c + +""" + +DELETE_SUBNET_GROUP_TEMPLATE = """ + + 6295e5ab-bbf3-11d3-f4c6-37db295f7674 + +""" diff --git a/tests/test_rds/test_rds.py b/tests/test_rds/test_rds.py index 1df5bf9ad..a4c758953 100644 --- a/tests/test_rds/test_rds.py +++ b/tests/test_rds/test_rds.py @@ -1,10 +1,11 @@ from __future__ import unicode_literals import boto.rds +import boto.vpc from boto.exception import BotoServerError import sure # noqa -from moto import mock_rds +from moto import mock_ec2, mock_rds @mock_rds @@ -138,3 +139,55 @@ def test_add_security_group_to_database(): list(database.security_groups).should.have.length_of(1) database.security_groups[0].name.should.equal("db_sg") + + +@mock_ec2 +@mock_rds +def test_add_database_subnet_group(): + vpc_conn = boto.vpc.connect_to_region("us-west-2") + vpc = vpc_conn.create_vpc("10.0.0.0/16") + subnet1 = vpc_conn.create_subnet(vpc.id, "10.1.0.0/24") + subnet2 = vpc_conn.create_subnet(vpc.id, "10.2.0.0/24") + + subnet_ids = [subnet1.id, subnet2.id] + conn = boto.rds.connect_to_region("us-west-2") + subnet_group = conn.create_db_subnet_group("db_subnet", "my db subnet", subnet_ids) + subnet_group.name.should.equal('db_subnet') + subnet_group.description.should.equal("my db subnet") + list(subnet_group.subnet_ids).should.equal(subnet_ids) + + +@mock_ec2 +@mock_rds +def test_describe_database_subnet_group(): + vpc_conn = boto.vpc.connect_to_region("us-west-2") + vpc = vpc_conn.create_vpc("10.0.0.0/16") + subnet = vpc_conn.create_subnet(vpc.id, "10.1.0.0/24") + + conn = boto.rds.connect_to_region("us-west-2") + conn.create_db_subnet_group("db_subnet1", "my db subnet", [subnet.id]) + conn.create_db_subnet_group("db_subnet2", "my db subnet", [subnet.id]) + + list(conn.get_all_db_subnet_groups()).should.have.length_of(2) + list(conn.get_all_db_subnet_groups("db_subnet1")).should.have.length_of(1) + + conn.get_all_db_subnet_groups.when.called_with("not-a-subnet").should.throw(BotoServerError) + + +@mock_ec2 +@mock_rds +def test_delete_database_subnet_group(): + vpc_conn = boto.vpc.connect_to_region("us-west-2") + vpc = vpc_conn.create_vpc("10.0.0.0/16") + subnet = vpc_conn.create_subnet(vpc.id, "10.1.0.0/24") + + conn = boto.rds.connect_to_region("us-west-2") + conn.create_db_subnet_group("db_subnet1", "my db subnet", [subnet.id]) + list(conn.get_all_db_subnet_groups()).should.have.length_of(1) + + conn.delete_db_subnet_group("db_subnet1") + list(conn.get_all_db_subnet_groups()).should.have.length_of(0) + + conn.delete_db_subnet_group.when.called_with("db_subnet1").should.throw(BotoServerError) + +# TODO incorporate subnet groups with actual DBs