from __future__ import unicode_literals from boto3 import Session from jinja2 import Template from moto.core import BaseBackend, CloudFormationModel from moto.ec2.models import ec2_backends from moto.rds.exceptions import UnformattedGetAttTemplateException from moto.rds2.models import rds2_backends class Database(CloudFormationModel): def get_cfn_attribute(self, attribute_name): if attribute_name == "Endpoint.Address": return self.address elif attribute_name == "Endpoint.Port": return self.port raise UnformattedGetAttTemplateException() @staticmethod def cloudformation_name_type(): return "DBInstanceIdentifier" @staticmethod def cloudformation_type(): # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbinstance.html return "AWS::RDS::DBInstance" @classmethod def create_from_cloudformation_json( cls, resource_name, cloudformation_json, region_name ): properties = cloudformation_json["Properties"] db_security_groups = properties.get("DBSecurityGroups") if not db_security_groups: db_security_groups = [] security_groups = [group.group_name for group in db_security_groups] db_subnet_group = properties.get("DBSubnetGroupName") db_subnet_group_name = db_subnet_group.subnet_name if db_subnet_group else None db_kwargs = { "auto_minor_version_upgrade": properties.get("AutoMinorVersionUpgrade"), "allocated_storage": properties.get("AllocatedStorage"), "availability_zone": properties.get("AvailabilityZone"), "backup_retention_period": properties.get("BackupRetentionPeriod"), "db_instance_class": properties.get("DBInstanceClass"), "db_instance_identifier": resource_name, "db_name": properties.get("DBName"), "db_subnet_group_name": db_subnet_group_name, "engine": properties.get("Engine"), "engine_version": properties.get("EngineVersion"), "iops": properties.get("Iops"), "kms_key_id": properties.get("KmsKeyId"), "master_password": properties.get("MasterUserPassword"), "master_username": properties.get("MasterUsername"), "multi_az": properties.get("MultiAZ"), "port": properties.get("Port", 3306), "publicly_accessible": properties.get("PubliclyAccessible"), "copy_tags_to_snapshot": properties.get("CopyTagsToSnapshot"), "region": region_name, "security_groups": security_groups, "storage_encrypted": properties.get("StorageEncrypted"), "storage_type": properties.get("StorageType"), "tags": properties.get("Tags"), } rds_backend = rds_backends[region_name] source_db_identifier = properties.get("SourceDBInstanceIdentifier") if source_db_identifier: # Replica db_kwargs["source_db_identifier"] = source_db_identifier database = rds_backend.create_database_replica(db_kwargs) else: database = rds_backend.create_database(db_kwargs) return database def to_xml(self): template = Template( """ {{ database.backup_retention_period }} {{ database.status }} {{ database.multi_az }} {{ database.db_instance_identifier }} 03:50-04:20 wed:06:38-wed:07:08 {% for replica_id in database.replicas %} {{ replica_id }} {% endfor %} {% if database.is_replica %} read replication replicating true {% endif %} {% if database.is_replica %} {{ database.source_db_identifier }} {% endif %} {{ database.engine }} {{ database.license_model }} {{ database.engine_version }} {% for security_group in database.security_groups %} active {{ security_group }} {% endfor %} {% if database.db_subnet_group %} {{ database.db_subnet_group.subnet_name }} {{ database.db_subnet_group.description }} {{ database.db_subnet_group.status }} {% for subnet in database.db_subnet_group.subnets %} Active {{ subnet.id }} {{ subnet.availability_zone }} false {% endfor %} {{ database.db_subnet_group.vpc_id }} {% endif %} {{ database.publicly_accessible }} {{ database.copy_tags_to_snapshot }} {{ database.auto_minor_version_upgrade }} {{ database.allocated_storage }} {{ database.storage_encrypted }} {% if database.kms_key_id %} {{ database.kms_key_id }} {% endif %} {% if database.iops %} {{ database.iops }} io1 {% else %} {{ database.storage_type }} {% endif %} {{ database.db_instance_class }} {{ database.instance_create_time }} {{ database.master_username }}
{{ database.address }}
{{ database.port }}
{{ database.db_instance_arn }}
""" ) return template.render(database=self) def delete(self, region_name): backend = rds_backends[region_name] backend.delete_database(self.db_instance_identifier) class SecurityGroup(CloudFormationModel): def __init__(self, group_name, description): self.group_name = group_name self.description = description self.status = "authorized" self.ip_ranges = [] self.ec2_security_groups = [] def to_xml(self): template = Template( """ {% for security_group in security_group.ec2_security_groups %} {{ security_group.id }} {{ security_group.name }} {{ security_group.owner_id }} authorized {% endfor %} {{ security_group.description }} {% for ip_range in security_group.ip_ranges %} {{ ip_range }} authorized {% endfor %} {{ security_group.ownder_id }} {{ security_group.group_name }} """ ) return template.render(security_group=self) def authorize_cidr(self, cidr_ip): self.ip_ranges.append(cidr_ip) def authorize_security_group(self, security_group): self.ec2_security_groups.append(security_group) @staticmethod def cloudformation_name_type(): return None @staticmethod def cloudformation_type(): # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbsecuritygroup.html return "AWS::RDS::DBSecurityGroup" @classmethod def create_from_cloudformation_json( cls, resource_name, cloudformation_json, region_name ): properties = cloudformation_json["Properties"] group_name = resource_name.lower() description = properties["GroupDescription"] security_group_ingress_rules = properties.get("DBSecurityGroupIngress", []) tags = properties.get("Tags") ec2_backend = ec2_backends[region_name] rds_backend = rds_backends[region_name] security_group = rds_backend.create_security_group( group_name, description, tags ) for security_group_ingress in security_group_ingress_rules: for ingress_type, ingress_value in security_group_ingress.items(): if ingress_type == "CIDRIP": security_group.authorize_cidr(ingress_value) elif ingress_type == "EC2SecurityGroupName": subnet = ec2_backend.get_security_group_from_name(ingress_value) security_group.authorize_security_group(subnet) elif ingress_type == "EC2SecurityGroupId": subnet = ec2_backend.get_security_group_from_id(ingress_value) security_group.authorize_security_group(subnet) return security_group def delete(self, region_name): backend = rds_backends[region_name] backend.delete_security_group(self.group_name) class SubnetGroup(CloudFormationModel): def __init__(self, subnet_name, description, subnets): self.subnet_name = subnet_name self.description = description self.subnets = subnets self.status = "Complete" self.vpc_id = self.subnets[0].vpc_id def to_xml(self): template = Template( """ {{ subnet_group.vpc_id }} {{ subnet_group.status }} {{ 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) @staticmethod def cloudformation_name_type(): return "DBSubnetGroupName" @staticmethod def cloudformation_type(): # https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-rds-dbsubnetgroup.html return "AWS::RDS::DBSubnetGroup" @classmethod def create_from_cloudformation_json( cls, resource_name, cloudformation_json, region_name ): properties = cloudformation_json["Properties"] subnet_name = resource_name.lower() description = properties["DBSubnetGroupDescription"] subnet_ids = properties["SubnetIds"] tags = properties.get("Tags") ec2_backend = ec2_backends[region_name] subnets = [ec2_backend.get_subnet(subnet_id) for subnet_id in subnet_ids] rds_backend = rds_backends[region_name] subnet_group = rds_backend.create_subnet_group( subnet_name, description, subnets, tags ) return subnet_group def delete(self, region_name): backend = rds_backends[region_name] backend.delete_subnet_group(self.subnet_name) class RDSBackend(BaseBackend): def __init__(self, region): self.region = region def __getattr__(self, attr): return self.rds2_backend().__getattribute__(attr) def reset(self): # preserve region region = self.region self.rds2_backend().reset() self.__dict__ = {} self.__init__(region) def rds2_backend(self): return rds2_backends[self.region] rds_backends = {} for region in Session().get_available_regions("rds"): rds_backends[region] = RDSBackend(region) for region in Session().get_available_regions("rds", partition_name="aws-us-gov"): rds_backends[region] = RDSBackend(region) for region in Session().get_available_regions("rds", partition_name="aws-cn"): rds_backends[region] = RDSBackend(region)