1from moto.core.responses import BaseResponse
2from moto.ec2.models import ec2_backends
3from .models import rds_backends
4
5
6class RDSResponse(BaseResponse):
7    @property
8    def backend(self):
9        return rds_backends[self.region]
10
11    def _get_db_kwargs(self):
12        args = {
13            "auto_minor_version_upgrade": self._get_param("AutoMinorVersionUpgrade"),
14            "allocated_storage": self._get_int_param("AllocatedStorage"),
15            "availability_zone": self._get_param("AvailabilityZone"),
16            "backup_retention_period": self._get_param("BackupRetentionPeriod"),
17            "db_instance_class": self._get_param("DBInstanceClass"),
18            "db_instance_identifier": self._get_param("DBInstanceIdentifier"),
19            "db_name": self._get_param("DBName"),
20            # DBParameterGroupName
21            "db_subnet_group_name": self._get_param("DBSubnetGroupName"),
22            "engine": self._get_param("Engine"),
23            "engine_version": self._get_param("EngineVersion"),
24            "iops": self._get_int_param("Iops"),
25            "kms_key_id": self._get_param("KmsKeyId"),
26            "master_password": self._get_param("MasterUserPassword"),
27            "master_username": self._get_param("MasterUsername"),
28            "multi_az": self._get_bool_param("MultiAZ"),
29            # OptionGroupName
30            "port": self._get_param("Port"),
31            # PreferredBackupWindow
32            # PreferredMaintenanceWindow
33            "publicly_accessible": self._get_param("PubliclyAccessible"),
34            "region": self.region,
35            "security_groups": self._get_multi_param("DBSecurityGroups.member"),
36            "storage_encrypted": self._get_param("StorageEncrypted"),
37            "storage_type": self._get_param("StorageType"),
38            # VpcSecurityGroupIds.member.N
39            "tags": list(),
40        }
41        args["tags"] = self.unpack_complex_list_params("Tags.Tag", ("Key", "Value"))
42        return args
43
44    def _get_db_replica_kwargs(self):
45        return {
46            "auto_minor_version_upgrade": self._get_param("AutoMinorVersionUpgrade"),
47            "availability_zone": self._get_param("AvailabilityZone"),
48            "db_instance_class": self._get_param("DBInstanceClass"),
49            "db_instance_identifier": self._get_param("DBInstanceIdentifier"),
50            "db_subnet_group_name": self._get_param("DBSubnetGroupName"),
51            "iops": self._get_int_param("Iops"),
52            # OptionGroupName
53            "port": self._get_param("Port"),
54            "publicly_accessible": self._get_param("PubliclyAccessible"),
55            "source_db_identifier": self._get_param("SourceDBInstanceIdentifier"),
56            "storage_type": self._get_param("StorageType"),
57        }
58
59    def unpack_complex_list_params(self, label, names):
60        unpacked_list = list()
61        count = 1
62        while self._get_param("{0}.{1}.{2}".format(label, count, names[0])):
63            param = dict()
64            for i in range(len(names)):
65                param[names[i]] = self._get_param(
66                    "{0}.{1}.{2}".format(label, count, names[i])
67                )
68            unpacked_list.append(param)
69            count += 1
70        return unpacked_list
71
72    def create_db_instance(self):
73        db_kwargs = self._get_db_kwargs()
74
75        database = self.backend.create_database(db_kwargs)
76        template = self.response_template(CREATE_DATABASE_TEMPLATE)
77        return template.render(database=database)
78
79    def create_db_instance_read_replica(self):
80        db_kwargs = self._get_db_replica_kwargs()
81
82        database = self.backend.create_database_replica(db_kwargs)
83        template = self.response_template(CREATE_DATABASE_REPLICA_TEMPLATE)
84        return template.render(database=database)
85
86    def describe_db_instances(self):
87        db_instance_identifier = self._get_param("DBInstanceIdentifier")
88        all_instances = list(self.backend.describe_databases(db_instance_identifier))
89        marker = self._get_param("Marker")
90        all_ids = [instance.db_instance_identifier for instance in all_instances]
91        if marker:
92            start = all_ids.index(marker) + 1
93        else:
94            start = 0
95        page_size = self._get_int_param(
96            "MaxRecords", 50
97        )  # the default is 100, but using 50 to make testing easier
98        instances_resp = all_instances[start : start + page_size]
99        next_marker = None
100        if len(all_instances) > start + page_size:
101            next_marker = instances_resp[-1].db_instance_identifier
102
103        template = self.response_template(DESCRIBE_DATABASES_TEMPLATE)
104        return template.render(databases=instances_resp, marker=next_marker)
105
106    def modify_db_instance(self):
107        db_instance_identifier = self._get_param("DBInstanceIdentifier")
108        db_kwargs = self._get_db_kwargs()
109        new_db_instance_identifier = self._get_param("NewDBInstanceIdentifier")
110        if new_db_instance_identifier:
111            db_kwargs["new_db_instance_identifier"] = new_db_instance_identifier
112        database = self.backend.modify_database(db_instance_identifier, db_kwargs)
113        template = self.response_template(MODIFY_DATABASE_TEMPLATE)
114        return template.render(database=database)
115
116    def delete_db_instance(self):
117        db_instance_identifier = self._get_param("DBInstanceIdentifier")
118        database = self.backend.delete_database(db_instance_identifier)
119        template = self.response_template(DELETE_DATABASE_TEMPLATE)
120        return template.render(database=database)
121
122    def create_db_security_group(self):
123        group_name = self._get_param("DBSecurityGroupName")
124        description = self._get_param("DBSecurityGroupDescription")
125        tags = self.unpack_complex_list_params("Tags.Tag", ("Key", "Value"))
126        security_group = self.backend.create_security_group(
127            group_name, description, tags
128        )
129        template = self.response_template(CREATE_SECURITY_GROUP_TEMPLATE)
130        return template.render(security_group=security_group)
131
132    def describe_db_security_groups(self):
133        security_group_name = self._get_param("DBSecurityGroupName")
134        security_groups = self.backend.describe_security_groups(security_group_name)
135        template = self.response_template(DESCRIBE_SECURITY_GROUPS_TEMPLATE)
136        return template.render(security_groups=security_groups)
137
138    def delete_db_security_group(self):
139        security_group_name = self._get_param("DBSecurityGroupName")
140        security_group = self.backend.delete_security_group(security_group_name)
141        template = self.response_template(DELETE_SECURITY_GROUP_TEMPLATE)
142        return template.render(security_group=security_group)
143
144    def authorize_db_security_group_ingress(self):
145        security_group_name = self._get_param("DBSecurityGroupName")
146        cidr_ip = self._get_param("CIDRIP")
147        security_group = self.backend.authorize_security_group(
148            security_group_name, cidr_ip
149        )
150        template = self.response_template(AUTHORIZE_SECURITY_GROUP_TEMPLATE)
151        return template.render(security_group=security_group)
152
153    def create_db_subnet_group(self):
154        subnet_name = self._get_param("DBSubnetGroupName")
155        description = self._get_param("DBSubnetGroupDescription")
156        subnet_ids = self._get_multi_param("SubnetIds.member")
157        subnets = [
158            ec2_backends[self.region].get_subnet(subnet_id) for subnet_id in subnet_ids
159        ]
160        tags = self.unpack_complex_list_params("Tags.Tag", ("Key", "Value"))
161        subnet_group = self.backend.create_subnet_group(
162            subnet_name, description, subnets, tags
163        )
164        template = self.response_template(CREATE_SUBNET_GROUP_TEMPLATE)
165        return template.render(subnet_group=subnet_group)
166
167    def describe_db_subnet_groups(self):
168        subnet_name = self._get_param("DBSubnetGroupName")
169        subnet_groups = self.backend.describe_subnet_groups(subnet_name)
170        template = self.response_template(DESCRIBE_SUBNET_GROUPS_TEMPLATE)
171        return template.render(subnet_groups=subnet_groups)
172
173    def delete_db_subnet_group(self):
174        subnet_name = self._get_param("DBSubnetGroupName")
175        subnet_group = self.backend.delete_subnet_group(subnet_name)
176        template = self.response_template(DELETE_SUBNET_GROUP_TEMPLATE)
177        return template.render(subnet_group=subnet_group)
178
179
180CREATE_DATABASE_TEMPLATE = """<CreateDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
181  <CreateDBInstanceResult>
182    {{ database.to_xml() }}
183  </CreateDBInstanceResult>
184  <ResponseMetadata>
185    <RequestId>523e3218-afc7-11c3-90f5-f90431260ab4</RequestId>
186  </ResponseMetadata>
187</CreateDBInstanceResponse>"""
188
189CREATE_DATABASE_REPLICA_TEMPLATE = """<CreateDBInstanceReadReplicaResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
190  <CreateDBInstanceReadReplicaResult>
191    {{ database.to_xml() }}
192  </CreateDBInstanceReadReplicaResult>
193  <ResponseMetadata>
194    <RequestId>ba8dedf0-bb9a-11d3-855b-576787000e19</RequestId>
195  </ResponseMetadata>
196</CreateDBInstanceReadReplicaResponse>"""
197
198DESCRIBE_DATABASES_TEMPLATE = """<DescribeDBInstancesResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
199  <DescribeDBInstancesResult>
200    <DBInstances>
201    {% for database in databases %}
202        {{ database.to_xml() }}
203    {% endfor %}
204    </DBInstances>
205    {% if marker %}
206    <Marker>{{ marker }}</Marker>
207    {% endif %}
208  </DescribeDBInstancesResult>
209  <ResponseMetadata>
210    <RequestId>01b2685a-b978-11d3-f272-7cd6cce12cc5</RequestId>
211  </ResponseMetadata>
212</DescribeDBInstancesResponse>"""
213
214MODIFY_DATABASE_TEMPLATE = """<ModifyDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
215  <ModifyDBInstanceResult>
216    {{ database.to_xml() }}
217  </ModifyDBInstanceResult>
218  <ResponseMetadata>
219    <RequestId>f643f1ac-bbfe-11d3-f4c6-37db295f7674</RequestId>
220  </ResponseMetadata>
221</ModifyDBInstanceResponse>"""
222
223DELETE_DATABASE_TEMPLATE = """<DeleteDBInstanceResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
224  <DeleteDBInstanceResult>
225    {{ database.to_xml() }}
226  </DeleteDBInstanceResult>
227  <ResponseMetadata>
228    <RequestId>7369556f-b70d-11c3-faca-6ba18376ea1b</RequestId>
229  </ResponseMetadata>
230</DeleteDBInstanceResponse>"""
231
232CREATE_SECURITY_GROUP_TEMPLATE = """<CreateDBSecurityGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
233  <CreateDBSecurityGroupResult>
234    {{ security_group.to_xml() }}
235  </CreateDBSecurityGroupResult>
236  <ResponseMetadata>
237    <RequestId>e68ef6fa-afc1-11c3-845a-476777009d19</RequestId>
238  </ResponseMetadata>
239</CreateDBSecurityGroupResponse>"""
240
241DESCRIBE_SECURITY_GROUPS_TEMPLATE = """<DescribeDBSecurityGroupsResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
242  <DescribeDBSecurityGroupsResult>
243    <DBSecurityGroups>
244    {% for security_group in security_groups %}
245        {{ security_group.to_xml() }}
246    {% endfor %}
247    </DBSecurityGroups>
248  </DescribeDBSecurityGroupsResult>
249  <ResponseMetadata>
250    <RequestId>b76e692c-b98c-11d3-a907-5a2c468b9cb0</RequestId>
251  </ResponseMetadata>
252</DescribeDBSecurityGroupsResponse>"""
253
254DELETE_SECURITY_GROUP_TEMPLATE = """<DeleteDBSecurityGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
255  <ResponseMetadata>
256    <RequestId>7aec7454-ba25-11d3-855b-576787000e19</RequestId>
257  </ResponseMetadata>
258</DeleteDBSecurityGroupResponse>"""
259
260AUTHORIZE_SECURITY_GROUP_TEMPLATE = """<AuthorizeDBSecurityGroupIngressResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
261  <AuthorizeDBSecurityGroupIngressResult>
262  {{ security_group.to_xml() }}
263  </AuthorizeDBSecurityGroupIngressResult>
264  <ResponseMetadata>
265    <RequestId>6176b5f8-bfed-11d3-f92b-31fa5e8dbc99</RequestId>
266  </ResponseMetadata>
267</AuthorizeDBSecurityGroupIngressResponse>"""
268
269CREATE_SUBNET_GROUP_TEMPLATE = """<CreateDBSubnetGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
270  <CreateDBSubnetGroupResult>
271    {{ subnet_group.to_xml() }}
272  </CreateDBSubnetGroupResult>
273  <ResponseMetadata>
274    <RequestId>3a401b3f-bb9e-11d3-f4c6-37db295f7674</RequestId>
275  </ResponseMetadata>
276</CreateDBSubnetGroupResponse>"""
277
278DESCRIBE_SUBNET_GROUPS_TEMPLATE = """<DescribeDBSubnetGroupsResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
279  <DescribeDBSubnetGroupsResult>
280    <DBSubnetGroups>
281    {% for subnet_group in subnet_groups %}
282        {{ subnet_group.to_xml() }}
283    {% endfor %}
284    </DBSubnetGroups>
285  </DescribeDBSubnetGroupsResult>
286  <ResponseMetadata>
287    <RequestId>b783db3b-b98c-11d3-fbc7-5c0aad74da7c</RequestId>
288  </ResponseMetadata>
289</DescribeDBSubnetGroupsResponse>"""
290
291DELETE_SUBNET_GROUP_TEMPLATE = """<DeleteDBSubnetGroupResponse xmlns="http://rds.amazonaws.com/doc/2014-09-01/">
292  <ResponseMetadata>
293    <RequestId>6295e5ab-bbf3-11d3-f4c6-37db295f7674</RequestId>
294  </ResponseMetadata>
295</DeleteDBSubnetGroupResponse>"""
296