From 74bbd9c8e537286bb00d562c3d28870cef89cd6d Mon Sep 17 00:00:00 2001 From: Michael Nussbaum Date: Wed, 11 Jan 2017 18:02:51 -0800 Subject: [PATCH] Various RDS, RDS/Cloudformation, RDS/KMS improvements. (#789) We need to mock out deploying RDS instances with full disk encryption and detailed tagging. We also need to be able do deploy these instances with Cloudformation, and then access them with both boto and boto3. * Join RDS and RDS2 backends - this makes RDS resources created via either of the two boto RDS APIs visible to both, more closely mirroring how AWS works * Fix RDS responses that were returning JSON but should be returning XML * Add mocking of RDS Cloudformation calls * Add mocking of RDS full disk encryption with KMS * Add mocking of RDS DBParameterGroups * Fix mocking of RDS DBSecurityGroupIngress rules * Make mocking of RDS OptionGroupOptions more accurate * Fix mocking of RDS cross-region DB replication * Add RDS tag support to: * DBs * DBSubnetGroups * DBSecurityGroups Signed-off-by: Andrew Garrett --- moto/cloudformation/parsing.py | 5 +- moto/rds/models.py | 153 +-- moto/rds/responses.py | 24 +- moto/rds2/exceptions.py | 25 +- moto/rds2/models.py | 478 ++++++-- moto/rds2/responses.py | 561 +++++---- .../rds_mysql_with_db_parameter_group.py | 201 ++++ .../fixtures/rds_mysql_with_read_replica.py | 7 +- .../test_cloudformation_stack_integration.py | 40 + tests/test_rds/test_rds.py | 26 + tests/test_rds2/test_rds2.py | 1069 ++++++++++++----- 11 files changed, 1868 insertions(+), 721 deletions(-) create mode 100644 tests/test_cloudformation/fixtures/rds_mysql_with_db_parameter_group.py diff --git a/moto/cloudformation/parsing.py b/moto/cloudformation/parsing.py index 3e348ac37..521658cee 100644 --- a/moto/cloudformation/parsing.py +++ b/moto/cloudformation/parsing.py @@ -14,6 +14,7 @@ from moto.elb import models as elb_models from moto.iam import models as iam_models from moto.kms import models as kms_models from moto.rds import models as rds_models +from moto.rds2 import models as rds2_models from moto.redshift import models as redshift_models from moto.route53 import models as route53_models from moto.s3 import models as s3_models @@ -56,6 +57,7 @@ MODEL_MAP = { "AWS::RDS::DBInstance": rds_models.Database, "AWS::RDS::DBSecurityGroup": rds_models.SecurityGroup, "AWS::RDS::DBSubnetGroup": rds_models.SubnetGroup, + "AWS::RDS::DBParameterGroup": rds2_models.DBParameterGroup, "AWS::Redshift::Cluster": redshift_models.Cluster, "AWS::Redshift::ClusterParameterGroup": redshift_models.ParameterGroup, "AWS::Redshift::ClusterSubnetGroup": redshift_models.SubnetGroup, @@ -311,7 +313,8 @@ class ResourceMap(collections.Mapping): if not resource_json: raise KeyError(resource_logical_id) new_resource = parse_and_create_resource(resource_logical_id, resource_json, self, self._region_name) - self._parsed_resources[resource_logical_id] = new_resource + if new_resource is not None: + self._parsed_resources[resource_logical_id] = new_resource return new_resource def __iter__(self): diff --git a/moto/rds/models.py b/moto/rds/models.py index 3ce005e5e..b63a30737 100644 --- a/moto/rds/models.py +++ b/moto/rds/models.py @@ -10,6 +10,7 @@ from moto.cloudformation.exceptions import UnformattedGetAttTemplateException from moto.core import BaseBackend from moto.core.utils import get_random_hex from moto.ec2.models import ec2_backends +from moto.rds2.models import rds2_backends from .exceptions import DBInstanceNotFoundError, DBSecurityGroupNotFoundError, DBSubnetGroupNotFoundError @@ -26,6 +27,11 @@ class Database(object): if self.engine_version is None: self.engine_version = "5.6.21" self.iops = kwargs.get("iops") + self.storage_encrypted = kwargs.get("storage_encrypted", False) + if self.storage_encrypted: + self.kms_key_id = kwargs.get("kms_key_id", "default_kms_key_id") + else: + self.kms_key_id = kwargs.get("kms_key_id") self.storage_type = kwargs.get("storage_type") self.master_username = kwargs.get('master_username') self.master_password = kwargs.get('master_password') @@ -119,6 +125,7 @@ class Database(object): "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"), @@ -126,7 +133,9 @@ class Database(object): "publicly_accessible": properties.get("PubliclyAccessible"), "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] @@ -204,6 +213,10 @@ class Database(object): {{ database.publicly_accessible }} {{ 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 @@ -220,6 +233,10 @@ class Database(object): """) return template.render(database=self) + def delete(self, region_name): + backend = rds_backends[region_name] + backend.delete_database(self.db_instance_identifier) + class SecurityGroup(object): def __init__(self, group_name, description): @@ -267,25 +284,33 @@ class SecurityGroup(object): properties = cloudformation_json['Properties'] group_name = resource_name.lower() + get_random_hex(12) description = properties['GroupDescription'] - security_group_ingress = properties['DBSecurityGroupIngress'] + 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 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) + + 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(object): def __init__(self, subnet_name, description, subnets): @@ -324,6 +349,7 @@ class SubnetGroup(object): subnet_name = resource_name.lower() + get_random_hex(12) 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] @@ -332,102 +358,31 @@ class SubnetGroup(object): 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): - self.databases = {} - self.security_groups = {} - self.subnet_groups = {} + def __init__(self, region): + self.region = region - def create_database(self, db_kwargs): - database_id = db_kwargs['db_instance_identifier'] - database = Database(**db_kwargs) - self.databases[database_id] = database - return database + def __getattr__(self, attr): + return self.rds2_backend().__getattribute__(attr) - def create_database_replica(self, db_kwargs): - database_id = db_kwargs['db_instance_identifier'] - source_database_id = db_kwargs['source_db_identifier'] - primary = self.describe_databases(source_database_id)[0] - replica = copy.deepcopy(primary) - replica.update(db_kwargs) - replica.set_as_replica() - self.databases[database_id] = replica - primary.add_replica(replica) - return replica + def reset(self): + # preserve region + region = self.region + self.rds2_backend().reset() + self.__dict__ = {} + self.__init__(region) - def describe_databases(self, db_instance_identifier=None): - if db_instance_identifier: - if db_instance_identifier in self.databases: - return [self.databases[db_instance_identifier]] - else: - raise DBInstanceNotFoundError(db_instance_identifier) - return self.databases.values() + def rds2_backend(self): + return rds2_backends[self.region] - def modify_database(self, db_instance_identifier, db_kwargs): - database = self.describe_databases(db_instance_identifier)[0] - database.update(db_kwargs) - return database - - def delete_database(self, db_instance_identifier): - if db_instance_identifier in self.databases: - database = self.databases.pop(db_instance_identifier) - if database.is_replica: - primary = self.describe_databases(database.source_db_identifier)[0] - primary.remove_replica(database) - database.status = 'deleting' - return database - else: - raise DBInstanceNotFoundError(db_instance_identifier) - - def create_security_group(self, group_name, description): - security_group = SecurityGroup(group_name, description) - self.security_groups[group_name] = security_group - return security_group - - def describe_security_groups(self, security_group_name): - if security_group_name: - if security_group_name in self.security_groups: - return [self.security_groups[security_group_name]] - else: - raise DBSecurityGroupNotFoundError(security_group_name) - return self.security_groups.values() - - def delete_security_group(self, security_group_name): - if security_group_name in self.security_groups: - return self.security_groups.pop(security_group_name) - else: - raise DBSecurityGroupNotFoundError(security_group_name) - - def authorize_security_group(self, security_group_name, cidr_ip): - security_group = self.describe_security_groups(security_group_name)[0] - security_group.authorize_cidr(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() +rds_backends = dict((region.name, RDSBackend(region.name)) for region in boto.rds.regions()) diff --git a/moto/rds/responses.py b/moto/rds/responses.py index 98015e7bb..5207264f6 100644 --- a/moto/rds/responses.py +++ b/moto/rds/responses.py @@ -12,7 +12,7 @@ class RDSResponse(BaseResponse): return rds_backends[self.region] def _get_db_kwargs(self): - return { + args = { "auto_minor_version_upgrade": self._get_param('AutoMinorVersionUpgrade'), "allocated_storage": self._get_int_param('AllocatedStorage'), "availability_zone": self._get_param("AvailabilityZone"), @@ -25,6 +25,7 @@ class RDSResponse(BaseResponse): "engine": self._get_param("Engine"), "engine_version": self._get_param("EngineVersion"), "iops": self._get_int_param("Iops"), + "kms_key_id": self._get_param("KmsKeyId"), "master_password": self._get_param('MasterUserPassword'), "master_username": self._get_param('MasterUsername'), "multi_az": self._get_bool_param("MultiAZ"), @@ -35,9 +36,13 @@ class RDSResponse(BaseResponse): "publicly_accessible": self._get_param("PubliclyAccessible"), "region": self.region, "security_groups": self._get_multi_param('DBSecurityGroups.member'), + "storage_encrypted": self._get_param("StorageEncrypted"), "storage_type": self._get_param("StorageType"), # VpcSecurityGroupIds.member.N + "tags": list(), } + args['tags'] = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')) + return args def _get_db_replica_kwargs(self): return { @@ -54,6 +59,17 @@ class RDSResponse(BaseResponse): "storage_type": self._get_param("StorageType"), } + def unpack_complex_list_params(self, label, names): + unpacked_list = list() + count = 1 + while self._get_param('{0}.{1}.{2}'.format(label, count, names[0])): + param = dict() + for i in range(len(names)): + param[names[i]] = self._get_param('{0}.{1}.{2}'.format(label, count, names[i])) + unpacked_list.append(param) + count += 1 + return unpacked_list + def create_dbinstance(self): db_kwargs = self._get_db_kwargs() @@ -90,7 +106,8 @@ class RDSResponse(BaseResponse): def create_dbsecurity_group(self): group_name = self._get_param('DBSecurityGroupName') description = self._get_param('DBSecurityGroupDescription') - security_group = self.backend.create_security_group(group_name, description) + tags = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')) + security_group = self.backend.create_security_group(group_name, description, tags) template = self.response_template(CREATE_SECURITY_GROUP_TEMPLATE) return template.render(security_group=security_group) @@ -118,7 +135,8 @@ class RDSResponse(BaseResponse): 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) + tags = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')) + subnet_group = self.backend.create_subnet_group(subnet_name, description, subnets, tags) template = self.response_template(CREATE_SUBNET_GROUP_TEMPLATE) return template.render(subnet_group=subnet_group) diff --git a/moto/rds2/exceptions.py b/moto/rds2/exceptions.py index a5c935659..6fcae4b56 100644 --- a/moto/rds2/exceptions.py +++ b/moto/rds2/exceptions.py @@ -1,20 +1,22 @@ from __future__ import unicode_literals -import json +from jinja2 import Template from werkzeug.exceptions import BadRequest class RDSClientError(BadRequest): def __init__(self, code, message): super(RDSClientError, self).__init__() - self.description = json.dumps({ - "Error": { - "Code": code, - "Message": message, - 'Type': 'Sender', - }, - 'RequestId': '6876f774-7273-11e4-85dc-39e55ca848d1', - }) + template = Template(""" + + + {{ code }} + {{ message }} + Sender + + 6876f774-7273-11e4-85dc-39e55ca848d1 + """) + self.description = template.render(code=code, message=message) class DBInstanceNotFoundError(RDSClientError): @@ -37,3 +39,8 @@ class DBSubnetGroupNotFoundError(RDSClientError): 'DBSubnetGroupNotFound', "Subnet Group {0} not found.".format(subnet_group_name)) +class DBParameterGroupNotFoundError(RDSClientError): + def __init__(self, db_parameter_group_name): + super(DBParameterGroupNotFoundError, self).__init__( + 'DBParameterGroupNotFound', + 'DB Parameter Group {0} not found.'.format(db_parameter_group_name)) diff --git a/moto/rds2/models.py b/moto/rds2/models.py index 37ecbf873..9bb1f8200 100644 --- a/moto/rds2/models.py +++ b/moto/rds2/models.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals import copy +from collections import defaultdict import boto.rds2 import json from jinja2 import Template @@ -10,7 +11,12 @@ from moto.cloudformation.exceptions import UnformattedGetAttTemplateException from moto.core import BaseBackend from moto.core.utils import get_random_hex from moto.ec2.models import ec2_backends -from .exceptions import RDSClientError, DBInstanceNotFoundError, DBSecurityGroupNotFoundError, DBSubnetGroupNotFoundError +from .exceptions import (RDSClientError, + DBInstanceNotFoundError, + DBSecurityGroupNotFoundError, + DBSubnetGroupNotFoundError, + DBParameterGroupNotFoundError) + class Database(object): @@ -35,6 +41,11 @@ class Database(object): if not self.engine_version and self.engine in self.default_engine_versions: self.engine_version = self.default_engine_versions[self.engine] self.iops = kwargs.get("iops") + self.storage_encrypted = kwargs.get("storage_encrypted", False) + if self.storage_encrypted: + self.kms_key_id = kwargs.get("kms_key_id", "default_kms_key_id") + else: + self.kms_key_id = kwargs.get("kms_key_id") self.storage_type = kwargs.get("storage_type") self.master_username = kwargs.get('master_username') self.master_user_password = kwargs.get('master_user_password') @@ -64,13 +75,9 @@ class Database(object): self.security_groups = kwargs.get('security_groups', []) self.vpc_security_group_ids = kwargs.get('vpc_security_group_ids', []) self.preferred_maintenance_window = kwargs.get('preferred_maintenance_window', 'wed:06:38-wed:07:08') - self.db_parameter_group_name = kwargs.get('db_parameter_group_name', None) - self.default_parameter_groups = {"MySQL": "default.mysql5.6", - "mysql": "default.mysql5.6", - "postgres": "default.postgres9.3" - } - if not self.db_parameter_group_name and self.engine in self.default_parameter_groups: - self.db_parameter_group_name = self.default_parameter_groups[self.engine] + self.db_parameter_group_name = kwargs.get('db_parameter_group_name') + if self.db_parameter_group_name and self.db_parameter_group_name not in rds2_backends[self.region].db_parameter_groups: + raise DBParameterGroupNotFoundError(self.db_parameter_group_name) self.preferred_backup_window = kwargs.get('preferred_backup_window', '13:14-13:44') self.license_model = kwargs.get('license_model', 'general-public-license') @@ -84,6 +91,120 @@ class Database(object): self.character_set_name = kwargs.get('character_set_name', None) self.tags = kwargs.get('tags', []) + @property + def physical_resource_id(self): + return self.db_instance_identifier + + def db_parameter_groups(self): + if not self.db_parameter_group_name: + db_family, db_parameter_group_name = self.default_db_parameter_group_details() + description = 'Default parameter group for {0}'.format(db_family) + return [DBParameterGroup(name=db_parameter_group_name, + family=db_family, + description=description, + tags={})] + else: + return [rds2_backends[self.region].db_parameter_groups[self.db_parameter_group_name]] + + def default_db_parameter_group_details(self): + if not self.engine_version: + return (None, None) + + minor_engine_version = '.'.join(self.engine_version.rsplit('.')[:-1]) + db_family = '{0}{1}'.format(self.engine.lower(), minor_engine_version) + + return db_family, 'default.{0}'.format(db_family) + + 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 }} + general-public-license + {{ database.engine_version }} + + + + {% for db_parameter_group in database.db_parameter_groups() %} + + in-sync + {{ db_parameter_group.name }} + + {% endfor %} + + + {% 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.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.master_username }} + +
{{ database.address }}
+ {{ database.port }} +
+
""") + return template.render(database=self) + @property def address(self): return "{0}.aaaaaaaaaa.{1}.rds.amazonaws.com".format(self.db_instance_identifier, self.region) @@ -135,21 +256,25 @@ class Database(object): "engine": properties.get("Engine"), "engine_version": properties.get("EngineVersion"), "iops": properties.get("Iops"), + "kms_key_id": properties.get("KmsKeyId"), "master_user_password": properties.get('MasterUserPassword'), "master_username": properties.get('MasterUsername'), "multi_az": properties.get("MultiAZ"), + "db_parameter_group_name": properties.get('DBParameterGroupName'), "port": properties.get('Port', 3306), "publicly_accessible": properties.get("PubliclyAccessible"), "region": region_name, "security_groups": security_groups, + "storage_encrypted": properties.get("StorageEncrypted"), "storage_type": properties.get("StorageType"), + "tags": properties.get("Tags"), } rds2_backend = rds2_backends[region_name] source_db_identifier = properties.get("SourceDBInstanceIdentifier") if source_db_identifier: # Replica - db_kwargs["source_db_identifier"] = source_db_identifier.db_instance_identifier + db_kwargs["source_db_identifier"] = source_db_identifier database = rds2_backend.create_database_replica(db_kwargs) else: database = rds2_backend.create_database(db_kwargs) @@ -236,15 +361,19 @@ class Database(object): def remove_tags(self, tag_keys): self.tags = [tag_set for tag_set in self.tags if tag_set['Key'] not in tag_keys] + def delete(self, region_name): + backend = rds2_backends[region_name] + backend.delete_database(self.db_instance_identifier) + class SecurityGroup(object): - def __init__(self, group_name, description): + def __init__(self, group_name, description, tags): self.group_name = group_name self.description = description self.status = "authorized" self.ip_ranges = [] self.ec2_security_groups = [] - self.tags = [] + self.tags = tags self.owner_id = '1234567890' self.vpc_id = None @@ -301,27 +430,29 @@ class SecurityGroup(object): properties = cloudformation_json['Properties'] group_name = resource_name.lower() + get_random_hex(12) description = properties['GroupDescription'] - security_group_ingress = properties['DBSecurityGroupIngress'] + security_group_ingress_rules = properties.get('DBSecurityGroupIngress', []) + tags = properties.get('Tags') ec2_backend = ec2_backends[region_name] rds2_backend = rds2_backends[region_name] security_group = rds2_backend.create_security_group( group_name, description, + tags, ) - 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) + 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 get_tags(self): - # TODO: Write tags add/remove/list tests for SecurityGroups return self.tags def add_tags(self, tags): @@ -333,14 +464,18 @@ class SecurityGroup(object): def remove_tags(self, tag_keys): self.tags = [tag_set for tag_set in self.tags if tag_set['Key'] not in tag_keys] + def delete(self, region_name): + backend = rds2_backends[region_name] + backend.delete_security_group(self.group_name) + class SubnetGroup(object): - def __init__(self, subnet_name, description, subnets): + def __init__(self, subnet_name, description, subnets, tags): self.subnet_name = subnet_name self.description = description self.subnets = subnets self.status = "Complete" - self.tags = [] + self.tags = tags self.vpc_id = self.subnets[0].vpc_id def to_xml(self): @@ -392,6 +527,7 @@ class SubnetGroup(object): subnet_name = resource_name.lower() + get_random_hex(12) 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] @@ -400,11 +536,11 @@ class SubnetGroup(object): subnet_name, description, subnets, + tags, ) return subnet_group def get_tags(self): - # TODO: Write tags add/remove/list tests for SubnetGroups return self.tags def add_tags(self, tags): @@ -416,15 +552,27 @@ class SubnetGroup(object): def remove_tags(self, tag_keys): self.tags = [tag_set for tag_set in self.tags if tag_set['Key'] not in tag_keys] + def delete(self, region_name): + backend = rds2_backends[region_name] + backend.delete_subnet_group(self.subnet_name) + class RDS2Backend(BaseBackend): - def __init__(self): + def __init__(self, region): + self.region = region self.arn_regex = re_compile(r'^arn:aws:rds:.*:[0-9]*:(db|es|og|pg|ri|secgrp|snapshot|subgrp):.*$') self.databases = {} + self.db_parameter_groups = {} + self.option_groups = {} self.security_groups = {} self.subnet_groups = {} - self.option_groups = {} + + def reset(self): + # preserve region + region = self.region + self.__dict__ = {} + self.__init__(region) def create_database(self, db_kwargs): database_id = db_kwargs['db_instance_identifier'] @@ -435,7 +583,10 @@ class RDS2Backend(BaseBackend): def create_database_replica(self, db_kwargs): database_id = db_kwargs['db_instance_identifier'] source_database_id = db_kwargs['source_db_identifier'] - primary = self.describe_databases(source_database_id)[0] + primary = self.find_db_from_id(source_database_id) + if self.arn_regex.match(source_database_id): + db_kwargs['region'] = self.region + replica = copy.deepcopy(primary) replica.update(db_kwargs) replica.set_as_replica() @@ -460,18 +611,31 @@ class RDS2Backend(BaseBackend): database = self.describe_databases(db_instance_identifier)[0] return database + def find_db_from_id(self, db_id): + if self.arn_regex.match(db_id): + arn_breakdown = db_id.split(':') + region = arn_breakdown[3] + backend = rds2_backends[region] + db_name = arn_breakdown[-1] + else: + backend = self + db_name = db_id + + return backend.describe_databases(db_name)[0] + def delete_database(self, db_instance_identifier): if db_instance_identifier in self.databases: database = self.databases.pop(db_instance_identifier) if database.is_replica: - primary = self.describe_databases(database.source_db_identifier)[0] + primary = self.find_db_from_id(database.source_db_identifier) primary.remove_replica(database) + database.status = 'deleting' return database else: raise DBInstanceNotFoundError(db_instance_identifier) - def create_security_group(self, group_name, description): - security_group = SecurityGroup(group_name, description) + def create_security_group(self, group_name, description, tags): + security_group = SecurityGroup(group_name, description, tags) self.security_groups[group_name] = security_group return security_group @@ -489,13 +653,19 @@ class RDS2Backend(BaseBackend): else: raise DBSecurityGroupNotFoundError(security_group_name) + def delete_db_parameter_group(self, db_parameter_group_name): + if db_parameter_group_name in self.db_parameter_groups: + return self.db_parameter_groups.pop(db_parameter_group_name) + else: + raise DBParameterGroupNotFoundError(db_parameter_group_name) + def authorize_security_group(self, security_group_name, cidr_ip): security_group = self.describe_security_groups(security_group_name)[0] security_group.authorize_cidr(cidr_ip) return security_group - def create_subnet_group(self, subnet_name, description, subnets): - subnet_group = SubnetGroup(subnet_name, description, subnets) + def create_subnet_group(self, subnet_name, description, subnets, tags): + subnet_group = SubnetGroup(subnet_name, description, subnets, tags) self.subnet_groups[subnet_name] = subnet_group return subnet_group @@ -580,24 +750,18 @@ class RDS2Backend(BaseBackend): @staticmethod def describe_option_group_options(engine_name, major_engine_version=None): - default_option_group_options = { - 'mysql': {'all': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "12", "OptionsDependedOn": [], "MajorEngineVersion": "5.6", "Persistent": false, "DefaultPort": 11211, "Permanent": false, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies how many memcached read operations (get) to perform before doing a COMMIT to start a new transaction", "DefaultValue": "1", "AllowedValues": "1-4294967295", "IsModifiable": true, "SettingName": "DAEMON_MEMCACHED_R_BATCH_SIZE", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies how many memcached write operations, such as add, set, or incr, to perform before doing a COMMIT to start a new transaction", "DefaultValue": "1", "AllowedValues": "1-4294967295", "IsModifiable": true, "SettingName": "DAEMON_MEMCACHED_W_BATCH_SIZE", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies how often to auto-commit idle connections that use the InnoDB memcached interface.", "DefaultValue": "5", "AllowedValues": "1-1073741824", "IsModifiable": true, "SettingName": "INNODB_API_BK_COMMIT_INTERVAL", "ApplyType": "DYNAMIC"}, {"SettingDescription": "Disables the use of row locks when using the InnoDB memcached interface.", "DefaultValue": "0", "AllowedValues": "0,1", "IsModifiable": true, "SettingName": "INNODB_API_DISABLE_ROWLOCK", "ApplyType": "STATIC"}, {"SettingDescription": "Locks the table used by the InnoDB memcached plugin, so that it cannot be dropped or altered by DDL through the SQL interface.", "DefaultValue": "0", "AllowedValues": "0,1", "IsModifiable": true, "SettingName": "INNODB_API_ENABLE_MDL", "ApplyType": "STATIC"}, {"SettingDescription": "Lets you control the transaction isolation level on queries processed by the memcached interface.", "DefaultValue": "0", "AllowedValues": "0-3", "IsModifiable": true, "SettingName": "INNODB_API_TRX_LEVEL", "ApplyType": "STATIC"}, {"SettingDescription": "The binding protocol to use which can be either auto, ascii, or binary. The default is auto which means the server automatically negotiates the protocol with the client.", "DefaultValue": "auto", "AllowedValues": "auto,ascii,binary", "IsModifiable": true, "SettingName": "BINDING_PROTOCOL", "ApplyType": "STATIC"}, {"SettingDescription": "The backlog queue configures how many network connections can be waiting to be processed by memcached", "DefaultValue": "1024", "AllowedValues": "1-2048", "IsModifiable": true, "SettingName": "BACKLOG_QUEUE_LIMIT", "ApplyType": "STATIC"}, {"SettingDescription": "Disable the use of compare and swap (CAS) which reduces the per-item size by 8 bytes.", "DefaultValue": "0", "AllowedValues": "0,1", "IsModifiable": true, "SettingName": "CAS_DISABLED", "ApplyType": "STATIC"}, {"SettingDescription": "Minimum chunk size in bytes to allocate for the smallest item\'s key, value, and flags. The default is 48 and you can get a significant memory efficiency gain with a lower value.", "DefaultValue": "48", "AllowedValues": "1-48", "IsModifiable": true, "SettingName": "CHUNK_SIZE", "ApplyType": "STATIC"}, {"SettingDescription": "Chunk size growth factor that controls the size of each successive chunk with each chunk growing times this amount larger than the previous chunk.", "DefaultValue": "1.25", "AllowedValues": "1-2", "IsModifiable": true, "SettingName": "CHUNK_SIZE_GROWTH_FACTOR", "ApplyType": "STATIC"}, {"SettingDescription": "If enabled when there is no more memory to store items, memcached will return an error rather than evicting items.", "DefaultValue": "0", "AllowedValues": "0,1", "IsModifiable": true, "SettingName": "ERROR_ON_MEMORY_EXHAUSTED", "ApplyType": "STATIC"}, {"SettingDescription": "Maximum number of concurrent connections. Setting this value to anything less than 10 prevents MySQL from starting.", "DefaultValue": "1024", "AllowedValues": "10-1024", "IsModifiable": true, "SettingName": "MAX_SIMULTANEOUS_CONNECTIONS", "ApplyType": "STATIC"}, {"SettingDescription": "Verbose level for memcached.", "DefaultValue": "v", "AllowedValues": "v,vv,vvv", "IsModifiable": true, "SettingName": "VERBOSITY", "ApplyType": "STATIC"}], "EngineName": "mysql", "Name": "MEMCACHED", "PortRequired": true, "Description": "Innodb Memcached for MySQL"}]}, "ResponseMetadata": {"RequestId": "c9847a08-9fca-11e4-9084-5754f80d5144"}}}', - '5.6': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "12", "OptionsDependedOn": [], "MajorEngineVersion": "5.6", "Persistent": false, "DefaultPort": 11211, "Permanent": false, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies how many memcached read operations (get) to perform before doing a COMMIT to start a new transaction", "DefaultValue": "1", "AllowedValues": "1-4294967295", "IsModifiable": true, "SettingName": "DAEMON_MEMCACHED_R_BATCH_SIZE", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies how many memcached write operations, such as add, set, or incr, to perform before doing a COMMIT to start a new transaction", "DefaultValue": "1", "AllowedValues": "1-4294967295", "IsModifiable": true, "SettingName": "DAEMON_MEMCACHED_W_BATCH_SIZE", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies how often to auto-commit idle connections that use the InnoDB memcached interface.", "DefaultValue": "5", "AllowedValues": "1-1073741824", "IsModifiable": true, "SettingName": "INNODB_API_BK_COMMIT_INTERVAL", "ApplyType": "DYNAMIC"}, {"SettingDescription": "Disables the use of row locks when using the InnoDB memcached interface.", "DefaultValue": "0", "AllowedValues": "0,1", "IsModifiable": true, "SettingName": "INNODB_API_DISABLE_ROWLOCK", "ApplyType": "STATIC"}, {"SettingDescription": "Locks the table used by the InnoDB memcached plugin, so that it cannot be dropped or altered by DDL through the SQL interface.", "DefaultValue": "0", "AllowedValues": "0,1", "IsModifiable": true, "SettingName": "INNODB_API_ENABLE_MDL", "ApplyType": "STATIC"}, {"SettingDescription": "Lets you control the transaction isolation level on queries processed by the memcached interface.", "DefaultValue": "0", "AllowedValues": "0-3", "IsModifiable": true, "SettingName": "INNODB_API_TRX_LEVEL", "ApplyType": "STATIC"}, {"SettingDescription": "The binding protocol to use which can be either auto, ascii, or binary. The default is auto which means the server automatically negotiates the protocol with the client.", "DefaultValue": "auto", "AllowedValues": "auto,ascii,binary", "IsModifiable": true, "SettingName": "BINDING_PROTOCOL", "ApplyType": "STATIC"}, {"SettingDescription": "The backlog queue configures how many network connections can be waiting to be processed by memcached", "DefaultValue": "1024", "AllowedValues": "1-2048", "IsModifiable": true, "SettingName": "BACKLOG_QUEUE_LIMIT", "ApplyType": "STATIC"}, {"SettingDescription": "Disable the use of compare and swap (CAS) which reduces the per-item size by 8 bytes.", "DefaultValue": "0", "AllowedValues": "0,1", "IsModifiable": true, "SettingName": "CAS_DISABLED", "ApplyType": "STATIC"}, {"SettingDescription": "Minimum chunk size in bytes to allocate for the smallest item\'s key, value, and flags. The default is 48 and you can get a significant memory efficiency gain with a lower value.", "DefaultValue": "48", "AllowedValues": "1-48", "IsModifiable": true, "SettingName": "CHUNK_SIZE", "ApplyType": "STATIC"}, {"SettingDescription": "Chunk size growth factor that controls the size of each successive chunk with each chunk growing times this amount larger than the previous chunk.", "DefaultValue": "1.25", "AllowedValues": "1-2", "IsModifiable": true, "SettingName": "CHUNK_SIZE_GROWTH_FACTOR", "ApplyType": "STATIC"}, {"SettingDescription": "If enabled when there is no more memory to store items, memcached will return an error rather than evicting items.", "DefaultValue": "0", "AllowedValues": "0,1", "IsModifiable": true, "SettingName": "ERROR_ON_MEMORY_EXHAUSTED", "ApplyType": "STATIC"}, {"SettingDescription": "Maximum number of concurrent connections. Setting this value to anything less than 10 prevents MySQL from starting.", "DefaultValue": "1024", "AllowedValues": "10-1024", "IsModifiable": true, "SettingName": "MAX_SIMULTANEOUS_CONNECTIONS", "ApplyType": "STATIC"}, {"SettingDescription": "Verbose level for memcached.", "DefaultValue": "v", "AllowedValues": "v,vv,vvv", "IsModifiable": true, "SettingName": "VERBOSITY", "ApplyType": "STATIC"}], "EngineName": "mysql", "Name": "MEMCACHED", "PortRequired": true, "Description": "Innodb Memcached for MySQL"}]}, "ResponseMetadata": {"RequestId": "c9847a08-9fca-11e4-9084-5754f80d5144"}}}', - }, - 'sqlserver-ee': {'all': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "2789.0.v1", "OptionsDependedOn": [], "MajorEngineVersion": "10.50", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "sqlserver-ee", "Name": "Mirroring", "PortRequired": false, "Description": "SQLServer Database Mirroring"}, {"MinimumRequiredMinorEngineVersion": "2789.0.v1", "OptionsDependedOn": [], "MajorEngineVersion": "10.50", "Persistent": true, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "sqlserver-ee", "Name": "TDE", "PortRequired": false, "Description": "SQL Server - Transparent Data Encryption"}, {"MinimumRequiredMinorEngineVersion": "2100.60.v1", "OptionsDependedOn": [], "MajorEngineVersion": "11.00", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "sqlserver-ee", "Name": "Mirroring", "PortRequired": false, "Description": "SQLServer Database Mirroring"}, {"MinimumRequiredMinorEngineVersion": "2100.60.v1", "OptionsDependedOn": [], "MajorEngineVersion": "11.00", "Persistent": true, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "sqlserver-ee", "Name": "TDE", "PortRequired": false, "Description": "SQL Server - Transparent Data Encryption"}]}, "ResponseMetadata": {"RequestId": "c9f2fd9b-9fcb-11e4-8add-31b6fe33145f"}}}', - '10.50': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "2789.0.v1", "OptionsDependedOn": [], "MajorEngineVersion": "10.50", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "sqlserver-ee", "Name": "Mirroring", "PortRequired": false, "Description": "SQLServer Database Mirroring"}, {"MinimumRequiredMinorEngineVersion": "2789.0.v1", "OptionsDependedOn": [], "MajorEngineVersion": "10.50", "Persistent": true, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "sqlserver-ee", "Name": "TDE", "PortRequired": false, "Description": "SQL Server - Transparent Data Encryption"}]}, "ResponseMetadata": {"RequestId": "e6326fd0-9fcb-11e4-99cf-55e92d4bbada"}}}', - '11.00': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "2100.60.v1", "OptionsDependedOn": [], "MajorEngineVersion": "11.00", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "sqlserver-ee", "Name": "Mirroring", "PortRequired": false, "Description": "SQLServer Database Mirroring"}, {"MinimumRequiredMinorEngineVersion": "2100.60.v1", "OptionsDependedOn": [], "MajorEngineVersion": "11.00", "Persistent": true, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "sqlserver-ee", "Name": "TDE", "PortRequired": false, "Description": "SQL Server - Transparent Data Encryption"}]}, "ResponseMetadata": {"RequestId": "222cbeeb-9fcc-11e4-bb07-576f5bf522b5"}}}' - }, - 'oracle-ee': {'all': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["XMLDB"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX", "PortRequired": false, "Description": "Oracle Application Express Runtime Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["APEX"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX-DEV", "PortRequired": false, "Description": "Oracle Application Express Development Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the desired encryption behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies the desired data integrity behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of encryption algorithms in order of intended use", "DefaultValue": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "AllowedValues": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_TYPES_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of checksumming algorithms in order of intended use", "DefaultValue": "SHA1,MD5", "AllowedValues": "SHA1,MD5", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER", "ApplyType": "STATIC"}], "EngineName": "oracle-ee", "Name": "NATIVE_NETWORK_ENCRYPTION", "PortRequired": false, "Description": "Oracle Advanced Security - Native Network Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": 1158, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "OEM", "PortRequired": true, "Description": "Oracle Enterprise Manager (Database Control only)"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "STATSPACK", "PortRequired": false, "Description": "Oracle Statspack"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE", "PortRequired": false, "Description": "Oracle Advanced Security - Transparent Data Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE_HSM", "PortRequired": false, "Description": "Oracle Advanced Security - TDE with HSM"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the timezone the user wants to change the system time to", "DefaultValue": "UTC", "AllowedValues": "Africa/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTC", "IsModifiable": true, "SettingName": "TIME_ZONE", "ApplyType": "DYNAMIC"}], "EngineName": "oracle-ee", "Name": "Timezone", "PortRequired": false, "Description": "Change time zone"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "XMLDB", "PortRequired": false, "Description": "Oracle XMLDB Repository"}]}, "ResponseMetadata": {"RequestId": "36a0a612-9fcc-11e4-a07c-e12b0fcebb71"}}}', - '11.2': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["XMLDB"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX", "PortRequired": false, "Description": "Oracle Application Express Runtime Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["APEX"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX-DEV", "PortRequired": false, "Description": "Oracle Application Express Development Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the desired encryption behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies the desired data integrity behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of encryption algorithms in order of intended use", "DefaultValue": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "AllowedValues": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_TYPES_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of checksumming algorithms in order of intended use", "DefaultValue": "SHA1,MD5", "AllowedValues": "SHA1,MD5", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER", "ApplyType": "STATIC"}], "EngineName": "oracle-ee", "Name": "NATIVE_NETWORK_ENCRYPTION", "PortRequired": false, "Description": "Oracle Advanced Security - Native Network Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": 1158, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "OEM", "PortRequired": true, "Description": "Oracle Enterprise Manager (Database Control only)"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "STATSPACK", "PortRequired": false, "Description": "Oracle Statspack"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE", "PortRequired": false, "Description": "Oracle Advanced Security - Transparent Data Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE_HSM", "PortRequired": false, "Description": "Oracle Advanced Security - TDE with HSM"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the timezone the user wants to change the system time to", "DefaultValue": "UTC", "AllowedValues": "Africa/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTC", "IsModifiable": true, "SettingName": "TIME_ZONE", "ApplyType": "DYNAMIC"}], "EngineName": "oracle-ee", "Name": "Timezone", "PortRequired": false, "Description": "Change time zone"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "XMLDB", "PortRequired": false, "Description": "Oracle XMLDB Repository"}]}, "ResponseMetadata": {"RequestId": "36a0a612-9fcc-11e4-a07c-e12b0fcebb71"}}}' - }, - 'oracle-sa': {'all': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["XMLDB"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX", "PortRequired": false, "Description": "Oracle Application Express Runtime Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["APEX"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX-DEV", "PortRequired": false, "Description": "Oracle Application Express Development Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the desired encryption behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies the desired data integrity behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of encryption algorithms in order of intended use", "DefaultValue": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "AllowedValues": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_TYPES_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of checksumming algorithms in order of intended use", "DefaultValue": "SHA1,MD5", "AllowedValues": "SHA1,MD5", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER", "ApplyType": "STATIC"}], "EngineName": "oracle-ee", "Name": "NATIVE_NETWORK_ENCRYPTION", "PortRequired": false, "Description": "Oracle Advanced Security - Native Network Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": 1158, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "OEM", "PortRequired": true, "Description": "Oracle Enterprise Manager (Database Control only)"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "STATSPACK", "PortRequired": false, "Description": "Oracle Statspack"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE", "PortRequired": false, "Description": "Oracle Advanced Security - Transparent Data Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE_HSM", "PortRequired": false, "Description": "Oracle Advanced Security - TDE with HSM"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the timezone the user wants to change the system time to", "DefaultValue": "UTC", "AllowedValues": "Africa/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTC", "IsModifiable": true, "SettingName": "TIME_ZONE", "ApplyType": "DYNAMIC"}], "EngineName": "oracle-ee", "Name": "Timezone", "PortRequired": false, "Description": "Change time zone"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "XMLDB", "PortRequired": false, "Description": "Oracle XMLDB Repository"}]}, "ResponseMetadata": {"RequestId": "36a0a612-9fcc-11e4-a07c-e12b0fcebb71"}}}', - '11.2': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["XMLDB"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX", "PortRequired": false, "Description": "Oracle Application Express Runtime Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["APEX"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX-DEV", "PortRequired": false, "Description": "Oracle Application Express Development Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the desired encryption behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies the desired data integrity behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of encryption algorithms in order of intended use", "DefaultValue": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "AllowedValues": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_TYPES_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of checksumming algorithms in order of intended use", "DefaultValue": "SHA1,MD5", "AllowedValues": "SHA1,MD5", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER", "ApplyType": "STATIC"}], "EngineName": "oracle-ee", "Name": "NATIVE_NETWORK_ENCRYPTION", "PortRequired": false, "Description": "Oracle Advanced Security - Native Network Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": 1158, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "OEM", "PortRequired": true, "Description": "Oracle Enterprise Manager (Database Control only)"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "STATSPACK", "PortRequired": false, "Description": "Oracle Statspack"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE", "PortRequired": false, "Description": "Oracle Advanced Security - Transparent Data Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE_HSM", "PortRequired": false, "Description": "Oracle Advanced Security - TDE with HSM"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the timezone the user wants to change the system time to", "DefaultValue": "UTC", "AllowedValues": "Africa/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTC", "IsModifiable": true, "SettingName": "TIME_ZONE", "ApplyType": "DYNAMIC"}], "EngineName": "oracle-ee", "Name": "Timezone", "PortRequired": false, "Description": "Change time zone"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "XMLDB", "PortRequired": false, "Description": "Oracle XMLDB Repository"}]}, "ResponseMetadata": {"RequestId": "36a0a612-9fcc-11e4-a07c-e12b0fcebb71"}}}' - }, - 'oracle-sa1': {'all': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["XMLDB"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX", "PortRequired": false, "Description": "Oracle Application Express Runtime Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["APEX"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX-DEV", "PortRequired": false, "Description": "Oracle Application Express Development Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the desired encryption behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies the desired data integrity behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of encryption algorithms in order of intended use", "DefaultValue": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "AllowedValues": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_TYPES_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of checksumming algorithms in order of intended use", "DefaultValue": "SHA1,MD5", "AllowedValues": "SHA1,MD5", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER", "ApplyType": "STATIC"}], "EngineName": "oracle-ee", "Name": "NATIVE_NETWORK_ENCRYPTION", "PortRequired": false, "Description": "Oracle Advanced Security - Native Network Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": 1158, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "OEM", "PortRequired": true, "Description": "Oracle Enterprise Manager (Database Control only)"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "STATSPACK", "PortRequired": false, "Description": "Oracle Statspack"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE", "PortRequired": false, "Description": "Oracle Advanced Security - Transparent Data Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE_HSM", "PortRequired": false, "Description": "Oracle Advanced Security - TDE with HSM"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the timezone the user wants to change the system time to", "DefaultValue": "UTC", "AllowedValues": "Africa/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTC", "IsModifiable": true, "SettingName": "TIME_ZONE", "ApplyType": "DYNAMIC"}], "EngineName": "oracle-ee", "Name": "Timezone", "PortRequired": false, "Description": "Change time zone"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "XMLDB", "PortRequired": false, "Description": "Oracle XMLDB Repository"}]}, "ResponseMetadata": {"RequestId": "36a0a612-9fcc-11e4-a07c-e12b0fcebb71"}}}', - '11.2': '{"DescribeOptionGroupOptionsResponse": {"DescribeOptionGroupOptionsResult": {"Marker": null, "OptionGroupOptions": [{"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["XMLDB"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX", "PortRequired": false, "Description": "Oracle Application Express Runtime Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": ["APEX"], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "APEX-DEV", "PortRequired": false, "Description": "Oracle Application Express Development Environment"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the desired encryption behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies the desired data integrity behavior", "DefaultValue": "REQUESTED", "AllowedValues": "ACCEPTED,REJECTED,REQUESTED,REQUIRED", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of encryption algorithms in order of intended use", "DefaultValue": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "AllowedValues": "RC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40", "IsModifiable": true, "SettingName": "SQLNET.ENCRYPTION_TYPES_SERVER", "ApplyType": "STATIC"}, {"SettingDescription": "Specifies list of checksumming algorithms in order of intended use", "DefaultValue": "SHA1,MD5", "AllowedValues": "SHA1,MD5", "IsModifiable": true, "SettingName": "SQLNET.CRYPTO_CHECKSUM_TYPES_SERVER", "ApplyType": "STATIC"}], "EngineName": "oracle-ee", "Name": "NATIVE_NETWORK_ENCRYPTION", "PortRequired": false, "Description": "Oracle Advanced Security - Native Network Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": 1158, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "OEM", "PortRequired": true, "Description": "Oracle Enterprise Manager (Database Control only)"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "STATSPACK", "PortRequired": false, "Description": "Oracle Statspack"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE", "PortRequired": false, "Description": "Oracle Advanced Security - Transparent Data Encryption"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "TDE_HSM", "PortRequired": false, "Description": "Oracle Advanced Security - TDE with HSM"}, {"MinimumRequiredMinorEngineVersion": "0.2.v3", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": true, "DefaultPort": null, "Permanent": true, "OptionGroupOptionSettings": [{"SettingDescription": "Specifies the timezone the user wants to change the system time to", "DefaultValue": "UTC", "AllowedValues": "Africa/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTC", "IsModifiable": true, "SettingName": "TIME_ZONE", "ApplyType": "DYNAMIC"}], "EngineName": "oracle-ee", "Name": "Timezone", "PortRequired": false, "Description": "Change time zone"}, {"MinimumRequiredMinorEngineVersion": "0.2.v4", "OptionsDependedOn": [], "MajorEngineVersion": "11.2", "Persistent": false, "DefaultPort": null, "Permanent": false, "OptionGroupOptionSettings": [], "EngineName": "oracle-ee", "Name": "XMLDB", "PortRequired": false, "Description": "Oracle XMLDB Repository"}]}, "ResponseMetadata": {"RequestId": "36a0a612-9fcc-11e4-a07c-e12b0fcebb71"}}}' - } - } + default_option_group_options = {'mysql': {'5.6': '\n \n \n \n 5.611211TrueInnodb Memcached for MySQLMEMCACHED1-4294967295STATIC1TrueSpecifies how many memcached read operations (get) to perform before doing a COMMIT to start a new transactionDAEMON_MEMCACHED_R_BATCH_SIZE1-4294967295STATIC1TrueSpecifies how many memcached write operations, such as add, set, or incr, to perform before doing a COMMIT to start a new transactionDAEMON_MEMCACHED_W_BATCH_SIZE1-1073741824DYNAMIC5TrueSpecifies how often to auto-commit idle connections that use the InnoDB memcached interface.INNODB_API_BK_COMMIT_INTERVAL0,1STATIC0TrueDisables the use of row locks when using the InnoDB memcached interface.INNODB_API_DISABLE_ROWLOCK0,1STATIC0TrueLocks the table used by the InnoDB memcached plugin, so that it cannot be dropped or altered by DDL through the SQL interface.INNODB_API_ENABLE_MDL0-3STATIC0TrueLets you control the transaction isolation level on queries processed by the memcached interface.INNODB_API_TRX_LEVELauto,ascii,binarySTATICautoTrueThe binding protocol to use which can be either auto, ascii, or binary. The default is auto which means the server automatically negotiates the protocol with the client.BINDING_PROTOCOL1-2048STATIC1024TrueThe backlog queue configures how many network connections can be waiting to be processed by memcachedBACKLOG_QUEUE_LIMIT0,1STATIC0TrueDisable the use of compare and swap (CAS) which reduces the per-item size by 8 bytes.CAS_DISABLED1-48STATIC48TrueMinimum chunk size in bytes to allocate for the smallest item\'s key, value, and flags. The default is 48 and you can get a significant memory efficiency gain with a lower value.CHUNK_SIZE1-2STATIC1.25TrueChunk size growth factor that controls the size of each successive chunk with each chunk growing times this amount larger than the previous chunk.CHUNK_SIZE_GROWTH_FACTOR0,1STATIC0TrueIf enabled when there is no more memory to store items, memcached will return an error rather than evicting items.ERROR_ON_MEMORY_EXHAUSTED10-1024STATIC1024TrueMaximum number of concurrent connections. Setting this value to anything less than 10 prevents MySQL from starting.MAX_SIMULTANEOUS_CONNECTIONSv,vv,vvvSTATICvTrueVerbose level for memcached.VERBOSITYmysql\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n', + 'all': '\n \n \n \n 5.611211TrueInnodb Memcached for MySQLMEMCACHED1-4294967295STATIC1TrueSpecifies how many memcached read operations (get) to perform before doing a COMMIT to start a new transactionDAEMON_MEMCACHED_R_BATCH_SIZE1-4294967295STATIC1TrueSpecifies how many memcached write operations, such as add, set, or incr, to perform before doing a COMMIT to start a new transactionDAEMON_MEMCACHED_W_BATCH_SIZE1-1073741824DYNAMIC5TrueSpecifies how often to auto-commit idle connections that use the InnoDB memcached interface.INNODB_API_BK_COMMIT_INTERVAL0,1STATIC0TrueDisables the use of row locks when using the InnoDB memcached interface.INNODB_API_DISABLE_ROWLOCK0,1STATIC0TrueLocks the table used by the InnoDB memcached plugin, so that it cannot be dropped or altered by DDL through the SQL interface.INNODB_API_ENABLE_MDL0-3STATIC0TrueLets you control the transaction isolation level on queries processed by the memcached interface.INNODB_API_TRX_LEVELauto,ascii,binarySTATICautoTrueThe binding protocol to use which can be either auto, ascii, or binary. The default is auto which means the server automatically negotiates the protocol with the client.BINDING_PROTOCOL1-2048STATIC1024TrueThe backlog queue configures how many network connections can be waiting to be processed by memcachedBACKLOG_QUEUE_LIMIT0,1STATIC0TrueDisable the use of compare and swap (CAS) which reduces the per-item size by 8 bytes.CAS_DISABLED1-48STATIC48TrueMinimum chunk size in bytes to allocate for the smallest item\'s key, value, and flags. The default is 48 and you can get a significant memory efficiency gain with a lower value.CHUNK_SIZE1-2STATIC1.25TrueChunk size growth factor that controls the size of each successive chunk with each chunk growing times this amount larger than the previous chunk.CHUNK_SIZE_GROWTH_FACTOR0,1STATIC0TrueIf enabled when there is no more memory to store items, memcached will return an error rather than evicting items.ERROR_ON_MEMORY_EXHAUSTED10-1024STATIC1024TrueMaximum number of concurrent connections. Setting this value to anything less than 10 prevents MySQL from starting.MAX_SIMULTANEOUS_CONNECTIONSv,vv,vvvSTATICvTrueVerbose level for memcached.VERBOSITYmysql\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n'}, + 'oracle-ee': {'11.2': '\n \n \n \n 11.2XMLDBOracle Application Express Runtime EnvironmentAPEXoracle-ee\n \n 11.2APEXOracle Application Express Development EnvironmentAPEX-DEVoracle-ee\n \n 11.2Oracle Advanced Security - Native Network EncryptionNATIVE_NETWORK_ENCRYPTIONACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired encryption behaviorSQLNET.ENCRYPTION_SERVERACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired data integrity behaviorSQLNET.CRYPTO_CHECKSUM_SERVERRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40STATICRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40TrueSpecifies list of encryption algorithms in order of intended useSQLNET.ENCRYPTION_TYPES_SERVERSHA1,MD5STATICSHA1,MD5TrueSpecifies list of checksumming algorithms in order of intended useSQLNET.CRYPTO_CHECKSUM_TYPES_SERVERoracle-ee\n \n 11.21158TrueOracle Enterprise Manager (Database Control only)OEMoracle-ee\n \n 11.2Oracle StatspackSTATSPACKoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - Transparent Data EncryptionTDEoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - TDE with HSMTDE_HSMoracle-ee\n \n 11.2TrueTrueChange time zoneTimezoneAfrica/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTCDYNAMICUTCTrueSpecifies the timezone the user wants to change the system time toTIME_ZONEoracle-ee\n \n 11.2Oracle XMLDB RepositoryXMLDBoracle-ee\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n', + 'all': '\n \n \n \n 11.2XMLDBOracle Application Express Runtime EnvironmentAPEXoracle-ee\n \n 11.2APEXOracle Application Express Development EnvironmentAPEX-DEVoracle-ee\n \n 11.2Oracle Advanced Security - Native Network EncryptionNATIVE_NETWORK_ENCRYPTIONACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired encryption behaviorSQLNET.ENCRYPTION_SERVERACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired data integrity behaviorSQLNET.CRYPTO_CHECKSUM_SERVERRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40STATICRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40TrueSpecifies list of encryption algorithms in order of intended useSQLNET.ENCRYPTION_TYPES_SERVERSHA1,MD5STATICSHA1,MD5TrueSpecifies list of checksumming algorithms in order of intended useSQLNET.CRYPTO_CHECKSUM_TYPES_SERVERoracle-ee\n \n 11.21158TrueOracle Enterprise Manager (Database Control only)OEMoracle-ee\n \n 11.2Oracle StatspackSTATSPACKoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - Transparent Data EncryptionTDEoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - TDE with HSMTDE_HSMoracle-ee\n \n 11.2TrueTrueChange time zoneTimezoneAfrica/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTCDYNAMICUTCTrueSpecifies the timezone the user wants to change the system time toTIME_ZONEoracle-ee\n \n 11.2Oracle XMLDB RepositoryXMLDBoracle-ee\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n'}, + 'oracle-sa': {'11.2': '\n \n \n \n 11.2XMLDBOracle Application Express Runtime EnvironmentAPEXoracle-ee\n \n 11.2APEXOracle Application Express Development EnvironmentAPEX-DEVoracle-ee\n \n 11.2Oracle Advanced Security - Native Network EncryptionNATIVE_NETWORK_ENCRYPTIONACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired encryption behaviorSQLNET.ENCRYPTION_SERVERACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired data integrity behaviorSQLNET.CRYPTO_CHECKSUM_SERVERRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40STATICRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40TrueSpecifies list of encryption algorithms in order of intended useSQLNET.ENCRYPTION_TYPES_SERVERSHA1,MD5STATICSHA1,MD5TrueSpecifies list of checksumming algorithms in order of intended useSQLNET.CRYPTO_CHECKSUM_TYPES_SERVERoracle-ee\n \n 11.21158TrueOracle Enterprise Manager (Database Control only)OEMoracle-ee\n \n 11.2Oracle StatspackSTATSPACKoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - Transparent Data EncryptionTDEoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - TDE with HSMTDE_HSMoracle-ee\n \n 11.2TrueTrueChange time zoneTimezoneAfrica/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTCDYNAMICUTCTrueSpecifies the timezone the user wants to change the system time toTIME_ZONEoracle-ee\n \n 11.2Oracle XMLDB RepositoryXMLDBoracle-ee\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n', + 'all': '\n \n \n \n 11.2XMLDBOracle Application Express Runtime EnvironmentAPEXoracle-ee\n \n 11.2APEXOracle Application Express Development EnvironmentAPEX-DEVoracle-ee\n \n 11.2Oracle Advanced Security - Native Network EncryptionNATIVE_NETWORK_ENCRYPTIONACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired encryption behaviorSQLNET.ENCRYPTION_SERVERACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired data integrity behaviorSQLNET.CRYPTO_CHECKSUM_SERVERRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40STATICRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40TrueSpecifies list of encryption algorithms in order of intended useSQLNET.ENCRYPTION_TYPES_SERVERSHA1,MD5STATICSHA1,MD5TrueSpecifies list of checksumming algorithms in order of intended useSQLNET.CRYPTO_CHECKSUM_TYPES_SERVERoracle-ee\n \n 11.21158TrueOracle Enterprise Manager (Database Control only)OEMoracle-ee\n \n 11.2Oracle StatspackSTATSPACKoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - Transparent Data EncryptionTDEoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - TDE with HSMTDE_HSMoracle-ee\n \n 11.2TrueTrueChange time zoneTimezoneAfrica/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTCDYNAMICUTCTrueSpecifies the timezone the user wants to change the system time toTIME_ZONEoracle-ee\n \n 11.2Oracle XMLDB RepositoryXMLDBoracle-ee\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n'}, + 'oracle-sa1': {'11.2': '\n \n \n \n 11.2XMLDBOracle Application Express Runtime EnvironmentAPEXoracle-ee\n \n 11.2APEXOracle Application Express Development EnvironmentAPEX-DEVoracle-ee\n \n 11.2Oracle Advanced Security - Native Network EncryptionNATIVE_NETWORK_ENCRYPTIONACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired encryption behaviorSQLNET.ENCRYPTION_SERVERACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired data integrity behaviorSQLNET.CRYPTO_CHECKSUM_SERVERRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40STATICRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40TrueSpecifies list of encryption algorithms in order of intended useSQLNET.ENCRYPTION_TYPES_SERVERSHA1,MD5STATICSHA1,MD5TrueSpecifies list of checksumming algorithms in order of intended useSQLNET.CRYPTO_CHECKSUM_TYPES_SERVERoracle-ee\n \n 11.21158TrueOracle Enterprise Manager (Database Control only)OEMoracle-ee\n \n 11.2Oracle StatspackSTATSPACKoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - Transparent Data EncryptionTDEoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - TDE with HSMTDE_HSMoracle-ee\n \n 11.2TrueTrueChange time zoneTimezoneAfrica/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTCDYNAMICUTCTrueSpecifies the timezone the user wants to change the system time toTIME_ZONEoracle-ee\n \n 11.2Oracle XMLDB RepositoryXMLDBoracle-ee\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n', + 'all': '\n \n \n \n 11.2XMLDBOracle Application Express Runtime EnvironmentAPEXoracle-ee\n \n 11.2APEXOracle Application Express Development EnvironmentAPEX-DEVoracle-ee\n \n 11.2Oracle Advanced Security - Native Network EncryptionNATIVE_NETWORK_ENCRYPTIONACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired encryption behaviorSQLNET.ENCRYPTION_SERVERACCEPTED,REJECTED,REQUESTED,REQUIREDSTATICREQUESTEDTrueSpecifies the desired data integrity behaviorSQLNET.CRYPTO_CHECKSUM_SERVERRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40STATICRC4_256,AES256,AES192,3DES168,RC4_128,AES128,3DES112,RC4_56,DES,RC4_40,DES40TrueSpecifies list of encryption algorithms in order of intended useSQLNET.ENCRYPTION_TYPES_SERVERSHA1,MD5STATICSHA1,MD5TrueSpecifies list of checksumming algorithms in order of intended useSQLNET.CRYPTO_CHECKSUM_TYPES_SERVERoracle-ee\n \n 11.21158TrueOracle Enterprise Manager (Database Control only)OEMoracle-ee\n \n 11.2Oracle StatspackSTATSPACKoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - Transparent Data EncryptionTDEoracle-ee\n \n 11.2TrueTrueOracle Advanced Security - TDE with HSMTDE_HSMoracle-ee\n \n 11.2TrueTrueChange time zoneTimezoneAfrica/Cairo,Africa/Casablanca,Africa/Harare,Africa/Monrovia,Africa/Nairobi,Africa/Tripoli,Africa/Windhoek,America/Araguaina,America/Asuncion,America/Bogota,America/Caracas,America/Chihuahua,America/Cuiaba,America/Denver,America/Fortaleza,America/Guatemala,America/Halifax,America/Manaus,America/Matamoros,America/Monterrey,America/Montevideo,America/Phoenix,America/Santiago,America/Tijuana,Asia/Amman,Asia/Ashgabat,Asia/Baghdad,Asia/Baku,Asia/Bangkok,Asia/Beirut,Asia/Calcutta,Asia/Damascus,Asia/Dhaka,Asia/Irkutsk,Asia/Jerusalem,Asia/Kabul,Asia/Karachi,Asia/Kathmandu,Asia/Krasnoyarsk,Asia/Magadan,Asia/Muscat,Asia/Novosibirsk,Asia/Riyadh,Asia/Seoul,Asia/Shanghai,Asia/Singapore,Asia/Taipei,Asia/Tehran,Asia/Tokyo,Asia/Ulaanbaatar,Asia/Vladivostok,Asia/Yakutsk,Asia/Yerevan,Atlantic/Azores,Australia/Adelaide,Australia/Brisbane,Australia/Darwin,Australia/Hobart,Australia/Perth,Australia/Sydney,Brazil/East,Canada/Newfoundland,Canada/Saskatchewan,Europe/Amsterdam,Europe/Athens,Europe/Dublin,Europe/Helsinki,Europe/Istanbul,Europe/Kaliningrad,Europe/Moscow,Europe/Paris,Europe/Prague,Europe/Sarajevo,Pacific/Auckland,Pacific/Fiji,Pacific/Guam,Pacific/Honolulu,Pacific/Samoa,US/Alaska,US/Central,US/Eastern,US/East-Indiana,US/Pacific,UTCDYNAMICUTCTrueSpecifies the timezone the user wants to change the system time toTIME_ZONEoracle-ee\n \n 11.2Oracle XMLDB RepositoryXMLDBoracle-ee\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n'}, + 'sqlserver-ee': {'10.50': '\n \n \n \n 10.50SQLServer Database MirroringMirroringsqlserver-ee\n \n 10.50TrueSQL Server - Transparent Data EncryptionTDEsqlserver-ee\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n', + '11.00': '\n \n \n \n 11.00SQLServer Database MirroringMirroringsqlserver-ee\n \n 11.00TrueSQL Server - Transparent Data EncryptionTDEsqlserver-ee\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n', + 'all': '\n \n \n \n 10.50SQLServer Database MirroringMirroringsqlserver-ee\n \n 10.50TrueSQL Server - Transparent Data EncryptionTDEsqlserver-ee\n \n 11.00SQLServer Database MirroringMirroringsqlserver-ee\n \n 11.00TrueSQL Server - Transparent Data EncryptionTDEsqlserver-ee\n \n \n \n \n 457f7bb8-9fbf-11e4-9084-5754f80d5144\n \n'}} + if engine_name not in default_option_group_options: raise RDSClientError('InvalidParameterValue', 'Invalid DB engine: {0}'.format(engine_name)) if major_engine_version and major_engine_version not in default_option_group_options[engine_name]: @@ -620,6 +784,60 @@ class RDS2Backend(BaseBackend): self.option_groups[option_group_name].add_options(options_to_include) return self.option_groups[option_group_name] + def create_db_parameter_group(self, db_parameter_group_kwargs): + db_parameter_group_id = db_parameter_group_kwargs['name'] + if db_parameter_group_kwargs['name'] in self.db_parameter_groups: + raise RDSClientError('DBParameterGroupAlreadyExistsFault', + 'A DB parameter group named {0} already exists.'.format(db_parameter_group_kwargs['name'])) + if not db_parameter_group_kwargs.get('description'): + raise RDSClientError('InvalidParameterValue', + 'The parameter Description must be provided and must not be blank.') + if not db_parameter_group_kwargs.get('family'): + raise RDSClientError('InvalidParameterValue', + 'The parameter DBParameterGroupName must be provided and must not be blank.') + + db_parameter_group = DBParameterGroup(**db_parameter_group_kwargs) + self.db_parameter_groups[db_parameter_group_id] = db_parameter_group + return db_parameter_group + + def describe_db_parameter_groups(self, db_parameter_group_kwargs): + db_parameter_group_list = [] + + if db_parameter_group_kwargs.get('marker'): + marker = db_parameter_group_kwargs['marker'] + else: + marker = 0 + if db_parameter_group_kwargs.get('max_records'): + if db_parameter_group_kwargs['max_records'] < 20 or db_parameter_group_kwargs['max_records'] > 100: + raise RDSClientError('InvalidParameterValue', + 'Invalid value for max records. Must be between 20 and 100') + max_records = db_parameter_group_kwargs['max_records'] + else: + max_records = 100 + + for db_parameter_group_name, db_parameter_group in self.db_parameter_groups.items(): + if not db_parameter_group_kwargs.get('name') or db_parameter_group.name == db_parameter_group_kwargs.get('name'): + db_parameter_group_list.append(db_parameter_group) + else: + continue + + return db_parameter_group_list[marker:max_records+marker] + + def modify_db_parameter_group(self, db_parameter_group_name, db_parameter_group_parameters): + if db_parameter_group_name not in self.db_parameter_groups: + raise DBParameterGroupNotFoundError(db_parameter_group_name) + + db_parameter_group = self.db_parameter_groups[db_parameter_group_name] + db_parameter_group.update_parameters(db_parameter_group_parameters) + + return db_parameter_group + + def delete_db_parameter_group(self, db_parameter_group_name): + if db_parameter_group_name in self.db_parameter_groups: + return self.db_parameter_groups.pop(db_parameter_group_name) + else: + raise DBParameterGroupNotFoundError(db_parameter_group_name) + def list_tags_for_resource(self, arn): if self.arn_regex.match(arn): arn_breakdown = arn.split(':') @@ -635,19 +853,19 @@ class RDS2Backend(BaseBackend): if resource_name in self.option_groups: return self.option_groups[resource_name].get_tags() elif resource_type == 'pg': # Parameter Group - # TODO: Complete call to tags on resource type Parameter Group - return [] + if resource_name in self.db_parameter_groups: + return self.db_parameter_groups[resource_name].get_tags() elif resource_type == 'ri': # Reserved DB instance # TODO: Complete call to tags on resource type Reserved DB instance return [] elif resource_type == 'secgrp': # DB security group - if resource_type in self.security_groups: + if resource_name in self.security_groups: return self.security_groups[resource_name].get_tags() elif resource_type == 'snapshot': # DB Snapshot # TODO: Complete call to tags on resource type DB Snapshot return [] elif resource_type == 'subgrp': # DB subnet group - if resource_type in self.subnet_groups: + if resource_name in self.subnet_groups: return self.subnet_groups[resource_name].get_tags() else: raise RDSClientError('InvalidParameterValue', @@ -672,16 +890,16 @@ class RDS2Backend(BaseBackend): elif resource_type == 'ri': # Reserved DB instance return None elif resource_type == 'secgrp': # DB security group - if resource_type in self.security_groups: + if resource_name in self.security_groups: return self.security_groups[resource_name].remove_tags(tag_keys) elif resource_type == 'snapshot': # DB Snapshot return None elif resource_type == 'subgrp': # DB subnet group - if resource_type in self.subnet_groups: + if resource_name in self.subnet_groups: return self.subnet_groups[resource_name].remove_tags(tag_keys) else: raise RDSClientError('InvalidParameterValue', - 'Invalid resource name: {}'.format(arn)) + 'Invalid resource name: {0}'.format(arn)) def add_tags_to_resource(self, arn, tags): if self.arn_regex.match(arn): @@ -701,16 +919,16 @@ class RDS2Backend(BaseBackend): elif resource_type == 'ri': # Reserved DB instance return [] elif resource_type == 'secgrp': # DB security group - if resource_type in self.security_groups: + if resource_name in self.security_groups: return self.security_groups[resource_name].add_tags(tags) elif resource_type == 'snapshot': # DB Snapshot return [] elif resource_type == 'subgrp': # DB subnet group - if resource_type in self.subnet_groups: + if resource_name in self.subnet_groups: return self.subnet_groups[resource_name].add_tags(tags) else: raise RDSClientError('InvalidParameterValue', - 'Invalid resource name: {}'.format(arn)) + 'Invalid resource name: {0}'.format(arn)) class OptionGroup(object): @@ -736,6 +954,17 @@ class OptionGroup(object): }""") return template.render(option_group=self) + def to_xml(self): + template = Template(""" + {{ option_group.name }} + {{ option_group.vpc_and_non_vpc_instance_memberships }} + {{ option_group.major_engine_version }} + {{ option_group.engine_name }} + {{ option_group.description }} + + """) + return template.render(option_group=self) + def remove_options(self, options_to_remove): # TODO: Check for option in self.options and remove if exists. Raise error otherwise return @@ -758,11 +987,20 @@ class OptionGroup(object): class OptionGroupOption(object): - def __init__(self, engine_name, major_engine_version): - self.engine_name = engine_name - self.major_engine_version = major_engine_version - #TODO: Create validation for Options - #TODO: formulate way to store options settings + def __init__(self, **kwargs): + self.default_port = kwargs.get('default_port') + self.description = kwargs.get('description') + self.engine_name = kwargs.get('engine_name') + self.major_engine_version = kwargs.get('major_engine_version') + self.name = kwargs.get('name') + self.option_group_option_settings = self._make_option_group_option_settings(kwargs.get('option_group_option_settings', [])) + self.options_depended_on = kwargs.get('options_depended_on', []) + self.permanent = kwargs.get('permanent') + self.persistent = kwargs.get('persistent') + self.port_required = kwargs.get('port_required') + + def _make_option_group_option_settings(self, option_group_option_settings_kwargs): + return [OptionGroupOptionSetting(**setting_kwargs) for setting_kwargs in option_group_option_settings_kwargs] def to_json(self): template = Template("""{ "MinimumRequiredMinorEngineVersion": @@ -780,7 +1018,109 @@ class OptionGroupOption(object): }""") return template.render(option_group=self) + def to_xml(self): + template = Template(""" + {{ option_group.major_engine_version }} + {{ option_group.default_port }} + {{ option_group.port_required }} + {{ option_group.persistent }} + + {%- for option_name in option_group.options_depended_on -%} + {{ option_name }} + {%- endfor -%} + + {{ option_group.permanent }} + {{ option_group.description }} + {{ option_group.name }} + + {%- for setting in option_group.option_group_option_settings -%} + {{ setting.to_xml() }} + {%- endfor -%} + + {{ option_group.engine_name }} + {{ option_group.minimum_required_minor_engine_version }} +""") + return template.render(option_group=self) -rds2_backends = {} -for region in boto.rds2.regions(): - rds2_backends[region.name] = RDS2Backend() + +class OptionGroupOptionSetting(object): + def __init__(self, *kwargs): + self.allowed_values = kwargs.get('allowed_values') + self.apply_type = kwargs.get('apply_type') + self.default_value = kwargs.get('default_value') + self.is_modifiable = kwargs.get('is_modifiable') + self.setting_description = kwargs.get('setting_description') + self.setting_name = kwargs.get('setting_name') + + def to_xml(self): + template = Template(""" + {{ option_group_option_setting.allowed_values }} + {{ option_group_option_setting.apply_type }} + {{ option_group_option_setting.default_value }} + {{ option_group_option_setting.is_modifiable }} + {{ option_group_option_setting.setting_description }} + {{ option_group_option_setting.setting_name }} +""") + return template.render(option_group_option_setting=self) + +class DBParameterGroup(object): + def __init__(self, name, description, family, tags): + self.name = name + self.description = description + self.family = family + self.tags = tags + self.parameters = defaultdict(dict) + + def to_xml(self): + template = Template(""" + {{ param_group.name }} + {{ param_group.family }} + {{ param_group.description }} + """) + return template.render(param_group=self) + + def get_tags(self): + return self.tags + + def add_tags(self, tags): + new_keys = [tag_set['Key'] for tag_set in tags] + self.tags = [tag_set for tag_set in self.tags if tag_set['Key'] not in new_keys] + self.tags.extend(tags) + return self.tags + + def remove_tags(self, tag_keys): + self.tags = [tag_set for tag_set in self.tags if tag_set['Key'] not in tag_keys] + + def update_parameters(self, new_parameters): + for new_parameter in new_parameters: + parameter = self.parameters[new_parameter['ParameterName']] + parameter.update(new_parameter) + + def delete(self, region_name): + backend = rds2_backends[region_name] + backend.delete_db_parameter_group(self.name) + + @classmethod + def create_from_cloudformation_json(cls, resource_name, cloudformation_json, region_name): + properties = cloudformation_json['Properties'] + + db_parameter_group_kwargs = { + 'description': properties['Description'], + 'family': properties['Family'], + 'name': resource_name.lower(), + 'tags': properties.get("Tags"), + } + db_parameter_group_parameters = [] + for db_parameter, db_parameter_value in properties.get('Parameters', {}).items(): + db_parameter_group_parameters.append({ + 'ParameterName': db_parameter, + 'ParameterValue': db_parameter_value, + }) + + rds2_backend = rds2_backends[region_name] + db_parameter_group = rds2_backend.create_db_parameter_group(db_parameter_group_kwargs) + db_parameter_group.update_parameters(db_parameter_group_parameters) + return db_parameter_group + + +rds2_backends = dict((region.name, RDS2Backend(region.name)) for region in boto.rds2.regions()) diff --git a/moto/rds2/responses.py b/moto/rds2/responses.py index bd51f6ea0..879edbdd3 100644 --- a/moto/rds2/responses.py +++ b/moto/rds2/responses.py @@ -1,8 +1,10 @@ from __future__ import unicode_literals +from collections import defaultdict from moto.core.responses import BaseResponse from moto.ec2.models import ec2_backends from .models import rds2_backends +from .exceptions import DBParameterGroupNotFoundError import json import re @@ -22,11 +24,12 @@ class RDS2Response(BaseResponse): "db_instance_class": self._get_param('DBInstanceClass'), "db_instance_identifier": self._get_param('DBInstanceIdentifier'), "db_name": self._get_param("DBName"), - # DBParameterGroupName + "db_parameter_group_name": self._get_param("DBParameterGroupName"), "db_subnet_group_name": self._get_param("DBSubnetGroupName"), "engine": self._get_param("Engine"), "engine_version": self._get_param("EngineVersion"), "iops": self._get_int_param("Iops"), + "kms_key_id": self._get_param("KmsKeyId"), "master_user_password": self._get_param('MasterUserPassword'), "master_username": self._get_param('MasterUsername'), "multi_az": self._get_bool_param("MultiAZ"), @@ -36,12 +39,13 @@ class RDS2Response(BaseResponse): # PreferredMaintenanceWindow "publicly_accessible": self._get_param("PubliclyAccessible"), "region": self.region, - "security_groups": self._get_multi_param('DBSecurityGroups.member'), + "security_groups": self._get_multi_param('DBSecurityGroups.DBSecurityGroupName'), + "storage_encrypted": self._get_param("StorageEncrypted"), "storage_type": self._get_param("StorageType"), # VpcSecurityGroupIds.member.N - "tags": list() + "tags": list(), } - args['tags'] = self.unpack_complex_list_params('Tags.member', ('Key', 'Value')) + args['tags'] = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')) return args def _get_db_replica_kwargs(self): @@ -67,6 +71,14 @@ class RDS2Response(BaseResponse): 'name': self._get_param('OptionGroupName') } + def _get_db_parameter_group_kwargs(self): + return { + 'description': self._get_param('Description'), + 'family': self._get_param('DBParameterGroupFamily'), + 'name': self._get_param('DBParameterGroupName'), + 'tags': self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')), + } + def unpack_complex_list_params(self, label, names): unpacked_list = list() count = 1 @@ -150,7 +162,7 @@ class RDS2Response(BaseResponse): def add_tags_to_resource(self): arn = self._get_param('ResourceName') - tags = self.unpack_complex_list_params('Tags.member', ('Key', 'Value')) + tags = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')) tags = self.backend.add_tags_to_resource(arn, tags) template = self.response_template(ADD_TAGS_TO_RESOURCE_TEMPLATE) return template.render(tags=tags) @@ -168,7 +180,8 @@ class RDS2Response(BaseResponse): def create_db_security_group(self): group_name = self._get_param('DBSecurityGroupName') description = self._get_param('DBSecurityGroupDescription') - security_group = self.backend.create_security_group(group_name, description) + tags = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')) + security_group = self.backend.create_security_group(group_name, description, tags) template = self.response_template(CREATE_SECURITY_GROUP_TEMPLATE) return template.render(security_group=security_group) @@ -206,9 +219,10 @@ class RDS2Response(BaseResponse): def create_db_subnet_group(self): subnet_name = self._get_param('DBSubnetGroupName') description = self._get_param('DBSubnetGroupDescription') - subnet_ids = self._get_multi_param('SubnetIds.member') + subnet_ids = self._get_multi_param('SubnetIds.SubnetIdentifier') + tags = self.unpack_complex_list_params('Tags.Tag', ('Key', 'Value')) 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) + subnet_group = self.backend.create_subnet_group(subnet_name, description, subnets, tags) template = self.response_template(CREATE_SUBNET_GROUP_TEMPLATE) return template.render(subnet_group=subnet_group) @@ -283,225 +297,326 @@ class RDS2Response(BaseResponse): template = self.response_template(MODIFY_OPTION_GROUP_TEMPLATE) return template.render(option_group=option_group) + def create_dbparameter_group(self): + return self.create_db_parameter_group() -CREATE_DATABASE_TEMPLATE = """{ - "CreateDBInstanceResponse": { - "CreateDBInstanceResult": { - "DBInstance": {{ database.to_json() }} - }, - "ResponseMetadata": { "RequestId": "523e3218-afc7-11c3-90f5-f90431260ab4" } - } -}""" + def create_db_parameter_group(self): + kwargs = self._get_db_parameter_group_kwargs() + db_parameter_group = self.backend.create_db_parameter_group(kwargs) + template = self.response_template(CREATE_DB_PARAMETER_GROUP_TEMPLATE) + return template.render(db_parameter_group=db_parameter_group) -CREATE_DATABASE_REPLICA_TEMPLATE = """{"CreateDBInstanceReadReplicaResponse": { - "ResponseMetadata": { - "RequestId": "5e60c46d-a844-11e4-bb68-17f36418e58f" - }, - "CreateDBInstanceReadReplicaResult": { - "DBInstance": {{ database.to_json() }} - } -}}""" + def describe_dbparameter_groups(self): + return self.describe_db_parameter_groups() -DESCRIBE_DATABASES_TEMPLATE = """{ - "DescribeDBInstancesResponse": { - "DescribeDBInstancesResult": { - "DBInstances": [ - {%- for database in databases -%} - {%- if loop.index != 1 -%},{%- endif -%} - {{ database.to_json() }} + def describe_db_parameter_groups(self): + kwargs = self._get_db_parameter_group_kwargs() + kwargs['max_records'] = self._get_param('MaxRecords') + kwargs['marker'] = self._get_param('Marker') + db_parameter_groups = self.backend.describe_db_parameter_groups(kwargs) + template = self.response_template(DESCRIBE_DB_PARAMETER_GROUPS_TEMPLATE) + return template.render(db_parameter_groups=db_parameter_groups) + + def modify_dbparameter_group(self): + return self.modify_db_parameter_group() + + def modify_db_parameter_group(self): + db_parameter_group_name = self._get_param('DBParameterGroupName') + db_parameter_group_parameters = self._get_db_parameter_group_paramters() + db_parameter_group = self.backend.modify_db_parameter_group(db_parameter_group_name, + db_parameter_group_parameters) + template = self.response_template(MODIFY_DB_PARAMETER_GROUP_TEMPLATE) + return template.render(db_parameter_group=db_parameter_group) + + def _get_db_parameter_group_paramters(self): + parameter_group_parameters = defaultdict(dict) + for param_name, value in self.querystring.items(): + if not param_name.startswith('Parameters.Parameter'): + continue + + split_param_name = param_name.split('.') + param_id = split_param_name[2] + param_setting = split_param_name[3] + + parameter_group_parameters[param_id][param_setting] = value[0] + + return parameter_group_parameters.values() + + def describe_dbparameters(self): + return self.describe_db_parameters() + + def describe_db_parameters(self): + db_parameter_group_name = self._get_param('DBParameterGroupName') + db_parameter_groups = self.backend.describe_db_parameter_groups({'name': db_parameter_group_name}) + if not db_parameter_groups: + raise DBParameterGroupNotFoundError(db_parameter_group_name) + + template = self.response_template(DESCRIBE_DB_PARAMETERS_TEMPLATE) + return template.render(db_parameter_group=db_parameter_groups[0]) + + def delete_dbparameter_group(self): + return self.delete_db_parameter_group() + + def delete_db_parameter_group(self): + kwargs = self._get_db_parameter_group_kwargs() + db_parameter_group = self.backend.delete_db_parameter_group(kwargs['name']) + template = self.response_template(DELETE_DB_PARAMETER_GROUP_TEMPLATE) + return template.render(db_parameter_group=db_parameter_group) + + +CREATE_DATABASE_TEMPLATE = """ + + {{ database.to_xml() }} + + + 523e3218-afc7-11c3-90f5-f90431260ab4 + +""" + +CREATE_DATABASE_REPLICA_TEMPLATE = """ + + {{ database.to_xml() }} + + + 5e60c46d-a844-11e4-bb68-17f36418e58f + +""" + +DESCRIBE_DATABASES_TEMPLATE = """ + + + {%- for database in databases -%} + {{ database.to_xml() }} + {%- endfor -%} + + + + 523e3218-afc7-11c3-90f5-f90431260ab4 + +""" + +MODIFY_DATABASE_TEMPLATE = """ + + {{ database.to_xml() }} + + + bb58476c-a1a8-11e4-99cf-55e92d4bbada + +""" + +REBOOT_DATABASE_TEMPLATE = """ + + {{ database.to_xml() }} + + + d55711cb-a1ab-11e4-99cf-55e92d4bbada + +""" + + +DELETE_DATABASE_TEMPLATE = """ + + {{ database.to_xml() }} + + + 7369556f-b70d-11c3-faca-6ba18376ea1b + +""" + +CREATE_SECURITY_GROUP_TEMPLATE = """ + + {{ security_group.to_xml() }} + + + 462165d0-a77a-11e4-a5fa-75b30c556f97 + +""" + +DESCRIBE_SECURITY_GROUPS_TEMPLATE = """ + + + {% for security_group in security_groups %} + {{ security_group.to_xml() }} + {% endfor %} + + + + 5df2014e-a779-11e4-bdb0-594def064d0c + +""" + +DELETE_SECURITY_GROUP_TEMPLATE = """ + + 97e846bd-a77d-11e4-ac58-91351c0f3426 + +""" + +AUTHORIZE_SECURITY_GROUP_TEMPLATE = """ + + {{ security_group.to_xml() }} + + + 75d32fd5-a77e-11e4-8892-b10432f7a87d + +""" + +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 = """ + + 13785dd5-a7fc-11e4-bb9c-7f371d0859b0 + +""" + +CREATE_OPTION_GROUP_TEMPLATE = """ + + {{ option_group.to_xml() }} + + + 1e38dad4-9f50-11e4-87ea-a31c60ed2e36 + +""" + +DELETE_OPTION_GROUP_TEMPLATE = """ + + e2590367-9fa2-11e4-99cf-55e92d41c60e + +""" + +DESCRIBE_OPTION_GROUP_TEMPLATE = """ + + + {%- for option_group in option_groups -%} + {{ option_group.to_xml() }} + {%- endfor -%} + + + + 4caf445d-9fbc-11e4-87ea-a31c60ed2e36 + +""" + +DESCRIBE_OPTION_GROUP_OPTIONS_TEMPLATE = """ + + + {%- for option_group_option in option_group_options -%} + {{ option_group_option.to_xml() }} + {%- endfor -%} + + + + 457f7bb8-9fbf-11e4-9084-5754f80d5144 + +""" + +MODIFY_OPTION_GROUP_TEMPLATE = """ + + {{ option_group.to_xml() }} + + + ce9284a5-a0de-11e4-b984-a11a53e1f328 + +""" + +CREATE_DB_PARAMETER_GROUP_TEMPLATE = """ + + {{ db_parameter_group.to_xml() }} + + + 7805c127-af22-11c3-96ac-6999cc5f7e72 + +""" + +DESCRIBE_DB_PARAMETER_GROUPS_TEMPLATE = """ + + + {%- for db_parameter_group in db_parameter_groups -%} + {{ db_parameter_group.to_xml() }} + {%- endfor -%} + + + + b75d527a-b98c-11d3-f272-7cd6cce12cc5 + +""" + +MODIFY_DB_PARAMETER_GROUP_TEMPLATE = """ + + {{ db_parameter_group.name }} + + + 12d7435e-bba0-11d3-fe11-33d33a9bb7e3 + +""" + +DELETE_DB_PARAMETER_GROUP_TEMPLATE = """ + + cad6c267-ba25-11d3-fe11-33d33a9bb7e3 + +""" + +DESCRIBE_DB_PARAMETERS_TEMPLATE = """ + + + {%- for db_parameter_name, db_parameter in db_parameter_group.parameters.items() -%} + + {%- for parameter_name, parameter_value in db_parameter.items() -%} + <{{ parameter_name }}>{{ parameter_value }} {%- endfor -%} - ] - }, - "ResponseMetadata": { "RequestId": "523e3218-afc7-11c3-90f5-f90431260ab4" } - } -}""" + + {%- endfor -%} + + + + 8c40488f-b9ff-11d3-a15e-7ac49293f4fa + + +""" -MODIFY_DATABASE_TEMPLATE = """{"ModifyDBInstanceResponse": { - "ModifyDBInstanceResult": { - "DBInstance": {{ database.to_json() }}, - "ResponseMetadata": { - "RequestId": "bb58476c-a1a8-11e4-99cf-55e92d4bbada" - } - } - } -}""" +LIST_TAGS_FOR_RESOURCE_TEMPLATE = """ + + + {%- for tag in tags -%} + + {{ tag['Key'] }} + {{ tag['Value'] }} + + {%- endfor -%} + + + + 8c21ba39-a598-11e4-b688-194eaf8658fa + +""" -REBOOT_DATABASE_TEMPLATE = """{"RebootDBInstanceResponse": { - "RebootDBInstanceResult": { - "DBInstance": {{ database.to_json() }}, - "ResponseMetadata": { - "RequestId": "d55711cb-a1ab-11e4-99cf-55e92d4bbada" - } - } - } -}""" +ADD_TAGS_TO_RESOURCE_TEMPLATE = """ + + b194d9ca-a664-11e4-b688-194eaf8658fa + +""" - -DELETE_DATABASE_TEMPLATE = """{ "DeleteDBInstanceResponse": { - "DeleteDBInstanceResult": { - "DBInstance": {{ database.to_json() }} - }, - "ResponseMetadata": { - "RequestId": "523e3218-afc7-11c3-90f5-f90431260ab4" - } - } -}""" - -CREATE_SECURITY_GROUP_TEMPLATE = """{"CreateDBSecurityGroupResponse": { - "CreateDBSecurityGroupResult": { - "DBSecurityGroup": - {{ security_group.to_json() }}, - "ResponseMetadata": { - "RequestId": "462165d0-a77a-11e4-a5fa-75b30c556f97" - }} - } -}""" - -DESCRIBE_SECURITY_GROUPS_TEMPLATE = """{ - "DescribeDBSecurityGroupsResponse": { - "ResponseMetadata": { - "RequestId": "5df2014e-a779-11e4-bdb0-594def064d0c" - }, - "DescribeDBSecurityGroupsResult": { - "Marker": "null", - "DBSecurityGroups": [ - {% for security_group in security_groups %} - {%- if loop.index != 1 -%},{%- endif -%} - {{ security_group.to_json() }} - {% endfor %} - ] - } - } -}""" - -DELETE_SECURITY_GROUP_TEMPLATE = """{"DeleteDBSecurityGroupResponse": { - "ResponseMetadata": { - "RequestId": "97e846bd-a77d-11e4-ac58-91351c0f3426" - } -}}""" - -AUTHORIZE_SECURITY_GROUP_TEMPLATE = """{ - "AuthorizeDBSecurityGroupIngressResponse": { - "AuthorizeDBSecurityGroupIngressResult": { - "DBSecurityGroup": {{ security_group.to_json() }} - }, - "ResponseMetadata": { - "RequestId": "75d32fd5-a77e-11e4-8892-b10432f7a87d" - } - } -}""" - -CREATE_SUBNET_GROUP_TEMPLATE = """{ - "CreateDBSubnetGroupResponse": { - "CreateDBSubnetGroupResult": - { {{ subnet_group.to_json() }} }, - "ResponseMetadata": { "RequestId": "3a401b3f-bb9e-11d3-f4c6-37db295f7674" } - } -}""" - -DESCRIBE_SUBNET_GROUPS_TEMPLATE = """{ - "DescribeDBSubnetGroupsResponse": { - "DescribeDBSubnetGroupsResult": { - "DBSubnetGroups": [ - {% for subnet_group in subnet_groups %} - { {{ subnet_group.to_json() }} }{%- if not loop.last -%},{%- endif -%} - {% endfor %} - ], - "Marker": null - }, - "ResponseMetadata": { "RequestId": "b783db3b-b98c-11d3-fbc7-5c0aad74da7c" } - } -}""" - - -DELETE_SUBNET_GROUP_TEMPLATE = """{"DeleteDBSubnetGroupResponse": {"ResponseMetadata": {"RequestId": "13785dd5-a7fc-11e4-bb9c-7f371d0859b0"}}}""" - -CREATE_OPTION_GROUP_TEMPLATE = """{ - "CreateOptionGroupResponse": { - "CreateOptionGroupResult": { - "OptionGroup": {{ option_group.to_json() }} - }, - "ResponseMetadata": { - "RequestId": "1e38dad4-9f50-11e4-87ea-a31c60ed2e36" - } - } -}""" - -DELETE_OPTION_GROUP_TEMPLATE = \ - """{"DeleteOptionGroupResponse": {"ResponseMetadata": {"RequestId": "e2590367-9fa2-11e4-99cf-55e92d41c60e"}}}""" - -DESCRIBE_OPTION_GROUP_TEMPLATE = \ - """{"DescribeOptionGroupsResponse": { - "DescribeOptionGroupsResult": { - "Marker": null, - "OptionGroupsList": [ - {%- for option_group in option_groups -%} - {%- if loop.index != 1 -%},{%- endif -%} - {{ option_group.to_json() }} - {%- endfor -%} - ]}, - "ResponseMetadata": {"RequestId": "4caf445d-9fbc-11e4-87ea-a31c60ed2e36"} - }}""" - -DESCRIBE_OPTION_GROUP_OPTIONS_TEMPLATE = \ - """{"DescribeOptionGroupOptionsResponse": { - "DescribeOptionGroupOptionsResult": { - "Marker": null, - "OptionGroupOptions": [ - {%- for option_group_option in option_group_options -%} - {%- if loop.index != 1 -%},{%- endif -%} - {{ option_group_option.to_json() }} - {%- endfor -%} - ]}, - "ResponseMetadata": {"RequestId": "457f7bb8-9fbf-11e4-9084-5754f80d5144"} - }}""" - -MODIFY_OPTION_GROUP_TEMPLATE = \ - """{"ModifyOptionGroupResponse": { - "ResponseMetadata": { - "RequestId": "ce9284a5-a0de-11e4-b984-a11a53e1f328" - }, - "ModifyOptionGroupResult": - {{ option_group.to_json() }} - } - }""" - -LIST_TAGS_FOR_RESOURCE_TEMPLATE = \ - """{"ListTagsForResourceResponse": - {"ListTagsForResourceResult": - {"TagList": [ - {%- for tag in tags -%} - {%- if loop.index != 1 -%},{%- endif -%} - { - "Key": "{{ tag['Key'] }}", - "Value": "{{ tag['Value'] }}" - } - {%- endfor -%} - ]}, - "ResponseMetadata": { - "RequestId": "8c21ba39-a598-11e4-b688-194eaf8658fa" - } - } - }""" - -ADD_TAGS_TO_RESOURCE_TEMPLATE = \ - """{"ListTagsForResourceResponse": { - "ListTagsForResourceResult": { - "TagList": [ - {%- for tag in tags -%} - {%- if loop.index != 1 -%},{%- endif -%} - { - "Key": "{{ tag['Key'] }}", - "Value": "{{ tag['Value'] }}" - } - {%- endfor -%} - ]}, - "ResponseMetadata": { - "RequestId": "b194d9ca-a664-11e4-b688-194eaf8658fa" - } - } - }""" - -REMOVE_TAGS_FROM_RESOURCE_TEMPLATE = \ - """{"RemoveTagsFromResourceResponse": {"ResponseMetadata": {"RequestId": "c6499a01-a664-11e4-8069-fb454b71a80e"}}} - """ +REMOVE_TAGS_FROM_RESOURCE_TEMPLATE = """ + + b194d9ca-a664-11e4-b688-194eaf8658fa + +""" diff --git a/tests/test_cloudformation/fixtures/rds_mysql_with_db_parameter_group.py b/tests/test_cloudformation/fixtures/rds_mysql_with_db_parameter_group.py new file mode 100644 index 000000000..866197125 --- /dev/null +++ b/tests/test_cloudformation/fixtures/rds_mysql_with_db_parameter_group.py @@ -0,0 +1,201 @@ +from __future__ import unicode_literals + +template = { + "AWSTemplateFormatVersion" : "2010-09-09", + + "Description" : "AWS CloudFormation Sample Template RDS_MySQL_With_Read_Replica: Sample template showing how to create a highly-available, RDS DBInstance with a read replica. **WARNING** This template creates an Amazon Relational Database Service database instance and Amazon CloudWatch alarms. You will be billed for the AWS resources used if you create a stack from this template.", + + "Parameters": { + "DBName": { + "Default": "MyDatabase", + "Description" : "The database name", + "Type": "String", + "MinLength": "1", + "MaxLength": "64", + "AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*", + "ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters." + }, + + "DBInstanceIdentifier": { + "Type": "String" + }, + + "DBUser": { + "NoEcho": "true", + "Description" : "The database admin account username", + "Type": "String", + "MinLength": "1", + "MaxLength": "16", + "AllowedPattern" : "[a-zA-Z][a-zA-Z0-9]*", + "ConstraintDescription" : "must begin with a letter and contain only alphanumeric characters." + }, + + "DBPassword": { + "NoEcho": "true", + "Description" : "The database admin account password", + "Type": "String", + "MinLength": "1", + "MaxLength": "41", + "AllowedPattern" : "[a-zA-Z0-9]+", + "ConstraintDescription" : "must contain only alphanumeric characters." + }, + + "DBAllocatedStorage": { + "Default": "5", + "Description" : "The size of the database (Gb)", + "Type": "Number", + "MinValue": "5", + "MaxValue": "1024", + "ConstraintDescription" : "must be between 5 and 1024Gb." + }, + + "DBInstanceClass": { + "Description" : "The database instance type", + "Type": "String", + "Default": "db.m1.small", + "AllowedValues" : [ "db.t1.micro", "db.m1.small", "db.m1.medium", "db.m1.large", "db.m1.xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge", "db.m3.medium", "db.m3.large", "db.m3.xlarge", "db.m3.2xlarge", "db.r3.large", "db.r3.xlarge", "db.r3.2xlarge", "db.r3.4xlarge", "db.r3.8xlarge", "db.m2.xlarge", "db.m2.2xlarge", "db.m2.4xlarge", "db.cr1.8xlarge"] +, + "ConstraintDescription" : "must select a valid database instance type." + }, + + "EC2SecurityGroup": { + "Description" : "The EC2 security group that contains instances that need access to the database", + "Default": "default", + "Type": "String", + "AllowedPattern" : "[a-zA-Z0-9\\-]+", + "ConstraintDescription" : "must be a valid security group name." + }, + + "MultiAZ" : { + "Description" : "Multi-AZ master database", + "Type" : "String", + "Default" : "false", + "AllowedValues" : [ "true", "false" ], + "ConstraintDescription" : "must be true or false." + } + }, + + "Conditions" : { + "Is-EC2-VPC" : { "Fn::Or" : [ {"Fn::Equals" : [{"Ref" : "AWS::Region"}, "eu-central-1" ]}, + {"Fn::Equals" : [{"Ref" : "AWS::Region"}, "cn-north-1" ]}]}, + "Is-EC2-Classic" : { "Fn::Not" : [{ "Condition" : "Is-EC2-VPC"}]} + }, + + "Resources" : { + "DBParameterGroup": { + "Type": "AWS::RDS::DBParameterGroup", + "Properties" : { + "Description": "DB Parameter Goup", + "Family" : "MySQL5.1", + "Parameters": { + "BACKLOG_QUEUE_LIMIT": "2048" + } + } + }, + + "DBEC2SecurityGroup": { + "Type": "AWS::EC2::SecurityGroup", + "Condition" : "Is-EC2-VPC", + "Properties" : { + "GroupDescription": "Open database for access", + "SecurityGroupIngress" : [{ + "IpProtocol" : "tcp", + "FromPort" : "3306", + "ToPort" : "3306", + "SourceSecurityGroupName" : { "Ref" : "EC2SecurityGroup" } + }] + } + }, + + "DBSecurityGroup": { + "Type": "AWS::RDS::DBSecurityGroup", + "Condition" : "Is-EC2-Classic", + "Properties": { + "DBSecurityGroupIngress": [{ + "EC2SecurityGroupName": { "Ref": "EC2SecurityGroup" } + }], + "GroupDescription": "database access" + } + }, + + "my_vpc": { + "Type" : "AWS::EC2::VPC", + "Properties" : { + "CidrBlock" : "10.0.0.0/16", + } + }, + + "EC2Subnet": { + "Type" : "AWS::EC2::Subnet", + "Condition" : "Is-EC2-VPC", + "Properties" : { + "AvailabilityZone" : "eu-central-1a", + "CidrBlock" : "10.0.1.0/24", + "VpcId" : { "Ref" : "my_vpc" } + } + }, + + "DBSubnet": { + "Type": "AWS::RDS::DBSubnetGroup", + "Condition" : "Is-EC2-VPC", + "Properties": { + "DBSubnetGroupDescription": "my db subnet group", + "SubnetIds" : [ { "Ref": "EC2Subnet" } ], + } + }, + + "MasterDB" : { + "Type" : "AWS::RDS::DBInstance", + "Properties" : { + "DBInstanceIdentifier": { "Ref": "DBInstanceIdentifier" }, + "DBName" : { "Ref" : "DBName" }, + "AllocatedStorage" : { "Ref" : "DBAllocatedStorage" }, + "DBInstanceClass" : { "Ref" : "DBInstanceClass" }, + "Engine" : "MySQL", + "DBSubnetGroupName": {"Fn::If": ["Is-EC2-VPC", { "Ref": "DBSubnet" }, { "Ref": "AWS::NoValue" }]}, + "MasterUsername" : { "Ref" : "DBUser" }, + "MasterUserPassword" : { "Ref" : "DBPassword" }, + "MultiAZ" : { "Ref" : "MultiAZ" }, + "Tags" : [{ "Key" : "Name", "Value" : "Master Database" }], + "VPCSecurityGroups": { "Fn::If" : [ "Is-EC2-VPC", [ { "Fn::GetAtt": [ "DBEC2SecurityGroup", "GroupId" ] } ], { "Ref" : "AWS::NoValue"}]}, + "DBSecurityGroups": { "Fn::If" : [ "Is-EC2-Classic", [ { "Ref": "DBSecurityGroup" } ], { "Ref" : "AWS::NoValue"}]} + }, + "DeletionPolicy" : "Snapshot" + }, + + "ReplicaDB" : { + "Type" : "AWS::RDS::DBInstance", + "Properties" : { + "SourceDBInstanceIdentifier" : { "Ref" : "MasterDB" }, + "DBInstanceClass" : { "Ref" : "DBInstanceClass" }, + "Tags" : [{ "Key" : "Name", "Value" : "Read Replica Database" }] + } + } + }, + + "Outputs" : { + "EC2Platform" : { + "Description" : "Platform in which this stack is deployed", + "Value" : { "Fn::If" : [ "Is-EC2-VPC", "EC2-VPC", "EC2-Classic" ]} + }, + + "MasterJDBCConnectionString": { + "Description" : "JDBC connection string for the master database", + "Value" : { "Fn::Join": [ "", [ "jdbc:mysql://", + { "Fn::GetAtt": [ "MasterDB", "Endpoint.Address" ] }, + ":", + { "Fn::GetAtt": [ "MasterDB", "Endpoint.Port" ] }, + "/", + { "Ref": "DBName" }]]} + }, + "ReplicaJDBCConnectionString": { + "Description" : "JDBC connection string for the replica database", + "Value" : { "Fn::Join": [ "", [ "jdbc:mysql://", + { "Fn::GetAtt": [ "ReplicaDB", "Endpoint.Address" ] }, + ":", + { "Fn::GetAtt": [ "ReplicaDB", "Endpoint.Port" ] }, + "/", + { "Ref": "DBName" }]]} + } + } +} diff --git a/tests/test_cloudformation/fixtures/rds_mysql_with_read_replica.py b/tests/test_cloudformation/fixtures/rds_mysql_with_read_replica.py index b743f46f4..3e5efa04a 100644 --- a/tests/test_cloudformation/fixtures/rds_mysql_with_read_replica.py +++ b/tests/test_cloudformation/fixtures/rds_mysql_with_read_replica.py @@ -82,7 +82,6 @@ template = { }, "Resources" : { - "DBEC2SecurityGroup": { "Type": "AWS::EC2::SecurityGroup", "Condition" : "Is-EC2-VPC", @@ -101,9 +100,9 @@ template = { "Type": "AWS::RDS::DBSecurityGroup", "Condition" : "Is-EC2-Classic", "Properties": { - "DBSecurityGroupIngress": { + "DBSecurityGroupIngress": [{ "EC2SecurityGroupName": { "Ref": "EC2SecurityGroup" } - }, + }], "GroupDescription": "database access" } }, @@ -188,4 +187,4 @@ template = { { "Ref": "DBName" }]]} } } -} \ No newline at end of file +} diff --git a/tests/test_cloudformation/test_cloudformation_stack_integration.py b/tests/test_cloudformation/test_cloudformation_stack_integration.py index 0fb74bef9..ff2cad29e 100644 --- a/tests/test_cloudformation/test_cloudformation_stack_integration.py +++ b/tests/test_cloudformation/test_cloudformation_stack_integration.py @@ -27,6 +27,7 @@ from moto import ( mock_kms, mock_lambda, mock_rds, + mock_rds2, mock_redshift, mock_route53, mock_sns, @@ -36,6 +37,7 @@ from moto import ( from .fixtures import ( ec2_classic_eip, fn_join, + rds_mysql_with_db_parameter_group, rds_mysql_with_read_replica, redshift, route53_ec2_instance_with_public_ip, @@ -693,6 +695,44 @@ def test_vpc_single_instance_in_subnet(): eip_resource = [resource for resource in resources if resource.resource_type == 'AWS::EC2::EIP'][0] eip_resource.physical_resource_id.should.equal(eip.allocation_id) +@mock_cloudformation() +@mock_ec2() +@mock_rds2() +def test_rds_db_parameter_groups(): + ec2_conn = boto.ec2.connect_to_region("us-west-1") + ec2_conn.create_security_group('application', 'Our Application Group') + + template_json = json.dumps(rds_mysql_with_db_parameter_group.template) + conn = boto.cloudformation.connect_to_region("us-west-1") + conn.create_stack( + "test_stack", + template_body=template_json, + parameters=[ + ("DBInstanceIdentifier", "master_db"), + ("DBName", "my_db"), + ("DBUser", "my_user"), + ("DBPassword", "my_password"), + ("DBAllocatedStorage", "20"), + ("DBInstanceClass", "db.m1.medium"), + ("EC2SecurityGroup", "application"), + ("MultiAZ", "true"), + ], + ) + + rds_conn = boto3.client('rds', region_name="us-west-1") + + db_parameter_groups = rds_conn.describe_db_parameter_groups() + len(db_parameter_groups['DBParameterGroups']).should.equal(1) + db_parameter_group_name = db_parameter_groups['DBParameterGroups'][0]['DBParameterGroupName'] + + found_cloudformation_set_parameter = False + for db_parameter in rds_conn.describe_db_parameters(DBParameterGroupName=db_parameter_group_name)['Parameters']: + if db_parameter['ParameterName'] == 'BACKLOG_QUEUE_LIMIT' and db_parameter['ParameterValue'] == '2048': + found_cloudformation_set_parameter = True + + found_cloudformation_set_parameter.should.equal(True) + + @mock_cloudformation() @mock_ec2() diff --git a/tests/test_rds/test_rds.py b/tests/test_rds/test_rds.py index df8ede179..6078b5f6b 100644 --- a/tests/test_rds/test_rds.py +++ b/tests/test_rds/test_rds.py @@ -238,6 +238,32 @@ def test_create_database_replica(): primary = conn.get_all_dbinstances("db-master-1")[0] list(primary.read_replica_dbinstance_identifiers).should.have.length_of(0) +@disable_on_py3() +@mock_rds +def test_create_cross_region_database_replica(): + west_1_conn = boto.rds.connect_to_region("us-west-1") + west_2_conn = boto.rds.connect_to_region("us-west-2") + + primary = west_1_conn.create_dbinstance("db-master-1", 10, 'db.m1.small', 'root', 'hunter2') + + primary_arn = "arn:aws:rds:us-west-1:1234567890:db:db-master-1" + replica = west_2_conn.create_dbinstance_read_replica( + "replica", + primary_arn, + "db.m1.small", + ) + + primary = west_1_conn.get_all_dbinstances("db-master-1")[0] + primary.read_replica_dbinstance_identifiers[0].should.equal("replica") + + replica = west_2_conn.get_all_dbinstances("replica")[0] + replica.instance_class.should.equal("db.m1.small") + + west_2_conn.delete_dbinstance("replica") + + primary = west_1_conn.get_all_dbinstances("db-master-1")[0] + list(primary.read_replica_dbinstance_identifiers).should.have.length_of(0) + @disable_on_py3() @mock_rds diff --git a/tests/test_rds2/test_rds2.py b/tests/test_rds2/test_rds2.py index 7ca48f0aa..4e1c2b73c 100644 --- a/tests/test_rds2/test_rds2.py +++ b/tests/test_rds2/test_rds2.py @@ -2,479 +2,648 @@ from __future__ import unicode_literals import boto.rds2 import boto.vpc -from boto.exception import BotoServerError +from botocore.exceptions import ClientError, ParamValidationError +import boto3 import sure # noqa -from moto import mock_ec2, mock_rds2 +from moto import mock_ec2, mock_kms, mock_rds2 from tests.helpers import disable_on_py3 @disable_on_py3() @mock_rds2 def test_create_database(): - conn = boto.rds2.connect_to_region("us-west-2") - database = conn.create_db_instance(db_instance_identifier='db-master-1', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_security_groups=["my_sg"]) - database['CreateDBInstanceResponse']['CreateDBInstanceResult']['DBInstance']['DBInstanceStatus'].should.equal('available') - database['CreateDBInstanceResponse']['CreateDBInstanceResult']['DBInstance']['DBInstanceIdentifier'].should.equal("db-master-1") - database['CreateDBInstanceResponse']['CreateDBInstanceResult']['DBInstance']['AllocatedStorage'].should.equal('10') - database['CreateDBInstanceResponse']['CreateDBInstanceResult']['DBInstance']['DBInstanceClass'].should.equal("db.m1.small") - database['CreateDBInstanceResponse']['CreateDBInstanceResult']['DBInstance']['MasterUsername'].should.equal("root") - database['CreateDBInstanceResponse']['CreateDBInstanceResult']['DBInstance']['DBSecurityGroups'][0]['DBSecurityGroup']['DBSecurityGroupName'].should.equal('my_sg') + conn = boto3.client('rds', region_name='us-west-2') + database = conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + Engine='postgres', + DBInstanceClass='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=["my_sg"]) + database['DBInstance']['DBInstanceStatus'].should.equal('available') + database['DBInstance']['DBInstanceIdentifier'].should.equal("db-master-1") + database['DBInstance']['AllocatedStorage'].should.equal(10) + database['DBInstance']['DBInstanceClass'].should.equal("db.m1.small") + database['DBInstance']['MasterUsername'].should.equal("root") + database['DBInstance']['DBSecurityGroups'][0]['DBSecurityGroupName'].should.equal('my_sg') @disable_on_py3() @mock_rds2 def test_get_databases(): - conn = boto.rds2.connect_to_region("us-west-2") + conn = boto3.client('rds', region_name='us-west-2') instances = conn.describe_db_instances() - list(instances['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances']).should.have.length_of(0) + list(instances['DBInstances']).should.have.length_of(0) - conn.create_db_instance(db_instance_identifier='db-master-1', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_security_groups=["my_sg"]) - conn.create_db_instance(db_instance_identifier='db-master-2', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_security_groups=["my_sg"]) + conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + DBInstanceClass='postgres', + Engine='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=['my_sg']) + conn.create_db_instance(DBInstanceIdentifier='db-master-2', + AllocatedStorage=10, + DBInstanceClass='postgres', + Engine='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=['my_sg']) instances = conn.describe_db_instances() - list(instances['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances']).should.have.length_of(2) + list(instances['DBInstances']).should.have.length_of(2) - instances = conn.describe_db_instances("db-master-1") - list(instances['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances']).should.have.length_of(1) - instances['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances'][0]['DBInstanceIdentifier'].should.equal("db-master-1") + instances = conn.describe_db_instances(DBInstanceIdentifier="db-master-1") + list(instances['DBInstances']).should.have.length_of(1) + instances['DBInstances'][0]['DBInstanceIdentifier'].should.equal("db-master-1") @disable_on_py3() @mock_rds2 def test_describe_non_existant_database(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.describe_db_instances.when.called_with("not-a-db").should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.describe_db_instances.when.called_with(DBInstanceIdentifier="not-a-db").should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_modify_db_instance(): - conn = boto.rds2.connect_to_region("us-west-2") - database = conn.create_db_instance(db_instance_identifier='db-master-1', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_security_groups=["my_sg"]) - instances = conn.describe_db_instances('db-master-1') - instances['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances'][0]['AllocatedStorage'].should.equal('10') - conn.modify_db_instance(db_instance_identifier='db-master-1', allocated_storage=20, apply_immediately=True) - instances = conn.describe_db_instances('db-master-1') - instances['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances'][0]['AllocatedStorage'].should.equal('20') + conn = boto3.client('rds', region_name='us-west-2') + database = conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + DBInstanceClass='postgres', + Engine='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=['my_sg']) + instances = conn.describe_db_instances(DBInstanceIdentifier='db-master-1') + instances['DBInstances'][0]['AllocatedStorage'].should.equal(10) + conn.modify_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=20, + ApplyImmediately=True) + instances = conn.describe_db_instances(DBInstanceIdentifier='db-master-1') + instances['DBInstances'][0]['AllocatedStorage'].should.equal(20) @disable_on_py3() @mock_rds2 def test_modify_non_existant_database(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.modify_db_instance.when.called_with(db_instance_identifier='not-a-db', - allocated_storage=20, - apply_immediately=True).should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.modify_db_instance.when.called_with(DBInstanceIdentifier='not-a-db', + AllocatedStorage=20, + ApplyImmediately=True).should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_reboot_db_instance(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_db_instance(db_instance_identifier='db-master-1', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_security_groups=["my_sg"]) - database = conn.reboot_db_instance('db-master-1') - database['RebootDBInstanceResponse']['RebootDBInstanceResult']['DBInstance']['DBInstanceIdentifier'].should.equal("db-master-1") + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + DBInstanceClass='postgres', + Engine='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=['my_sg']) + database = conn.reboot_db_instance(DBInstanceIdentifier='db-master-1') + database['DBInstance']['DBInstanceIdentifier'].should.equal("db-master-1") @disable_on_py3() @mock_rds2 def test_reboot_non_existant_database(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.reboot_db_instance.when.called_with("not-a-db").should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.reboot_db_instance.when.called_with(DBInstanceIdentifier="not-a-db").should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_delete_database(): - conn = boto.rds2.connect_to_region("us-west-2") + conn = boto3.client('rds', region_name='us-west-2') instances = conn.describe_db_instances() - list(instances['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances']).should.have.length_of(0) - conn.create_db_instance(db_instance_identifier='db-master-1', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_security_groups=["my_sg"]) + list(instances['DBInstances']).should.have.length_of(0) + conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + DBInstanceClass='postgres', + Engine='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=['my_sg']) instances = conn.describe_db_instances() - list(instances['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances']).should.have.length_of(1) + list(instances['DBInstances']).should.have.length_of(1) - conn.delete_db_instance("db-master-1") + conn.delete_db_instance(DBInstanceIdentifier="db-master-1") instances = conn.describe_db_instances() - list(instances['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances']).should.have.length_of(0) + list(instances['DBInstances']).should.have.length_of(0) @disable_on_py3() @mock_rds2 def test_delete_non_existant_database(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.delete_db_instance.when.called_with("not-a-db").should.throw(BotoServerError) + conn = boto3.client('rds2', region_name="us-west-2") + conn.delete_db_instance.when.called_with(DBInstanceIdentifier="not-a-db").should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_create_option_group(): - conn = boto.rds2.connect_to_region("us-west-2") - option_group = conn.create_option_group('test', 'mysql', '5.6', 'test option group') - option_group['CreateOptionGroupResponse']['CreateOptionGroupResult']['OptionGroup']['OptionGroupName'].should.equal('test') - option_group['CreateOptionGroupResponse']['CreateOptionGroupResult']['OptionGroup']['EngineName'].should.equal('mysql') - option_group['CreateOptionGroupResponse']['CreateOptionGroupResult']['OptionGroup']['OptionGroupDescription'].should.equal('test option group') - option_group['CreateOptionGroupResponse']['CreateOptionGroupResult']['OptionGroup']['MajorEngineVersion'].should.equal('5.6') + conn = boto3.client('rds', region_name='us-west-2') + option_group = conn.create_option_group(OptionGroupName='test', + EngineName='mysql', + MajorEngineVersion='5.6', + OptionGroupDescription='test option group') + option_group['OptionGroup']['OptionGroupName'].should.equal('test') + option_group['OptionGroup']['EngineName'].should.equal('mysql') + option_group['OptionGroup']['OptionGroupDescription'].should.equal('test option group') + option_group['OptionGroup']['MajorEngineVersion'].should.equal('5.6') @disable_on_py3() @mock_rds2 def test_create_option_group_bad_engine_name(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_option_group.when.called_with('test', 'invalid_engine', '5.6', 'test invalid engine').should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_option_group.when.called_with(OptionGroupName='test', + EngineName='invalid_engine', + MajorEngineVersion='5.6', + OptionGroupDescription='test invalid engine').should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_create_option_group_bad_engine_major_version(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_option_group.when.called_with('test', 'mysql', '6.6.6', 'test invalid engine version').should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_option_group.when.called_with(OptionGroupName='test', + EngineName='mysql', + MajorEngineVersion='6.6.6', + OptionGroupDescription='test invalid engine version').should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_create_option_group_empty_description(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_option_group.when.called_with('test', 'mysql', '5.6', '').should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_option_group.when.called_with(OptionGroupName='test', + EngineName='mysql', + MajorEngineVersion='5.6', + OptionGroupDescription='').should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_create_option_group_duplicate(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_option_group('test', 'mysql', '5.6', 'test option group') - conn.create_option_group.when.called_with('test', 'mysql', '5.6', 'foo').should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_option_group(OptionGroupName='test', + EngineName='mysql', + MajorEngineVersion='5.6', + OptionGroupDescription='test option group') + conn.create_option_group.when.called_with(OptionGroupName='test', + EngineName='mysql', + MajorEngineVersion='5.6', + OptionGroupDescription='test option group').should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_describe_option_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_option_group('test', 'mysql', '5.6', 'test option group') - option_groups = conn.describe_option_groups('test') - option_groups['DescribeOptionGroupsResponse']['DescribeOptionGroupsResult']['OptionGroupsList'][0]['OptionGroupName'].should.equal('test') + conn = boto3.client('rds', region_name='us-west-2') + conn.create_option_group(OptionGroupName='test', + EngineName='mysql', + MajorEngineVersion='5.6', + OptionGroupDescription='test option group') + option_groups = conn.describe_option_groups(OptionGroupName='test') + option_groups['OptionGroupsList'][0]['OptionGroupName'].should.equal('test') @disable_on_py3() @mock_rds2 def test_describe_non_existant_option_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.describe_option_groups.when.called_with("not-a-option-group").should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.describe_option_groups.when.called_with(OptionGroupName="not-a-option-group").should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_delete_option_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_option_group('test', 'mysql', '5.6', 'test option group') - option_groups = conn.describe_option_groups('test') - option_groups['DescribeOptionGroupsResponse']['DescribeOptionGroupsResult']['OptionGroupsList'][0]['OptionGroupName'].should.equal('test') - conn.delete_option_group('test') - conn.describe_option_groups.when.called_with('test').should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_option_group(OptionGroupName='test', + EngineName='mysql', + MajorEngineVersion='5.6', + OptionGroupDescription='test option group') + option_groups = conn.describe_option_groups(OptionGroupName='test') + option_groups['OptionGroupsList'][0]['OptionGroupName'].should.equal('test') + conn.delete_option_group(OptionGroupName='test') + conn.describe_option_groups.when.called_with(OptionGroupName='test').should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_delete_non_existant_option_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.delete_option_group.when.called_with('non-existant').should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.delete_option_group.when.called_with(OptionGroupName='non-existant').should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_describe_option_group_options(): - conn = boto.rds2.connect_to_region("us-west-2") - option_group_options = conn.describe_option_group_options('sqlserver-ee') - len(option_group_options['DescribeOptionGroupOptionsResponse']['DescribeOptionGroupOptionsResult']['OptionGroupOptions']).should.equal(4) - option_group_options = conn.describe_option_group_options('sqlserver-ee', '11.00') - len(option_group_options['DescribeOptionGroupOptionsResponse']['DescribeOptionGroupOptionsResult']['OptionGroupOptions']).should.equal(2) - option_group_options = conn.describe_option_group_options('mysql', '5.6') - len(option_group_options['DescribeOptionGroupOptionsResponse']['DescribeOptionGroupOptionsResult']['OptionGroupOptions']).should.equal(1) - conn.describe_option_group_options.when.called_with('non-existent').should.throw(BotoServerError) - conn.describe_option_group_options.when.called_with('mysql', 'non-existent').should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + option_group_options = conn.describe_option_group_options(EngineName='sqlserver-ee') + len(option_group_options['OptionGroupOptions']).should.equal(4) + option_group_options = conn.describe_option_group_options(EngineName='sqlserver-ee', MajorEngineVersion='11.00') + len(option_group_options['OptionGroupOptions']).should.equal(2) + option_group_options = conn.describe_option_group_options(EngineName='mysql', MajorEngineVersion='5.6') + len(option_group_options['OptionGroupOptions']).should.equal(1) + conn.describe_option_group_options.when.called_with(EngineName='non-existent').should.throw(ClientError) + conn.describe_option_group_options.when.called_with(EngineName='mysql', MajorEngineVersion='non-existent').should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_modify_option_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_option_group('test', 'mysql', '5.6', 'test option group') + conn = boto3.client('rds', region_name='us-west-2') + conn.create_option_group(OptionGroupName='test', EngineName='mysql', MajorEngineVersion='5.6', OptionGroupDescription='test option group') # TODO: create option and validate before deleting. # if Someone can tell me how the hell to use this function # to add options to an option_group, I can finish coding this. - result = conn.modify_option_group('test', [], ['MEMCACHED'], True) - result['ModifyOptionGroupResponse']['ModifyOptionGroupResult']['EngineName'].should.equal('mysql') - result['ModifyOptionGroupResponse']['ModifyOptionGroupResult']['Options'].should.equal([]) - result['ModifyOptionGroupResponse']['ModifyOptionGroupResult']['OptionGroupName'].should.equal('test') + result = conn.modify_option_group(OptionGroupName='test', OptionsToInclude=[], OptionsToRemove=['MEMCACHED'], ApplyImmediately=True) + result['OptionGroup']['EngineName'].should.equal('mysql') + result['OptionGroup']['Options'].should.equal([]) + result['OptionGroup']['OptionGroupName'].should.equal('test') @disable_on_py3() @mock_rds2 def test_modify_option_group_no_options(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_option_group('test', 'mysql', '5.6', 'test option group') - conn.modify_option_group.when.called_with('test').should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_option_group(OptionGroupName='test', EngineName='mysql', MajorEngineVersion='5.6', OptionGroupDescription='test option group') + conn.modify_option_group.when.called_with(OptionGroupName='test').should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_modify_non_existant_option_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.modify_option_group.when.called_with('non-existant', [('OptionName', 'Port', 'DBSecurityGroupMemberships', 'VpcSecurityGroupMemberships', 'OptionSettings')]).should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.modify_option_group.when.called_with(OptionGroupName='non-existant', OptionsToInclude=[('OptionName', 'Port', 'DBSecurityGroupMemberships', 'VpcSecurityGroupMemberships', 'OptionSettings')]).should.throw(ParamValidationError) @disable_on_py3() @mock_rds2 def test_delete_non_existant_database(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.delete_db_instance.when.called_with("not-a-db").should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.delete_db_instance.when.called_with(DBInstanceIdentifier="not-a-db").should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_list_tags_invalid_arn(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.list_tags_for_resource.when.called_with('arn:aws:rds:bad-arn').should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.list_tags_for_resource.when.called_with(ResourceName='arn:aws:rds:bad-arn').should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_list_tags_db(): - conn = boto.rds2.connect_to_region("us-west-2") - result = conn.list_tags_for_resource('arn:aws:rds:us-west-2:1234567890:db:foo') - result['ListTagsForResourceResponse']['ListTagsForResourceResult']['TagList'].should.equal([]) - conn.create_db_instance(db_instance_identifier='db-with-tags', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_security_groups=["my_sg"], - tags=[('foo', 'bar'), ('foo1', 'bar1')]) - result = conn.list_tags_for_resource('arn:aws:rds:us-west-2:1234567890:db:db-with-tags') - result['ListTagsForResourceResponse']['ListTagsForResourceResult']['TagList'].should.equal([{'Value': 'bar', - 'Key': 'foo'}, - {'Value': 'bar1', - 'Key': 'foo1'}]) + conn = boto3.client('rds', region_name='us-west-2') + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:db:foo') + result['TagList'].should.equal([]) + conn.create_db_instance(DBInstanceIdentifier='db-with-tags', + AllocatedStorage=10, + DBInstanceClass='postgres', + Engine='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=['my_sg'], + Tags=[ + { + 'Key': 'foo', + 'Value': 'bar', + }, + { + 'Key': 'foo1', + 'Value': 'bar1', + }, + ]) + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:db:db-with-tags') + result['TagList'].should.equal([{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}]) @disable_on_py3() @mock_rds2 def test_add_tags_db(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_db_instance(db_instance_identifier='db-without-tags', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_security_groups=["my_sg"], - tags=[('foo', 'bar'), ('foo1', 'bar1')]) - result = conn.list_tags_for_resource('arn:aws:rds:us-west-2:1234567890:db:db-without-tags') - list(result['ListTagsForResourceResponse']['ListTagsForResourceResult']['TagList']).should.have.length_of(2) - conn.add_tags_to_resource('arn:aws:rds:us-west-2:1234567890:db:db-without-tags', - [('foo', 'fish'), ('foo2', 'bar2')]) - result = conn.list_tags_for_resource('arn:aws:rds:us-west-2:1234567890:db:db-without-tags') - list(result['ListTagsForResourceResponse']['ListTagsForResourceResult']['TagList']).should.have.length_of(3) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_instance(DBInstanceIdentifier='db-without-tags', + AllocatedStorage=10, + DBInstanceClass='postgres', + Engine='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=['my_sg'], + Tags=[ + { + 'Key': 'foo', + 'Value': 'bar', + }, + { + 'Key': 'foo1', + 'Value': 'bar1', + }, + ]) + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:db:db-without-tags') + list(result['TagList']).should.have.length_of(2) + conn.add_tags_to_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:db:db-without-tags', + Tags=[ + { + 'Key': 'foo', + 'Value': 'fish', + }, + { + 'Key': 'foo2', + 'Value': 'bar2', + }, + ]) + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:db:db-without-tags') + list(result['TagList']).should.have.length_of(3) @disable_on_py3() @mock_rds2 def test_remove_tags_db(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_db_instance(db_instance_identifier='db-with-tags', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_security_groups=["my_sg"], - tags=[('foo', 'bar'), ('foo1', 'bar1')]) - result = conn.list_tags_for_resource('arn:aws:rds:us-west-2:1234567890:db:db-with-tags') - len(result['ListTagsForResourceResponse']['ListTagsForResourceResult']['TagList']).should.equal(2) - conn.remove_tags_from_resource('arn:aws:rds:us-west-2:1234567890:db:db-with-tags', ['foo']) - result = conn.list_tags_for_resource('arn:aws:rds:us-west-2:1234567890:db:db-with-tags') - len(result['ListTagsForResourceResponse']['ListTagsForResourceResult']['TagList']).should.equal(1) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_instance(DBInstanceIdentifier='db-with-tags', + AllocatedStorage=10, + DBInstanceClass='postgres', + Engine='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=['my_sg'], + Tags=[ + { + 'Key': 'foo', + 'Value': 'bar', + }, + { + 'Key': 'foo1', + 'Value': 'bar1', + }, + ]) + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:db:db-with-tags') + list(result['TagList']).should.have.length_of(2) + conn.remove_tags_from_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:db:db-with-tags', TagKeys=['foo']) + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:db:db-with-tags') + len(result['TagList']).should.equal(1) @disable_on_py3() @mock_rds2 def test_add_tags_option_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_option_group('test', 'mysql', '5.6', 'test option group') - result = conn.list_tags_for_resource('arn:aws:rds:us-west-2:1234567890:og:test') - list(result['ListTagsForResourceResponse']['ListTagsForResourceResult']['TagList']).should.have.length_of(0) - conn.add_tags_to_resource('arn:aws:rds:us-west-2:1234567890:og:test', - [('foo', 'fish'), ('foo2', 'bar2')]) - result = conn.list_tags_for_resource('arn:aws:rds:us-west-2:1234567890:og:test') - list(result['ListTagsForResourceResponse']['ListTagsForResourceResult']['TagList']).should.have.length_of(2) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_option_group(OptionGroupName='test', + EngineName='mysql', + MajorEngineVersion='5.6', + OptionGroupDescription='test option group') + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:og:test') + list(result['TagList']).should.have.length_of(0) + conn.add_tags_to_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:og:test', + Tags=[ + { + 'Key': 'foo', + 'Value': 'fish', + }, + { + 'Key': 'foo2', + 'Value': 'bar2', + }]) + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:og:test') + list(result['TagList']).should.have.length_of(2) @disable_on_py3() @mock_rds2 def test_remove_tags_option_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_option_group('test', 'mysql', '5.6', 'test option group') - conn.add_tags_to_resource('arn:aws:rds:us-west-2:1234567890:og:test', - [('foo', 'fish'), ('foo2', 'bar2')]) - result = conn.list_tags_for_resource('arn:aws:rds:us-west-2:1234567890:og:test') - list(result['ListTagsForResourceResponse']['ListTagsForResourceResult']['TagList']).should.have.length_of(2) - conn.remove_tags_from_resource('arn:aws:rds:us-west-2:1234567890:og:test', - ['foo']) - result = conn.list_tags_for_resource('arn:aws:rds:us-west-2:1234567890:og:test') - list(result['ListTagsForResourceResponse']['ListTagsForResourceResult']['TagList']).should.have.length_of(1) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_option_group(OptionGroupName='test', + EngineName='mysql', + MajorEngineVersion='5.6', + OptionGroupDescription='test option group') + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:og:test') + conn.add_tags_to_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:og:test', + Tags=[ + { + 'Key': 'foo', + 'Value': 'fish', + }, + { + 'Key': 'foo2', + 'Value': 'bar2', + }]) + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:og:test') + list(result['TagList']).should.have.length_of(2) + conn.remove_tags_from_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:og:test', + TagKeys=['foo']) + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:og:test') + list(result['TagList']).should.have.length_of(1) @disable_on_py3() @mock_rds2 def test_create_database_security_group(): - conn = boto.rds2.connect_to_region("us-west-2") + conn = boto3.client('rds', region_name='us-west-2') - result = conn.create_db_security_group('db_sg', 'DB Security Group') - result['CreateDBSecurityGroupResponse']['CreateDBSecurityGroupResult']['DBSecurityGroup']['DBSecurityGroupName'].should.equal("db_sg") - result['CreateDBSecurityGroupResponse']['CreateDBSecurityGroupResult']['DBSecurityGroup']['DBSecurityGroupDescription'].should.equal("DB Security Group") - result['CreateDBSecurityGroupResponse']['CreateDBSecurityGroupResult']['DBSecurityGroup']['IPRanges'].should.equal([]) + result = conn.create_db_security_group(DBSecurityGroupName='db_sg', DBSecurityGroupDescription='DB Security Group') + result['DBSecurityGroup']['DBSecurityGroupName'].should.equal("db_sg") + result['DBSecurityGroup']['DBSecurityGroupDescription'].should.equal("DB Security Group") + result['DBSecurityGroup']['IPRanges'].should.equal([]) @disable_on_py3() @mock_rds2 def test_get_security_groups(): - conn = boto.rds2.connect_to_region("us-west-2") + conn = boto3.client('rds', region_name='us-west-2') result = conn.describe_db_security_groups() - result['DescribeDBSecurityGroupsResponse']['DescribeDBSecurityGroupsResult']['DBSecurityGroups'].should.have.length_of(0) + result['DBSecurityGroups'].should.have.length_of(0) - conn.create_db_security_group('db_sg1', 'DB Security Group') - conn.create_db_security_group('db_sg2', 'DB Security Group') + conn.create_db_security_group(DBSecurityGroupName='db_sg1', DBSecurityGroupDescription='DB Security Group') + conn.create_db_security_group(DBSecurityGroupName='db_sg2', DBSecurityGroupDescription='DB Security Group') result = conn.describe_db_security_groups() - result['DescribeDBSecurityGroupsResponse']['DescribeDBSecurityGroupsResult']['DBSecurityGroups'].should.have.length_of(2) + result['DBSecurityGroups'].should.have.length_of(2) - result = conn.describe_db_security_groups("db_sg1") - result['DescribeDBSecurityGroupsResponse']['DescribeDBSecurityGroupsResult']['DBSecurityGroups'].should.have.length_of(1) - result['DescribeDBSecurityGroupsResponse']['DescribeDBSecurityGroupsResult']['DBSecurityGroups'][0]['DBSecurityGroupName'].should.equal("db_sg1") + result = conn.describe_db_security_groups(DBSecurityGroupName="db_sg1") + result['DBSecurityGroups'].should.have.length_of(1) + result['DBSecurityGroups'][0]['DBSecurityGroupName'].should.equal("db_sg1") @disable_on_py3() @mock_rds2 def test_get_non_existant_security_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.describe_db_security_groups.when.called_with("not-a-sg").should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.describe_db_security_groups.when.called_with(DBSecurityGroupName="not-a-sg").should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_delete_database_security_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_db_security_group('db_sg', 'DB Security Group') + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_security_group(DBSecurityGroupName='db_sg', DBSecurityGroupDescription='DB Security Group') result = conn.describe_db_security_groups() - result['DescribeDBSecurityGroupsResponse']['DescribeDBSecurityGroupsResult']['DBSecurityGroups'].should.have.length_of(1) + result['DBSecurityGroups'].should.have.length_of(1) - conn.delete_db_security_group("db_sg") + conn.delete_db_security_group(DBSecurityGroupName="db_sg") result = conn.describe_db_security_groups() - result['DescribeDBSecurityGroupsResponse']['DescribeDBSecurityGroupsResult']['DBSecurityGroups'].should.have.length_of(0) + result['DBSecurityGroups'].should.have.length_of(0) @disable_on_py3() @mock_rds2 def test_delete_non_existant_security_group(): - conn = boto.rds2.connect_to_region("us-west-2") - conn.delete_db_security_group.when.called_with("not-a-db").should.throw(BotoServerError) + conn = boto3.client('rds', region_name='us-west-2') + conn.delete_db_security_group.when.called_with(DBSecurityGroupName="not-a-db").should.throw(ClientError) @disable_on_py3() @mock_rds2 def test_security_group_authorize(): - conn = boto.rds2.connect_to_region("us-west-2") - security_group = conn.create_db_security_group('db_sg', 'DB Security Group') - security_group['CreateDBSecurityGroupResponse']['CreateDBSecurityGroupResult']['DBSecurityGroup']['IPRanges'].should.equal([]) + conn = boto3.client('rds', region_name='us-west-2') + security_group = conn.create_db_security_group(DBSecurityGroupName='db_sg', + DBSecurityGroupDescription='DB Security Group') + security_group['DBSecurityGroup']['IPRanges'].should.equal([]) - conn.authorize_db_security_group_ingress(db_security_group_name='db_sg', - cidrip='10.3.2.45/32') + conn.authorize_db_security_group_ingress(DBSecurityGroupName='db_sg', + CIDRIP='10.3.2.45/32') - result = conn.describe_db_security_groups("db_sg") - result['DescribeDBSecurityGroupsResponse']['DescribeDBSecurityGroupsResult']['DBSecurityGroups'][0]['IPRanges'].should.have.length_of(1) - result['DescribeDBSecurityGroupsResponse']['DescribeDBSecurityGroupsResult']['DBSecurityGroups'][0]['IPRanges'].should.equal(['10.3.2.45/32']) + result = conn.describe_db_security_groups(DBSecurityGroupName="db_sg") + result['DBSecurityGroups'][0]['IPRanges'].should.have.length_of(1) + result['DBSecurityGroups'][0]['IPRanges'].should.equal([{'Status': 'authorized', 'CIDRIP': '10.3.2.45/32'}]) - conn.authorize_db_security_group_ingress(db_security_group_name='db_sg', - cidrip='10.3.2.46/32') - result = conn.describe_db_security_groups("db_sg") - result['DescribeDBSecurityGroupsResponse']['DescribeDBSecurityGroupsResult']['DBSecurityGroups'][0]['IPRanges'].should.have.length_of(2) - result['DescribeDBSecurityGroupsResponse']['DescribeDBSecurityGroupsResult']['DBSecurityGroups'][0]['IPRanges'].should.equal(['10.3.2.45/32', '10.3.2.46/32']) + conn.authorize_db_security_group_ingress(DBSecurityGroupName='db_sg', + CIDRIP='10.3.2.46/32') + result = conn.describe_db_security_groups(DBSecurityGroupName="db_sg") + result['DBSecurityGroups'][0]['IPRanges'].should.have.length_of(2) + result['DBSecurityGroups'][0]['IPRanges'].should.equal([ + {'Status': 'authorized', 'CIDRIP': '10.3.2.45/32'}, + {'Status': 'authorized', 'CIDRIP': '10.3.2.46/32'}, + ]) @disable_on_py3() @mock_rds2 def test_add_security_group_to_database(): - conn = boto.rds2.connect_to_region("us-west-2") + conn = boto3.client('rds', region_name='us-west-2') + + conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + DBInstanceClass='postgres', + Engine='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234) - conn.create_db_instance(db_instance_identifier='db-master-1', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2') result = conn.describe_db_instances() - result['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances'][0]['DBSecurityGroups'].should.equal([]) - conn.create_db_security_group('db_sg', 'DB Security Group') - conn.modify_db_instance(db_instance_identifier='db-master-1', - db_security_groups=['db_sg']) + result['DBInstances'][0]['DBSecurityGroups'].should.equal([]) + conn.create_db_security_group(DBSecurityGroupName='db_sg', + DBSecurityGroupDescription='DB Security Group') + conn.modify_db_instance(DBInstanceIdentifier='db-master-1', + DBSecurityGroups=['db_sg']) result = conn.describe_db_instances() - result['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances'][0]['DBSecurityGroups'][0]['DBSecurityGroup']['DBSecurityGroupName'].should.equal('db_sg') + result['DBInstances'][0]['DBSecurityGroups'][0]['DBSecurityGroupName'].should.equal('db_sg') + + +@disable_on_py3() +@mock_rds2 +def test_list_tags_security_group(): + conn = boto3.client('rds', region_name='us-west-2') + result = conn.describe_db_subnet_groups() + result['DBSubnetGroups'].should.have.length_of(0) + + security_group = conn.create_db_security_group(DBSecurityGroupName="db_sg", + DBSecurityGroupDescription='DB Security Group', + Tags=[{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}])['DBSecurityGroup']['DBSecurityGroupName'] + resource = 'arn:aws:rds:us-west-2:1234567890:secgrp:{0}'.format(security_group) + result = conn.list_tags_for_resource(ResourceName=resource) + result['TagList'].should.equal([{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}]) + + +@disable_on_py3() +@mock_rds2 +def test_add_tags_security_group(): + conn = boto3.client('rds', region_name='us-west-2') + result = conn.describe_db_subnet_groups() + result['DBSubnetGroups'].should.have.length_of(0) + + security_group = conn.create_db_security_group(DBSecurityGroupName="db_sg", + DBSecurityGroupDescription='DB Security Group')['DBSecurityGroup']['DBSecurityGroupName'] + + resource = 'arn:aws:rds:us-west-2:1234567890:secgrp:{0}'.format(security_group) + conn.add_tags_to_resource(ResourceName=resource, + Tags=[{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}]) + + result = conn.list_tags_for_resource(ResourceName=resource) + result['TagList'].should.equal([{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}]) + +@disable_on_py3() +@mock_rds2 +def test_remove_tags_security_group(): + conn = boto3.client('rds', region_name='us-west-2') + result = conn.describe_db_subnet_groups() + result['DBSubnetGroups'].should.have.length_of(0) + + security_group = conn.create_db_security_group(DBSecurityGroupName="db_sg", + DBSecurityGroupDescription='DB Security Group', + Tags=[{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}])['DBSecurityGroup']['DBSecurityGroupName'] + + resource = 'arn:aws:rds:us-west-2:1234567890:secgrp:{0}'.format(security_group) + conn.remove_tags_from_resource(ResourceName=resource, TagKeys=['foo']) + + result = conn.list_tags_for_resource(ResourceName=resource) + result['TagList'].should.equal([{'Value': 'bar1', 'Key': 'foo1'}]) @disable_on_py3() @mock_ec2 @mock_rds2 def test_create_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") + vpc_conn = boto3.client('ec2', 'us-west-2') + vpc = vpc_conn.create_vpc(CidrBlock='10.0.0.0/16')['Vpc'] + subnet1 = vpc_conn.create_subnet(VpcId=vpc['VpcId'], CidrBlock='10.1.0.0/24')['Subnet'] + subnet2 = vpc_conn.create_subnet(VpcId=vpc['VpcId'], CidrBlock='10.1.0.0/26')['Subnet'] - subnet_ids = [subnet1.id, subnet2.id] - conn = boto.rds2.connect_to_region("us-west-2") - result = conn.create_db_subnet_group("db_subnet", "my db subnet", subnet_ids) - result['CreateDBSubnetGroupResponse']['CreateDBSubnetGroupResult']['DBSubnetGroup']['DBSubnetGroupName'].should.equal("db_subnet") - result['CreateDBSubnetGroupResponse']['CreateDBSubnetGroupResult']['DBSubnetGroup']['DBSubnetGroupDescription'].should.equal("my db subnet") - subnets = result['CreateDBSubnetGroupResponse']['CreateDBSubnetGroupResult']['DBSubnetGroup']['Subnets'] - subnet_group_ids = [subnets['Subnet'][0]['SubnetIdentifier'], subnets['Subnet'][1]['SubnetIdentifier']] + subnet_ids = [subnet1['SubnetId'], subnet2['SubnetId']] + conn = boto3.client('rds', region_name='us-west-2') + result = conn.create_db_subnet_group(DBSubnetGroupName='db_subnet', + DBSubnetGroupDescription='my db subnet', + SubnetIds=subnet_ids) + result['DBSubnetGroup']['DBSubnetGroupName'].should.equal("db_subnet") + result['DBSubnetGroup']['DBSubnetGroupDescription'].should.equal("my db subnet") + subnets = result['DBSubnetGroup']['Subnets'] + subnet_group_ids = [subnets[0]['SubnetIdentifier'], subnets[1]['SubnetIdentifier']] list(subnet_group_ids).should.equal(subnet_ids) @@ -482,96 +651,370 @@ def test_create_database_subnet_group(): @mock_ec2 @mock_rds2 def test_create_database_in_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") + vpc_conn = boto3.client('ec2', 'us-west-2') + vpc = vpc_conn.create_vpc(CidrBlock='10.0.0.0/16')['Vpc'] + subnet = vpc_conn.create_subnet(VpcId=vpc['VpcId'], CidrBlock='10.1.0.0/24')['Subnet'] - conn = boto.rds2.connect_to_region("us-west-2") - conn.create_db_subnet_group("db_subnet1", "my db subnet", [subnet.id]) - conn.create_db_instance(db_instance_identifier='db-master-1', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_subnet_group_name='db_subnet1') - result = conn.describe_db_instances("db-master-1") - result['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances'][0]['DBSubnetGroup']['DBSubnetGroupName'].should.equal("db_subnet1") + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_subnet_group(DBSubnetGroupName='db_subnet1', + DBSubnetGroupDescription='my db subnet', + SubnetIds=[subnet['SubnetId']]) + conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + Engine='postgres', + DBInstanceClass='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSubnetGroupName='db_subnet1') + result = conn.describe_db_instances(DBInstanceIdentifier='db-master-1') + result['DBInstances'][0]['DBSubnetGroup']['DBSubnetGroupName'].should.equal('db_subnet1') @disable_on_py3() @mock_ec2 @mock_rds2 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") + vpc_conn = boto3.client('ec2', 'us-west-2') + vpc = vpc_conn.create_vpc(CidrBlock='10.0.0.0/16')['Vpc'] + subnet = vpc_conn.create_subnet(VpcId=vpc['VpcId'], CidrBlock='10.1.0.0/24')['Subnet'] - conn = boto.rds2.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]) + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_subnet_group(DBSubnetGroupName="db_subnet1", + DBSubnetGroupDescription='my db subnet', + SubnetIds=[subnet['SubnetId']]) + conn.create_db_subnet_group(DBSubnetGroupName='db_subnet2', + DBSubnetGroupDescription='my db subnet', + SubnetIds=[subnet['SubnetId']]) - resp = conn.describe_db_subnet_groups() - groups_resp = resp['DescribeDBSubnetGroupsResponse'] + resp = conn.describe_db_subnet_groups() + resp['DBSubnetGroups'].should.have.length_of(2) - subnet_groups = groups_resp['DescribeDBSubnetGroupsResult']['DBSubnetGroups'] - subnet_groups.should.have.length_of(2) + subnets = resp['DBSubnetGroups'][0]['Subnets'] + subnets.should.have.length_of(1) - subnets = groups_resp['DescribeDBSubnetGroupsResult']['DBSubnetGroups'][0]['DBSubnetGroup']['Subnets'] - subnets.should.have.length_of(1) + list(conn.describe_db_subnet_groups(DBSubnetGroupName="db_subnet1")['DBSubnetGroups']).should.have.length_of(1) - list(resp).should.have.length_of(1) - list(groups_resp).should.have.length_of(2) - list(conn.describe_db_subnet_groups("db_subnet1")).should.have.length_of(1) - - conn.describe_db_subnet_groups.when.called_with("not-a-subnet").should.throw(BotoServerError) + conn.describe_db_subnet_groups.when.called_with(DBSubnetGroupName="not-a-subnet").should.throw(ClientError) @disable_on_py3() @mock_ec2 @mock_rds2 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") + vpc_conn = boto3.client('ec2', 'us-west-2') + vpc = vpc_conn.create_vpc(CidrBlock='10.0.0.0/16')['Vpc'] + subnet = vpc_conn.create_subnet(VpcId=vpc['VpcId'], CidrBlock='10.1.0.0/24')['Subnet'] - conn = boto.rds2.connect_to_region("us-west-2") + conn = boto3.client('rds', region_name='us-west-2') result = conn.describe_db_subnet_groups() - result['DescribeDBSubnetGroupsResponse']['DescribeDBSubnetGroupsResult']['DBSubnetGroups'].should.have.length_of(0) + result['DBSubnetGroups'].should.have.length_of(0) - conn.create_db_subnet_group("db_subnet1", "my db subnet", [subnet.id]) + conn.create_db_subnet_group(DBSubnetGroupName="db_subnet1", + DBSubnetGroupDescription='my db subnet', + SubnetIds=[subnet['SubnetId']]) result = conn.describe_db_subnet_groups() - result['DescribeDBSubnetGroupsResponse']['DescribeDBSubnetGroupsResult']['DBSubnetGroups'].should.have.length_of(1) + result['DBSubnetGroups'].should.have.length_of(1) - conn.delete_db_subnet_group("db_subnet1") + conn.delete_db_subnet_group(DBSubnetGroupName="db_subnet1") result = conn.describe_db_subnet_groups() - result['DescribeDBSubnetGroupsResponse']['DescribeDBSubnetGroupsResult']['DBSubnetGroups'].should.have.length_of(0) + result['DBSubnetGroups'].should.have.length_of(0) - conn.delete_db_subnet_group.when.called_with("db_subnet1").should.throw(BotoServerError) + conn.delete_db_subnet_group.when.called_with(DBSubnetGroupName="db_subnet1").should.throw(ClientError) + + +@disable_on_py3() +@mock_ec2 +@mock_rds2 +def test_list_tags_database_subnet_group(): + vpc_conn = boto3.client('ec2', 'us-west-2') + vpc = vpc_conn.create_vpc(CidrBlock='10.0.0.0/16')['Vpc'] + subnet = vpc_conn.create_subnet(VpcId=vpc['VpcId'], CidrBlock='10.1.0.0/24')['Subnet'] + + conn = boto3.client('rds', region_name='us-west-2') + result = conn.describe_db_subnet_groups() + result['DBSubnetGroups'].should.have.length_of(0) + + subnet = conn.create_db_subnet_group(DBSubnetGroupName="db_subnet1", + DBSubnetGroupDescription='my db subnet', + SubnetIds=[subnet['SubnetId']], + Tags=[{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}])['DBSubnetGroup']['DBSubnetGroupName'] + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:subgrp:{0}'.format(subnet)) + result['TagList'].should.equal([{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}]) + +@disable_on_py3() +@mock_ec2 +@mock_rds2 +def test_add_tags_database_subnet_group(): + vpc_conn = boto3.client('ec2', 'us-west-2') + vpc = vpc_conn.create_vpc(CidrBlock='10.0.0.0/16')['Vpc'] + subnet = vpc_conn.create_subnet(VpcId=vpc['VpcId'], CidrBlock='10.1.0.0/24')['Subnet'] + + conn = boto3.client('rds', region_name='us-west-2') + result = conn.describe_db_subnet_groups() + result['DBSubnetGroups'].should.have.length_of(0) + + subnet = conn.create_db_subnet_group(DBSubnetGroupName="db_subnet1", + DBSubnetGroupDescription='my db subnet', + SubnetIds=[subnet['SubnetId']], + Tags=[])['DBSubnetGroup']['DBSubnetGroupName'] + resource = 'arn:aws:rds:us-west-2:1234567890:subgrp:{0}'.format(subnet) + + conn.add_tags_to_resource(ResourceName=resource, + Tags=[{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}]) + + result = conn.list_tags_for_resource(ResourceName=resource) + result['TagList'].should.equal([{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}]) + +@disable_on_py3() +@mock_ec2 +@mock_rds2 +def test_remove_tags_database_subnet_group(): + vpc_conn = boto3.client('ec2', 'us-west-2') + vpc = vpc_conn.create_vpc(CidrBlock='10.0.0.0/16')['Vpc'] + subnet = vpc_conn.create_subnet(VpcId=vpc['VpcId'], CidrBlock='10.1.0.0/24')['Subnet'] + + conn = boto3.client('rds', region_name='us-west-2') + result = conn.describe_db_subnet_groups() + result['DBSubnetGroups'].should.have.length_of(0) + + subnet = conn.create_db_subnet_group(DBSubnetGroupName="db_subnet1", + DBSubnetGroupDescription='my db subnet', + SubnetIds=[subnet['SubnetId']], + Tags=[{'Value': 'bar', + 'Key': 'foo'}, + {'Value': 'bar1', + 'Key': 'foo1'}])['DBSubnetGroup']['DBSubnetGroupName'] + resource = 'arn:aws:rds:us-west-2:1234567890:subgrp:{0}'.format(subnet) + + conn.remove_tags_from_resource(ResourceName=resource, TagKeys=['foo']) + + result = conn.list_tags_for_resource(ResourceName=resource) + result['TagList'].should.equal([{'Value': 'bar1', 'Key': 'foo1'}]) @disable_on_py3() @mock_rds2 def test_create_database_replica(): - conn = boto.rds2.connect_to_region("us-west-2") + conn = boto3.client('rds', region_name='us-west-2') - conn.create_db_instance(db_instance_identifier='db-master-1', - allocated_storage=10, - engine='postgres', - db_instance_class='db.m1.small', - master_username='root', - master_user_password='hunter2', - db_security_groups=["my_sg"]) + database = conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + Engine='postgres', + DBInstanceClass='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=["my_sg"]) - replica = conn.create_db_instance_read_replica("db-replica-1", "db-master-1", "db.m1.small") - replica['CreateDBInstanceReadReplicaResponse']['CreateDBInstanceReadReplicaResult']['DBInstance']['ReadReplicaSourceDBInstanceIdentifier'].should.equal('db-master-1') - replica['CreateDBInstanceReadReplicaResponse']['CreateDBInstanceReadReplicaResult']['DBInstance']['DBInstanceClass'].should.equal('db.m1.small') - replica['CreateDBInstanceReadReplicaResponse']['CreateDBInstanceReadReplicaResult']['DBInstance']['DBInstanceIdentifier'].should.equal('db-replica-1') + replica = conn.create_db_instance_read_replica(DBInstanceIdentifier="db-replica-1", + SourceDBInstanceIdentifier="db-master-1", + DBInstanceClass="db.m1.small") + replica['DBInstance']['ReadReplicaSourceDBInstanceIdentifier'].should.equal('db-master-1') + replica['DBInstance']['DBInstanceClass'].should.equal('db.m1.small') + replica['DBInstance']['DBInstanceIdentifier'].should.equal('db-replica-1') - master = conn.describe_db_instances("db-master-1") - master['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances'][0]['ReadReplicaDBInstanceIdentifiers'].should.equal(['db-replica-1']) + master = conn.describe_db_instances(DBInstanceIdentifier="db-master-1") + master['DBInstances'][0]['ReadReplicaDBInstanceIdentifiers'].should.equal(['db-replica-1']) - conn.delete_db_instance("db-replica-1") + conn.delete_db_instance(DBInstanceIdentifier="db-replica-1", SkipFinalSnapshot=True) - master = conn.describe_db_instances("db-master-1") - master['DescribeDBInstancesResponse']['DescribeDBInstancesResult']['DBInstances'][0]['ReadReplicaDBInstanceIdentifiers'].should.equal([]) + master = conn.describe_db_instances(DBInstanceIdentifier="db-master-1") + master['DBInstances'][0]['ReadReplicaDBInstanceIdentifiers'].should.equal([]) + +@disable_on_py3() +@mock_rds2 +@mock_kms +def test_create_database_with_encrypted_storage(): + kms_conn = boto3.client('kms', region_name='us-west-2') + key = kms_conn.create_key(Policy='my RDS encryption policy', + Description='RDS encryption key', + KeyUsage='ENCRYPT_DECRYPT') + + conn = boto3.client('rds', region_name='us-west-2') + database = conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + Engine='postgres', + DBInstanceClass='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234, + DBSecurityGroups=["my_sg"], + StorageEncrypted=True, + KmsKeyId=key['KeyMetadata']['KeyId']) + + database['DBInstance']['StorageEncrypted'].should.equal(True) + database['DBInstance']['KmsKeyId'].should.equal(key['KeyMetadata']['KeyId']) + +@disable_on_py3() +@mock_rds2 +def test_create_db_parameter_group(): + conn = boto3.client('rds', region_name='us-west-2') + db_parameter_group = conn.create_db_parameter_group(DBParameterGroupName='test', + DBParameterGroupFamily='mysql5.6', + Description='test parameter group') + + db_parameter_group['DBParameterGroup']['DBParameterGroupName'].should.equal('test') + db_parameter_group['DBParameterGroup']['DBParameterGroupFamily'].should.equal('mysql5.6') + db_parameter_group['DBParameterGroup']['Description'].should.equal('test parameter group') + +@disable_on_py3() +@mock_rds2 +def test_create_db_instance_with_parameter_group(): + conn = boto3.client('rds', region_name='us-west-2') + db_parameter_group = conn.create_db_parameter_group(DBParameterGroupName='test', + DBParameterGroupFamily='mysql5.6', + Description='test parameter group') + + database = conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + Engine='mysql', + DBInstanceClass='db.m1.small', + DBParameterGroupName='test', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234) + + len(database['DBInstance']['DBParameterGroups']).should.equal(1) + database['DBInstance']['DBParameterGroups'][0]['DBParameterGroupName'].should.equal('test') + database['DBInstance']['DBParameterGroups'][0]['ParameterApplyStatus'].should.equal('in-sync') + +@disable_on_py3() +@mock_rds2 +def test_modify_db_instance_with_parameter_group(): + conn = boto3.client('rds', region_name='us-west-2') + database = conn.create_db_instance(DBInstanceIdentifier='db-master-1', + AllocatedStorage=10, + Engine='mysql', + DBInstanceClass='db.m1.small', + MasterUsername='root', + MasterUserPassword='hunter2', + Port=1234) + + len(database['DBInstance']['DBParameterGroups']).should.equal(1) + database['DBInstance']['DBParameterGroups'][0]['DBParameterGroupName'].should.equal('default.mysql5.6') + database['DBInstance']['DBParameterGroups'][0]['ParameterApplyStatus'].should.equal('in-sync') + + db_parameter_group = conn.create_db_parameter_group(DBParameterGroupName='test', + DBParameterGroupFamily='mysql5.6', + Description='test parameter group') + conn.modify_db_instance(DBInstanceIdentifier='db-master-1', + DBParameterGroupName='test', + ApplyImmediately=True) + + database = conn.describe_db_instances(DBInstanceIdentifier='db-master-1')['DBInstances'][0] + len(database['DBParameterGroups']).should.equal(1) + database['DBParameterGroups'][0]['DBParameterGroupName'].should.equal('test') + database['DBParameterGroups'][0]['ParameterApplyStatus'].should.equal('in-sync') + + +@disable_on_py3() +@mock_rds2 +def test_create_db_parameter_group_empty_description(): + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_parameter_group.when.called_with(DBParameterGroupName='test', + DBParameterGroupFamily='mysql5.6', + Description='').should.throw(ClientError) + + +@disable_on_py3() +@mock_rds2 +def test_create_db_parameter_group_duplicate(): + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_parameter_group(DBParameterGroupName='test', + DBParameterGroupFamily='mysql5.6', + Description='test parameter group') + conn.create_db_parameter_group.when.called_with(DBParameterGroupName='test', + DBParameterGroupFamily='mysql5.6', + Description='test parameter group').should.throw(ClientError) + + +@disable_on_py3() +@mock_rds2 +def test_describe_db_parameter_group(): + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_parameter_group(DBParameterGroupName='test', + DBParameterGroupFamily='mysql5.6', + Description='test parameter group') + db_parameter_groups = conn.describe_db_parameter_groups(DBParameterGroupName='test') + db_parameter_groups['DBParameterGroups'][0]['DBParameterGroupName'].should.equal('test') + + +@disable_on_py3() +@mock_rds2 +def test_describe_non_existant_db_parameter_group(): + conn = boto3.client('rds', region_name='us-west-2') + db_parameter_groups = conn.describe_db_parameter_groups(DBParameterGroupName='test') + len(db_parameter_groups['DBParameterGroups']).should.equal(0) + + +@disable_on_py3() +@mock_rds2 +def test_delete_db_parameter_group(): + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_parameter_group(DBParameterGroupName='test', + DBParameterGroupFamily='mysql5.6', + Description='test parameter group') + db_parameter_groups = conn.describe_db_parameter_groups(DBParameterGroupName='test') + db_parameter_groups['DBParameterGroups'][0]['DBParameterGroupName'].should.equal('test') + conn.delete_db_parameter_group(DBParameterGroupName='test') + db_parameter_groups = conn.describe_db_parameter_groups(DBParameterGroupName='test') + len(db_parameter_groups['DBParameterGroups']).should.equal(0) + +@disable_on_py3() +@mock_rds2 +def test_modify_db_parameter_group(): + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_parameter_group(DBParameterGroupName='test', + DBParameterGroupFamily='mysql5.6', + Description='test parameter group') + + modify_result = conn.modify_db_parameter_group(DBParameterGroupName='test', + Parameters=[{ + 'ParameterName': 'foo', + 'ParameterValue': 'foo_val', + 'Description': 'test param', + 'ApplyMethod': 'immediate' + }] + ) + + modify_result['DBParameterGroupName'].should.equal('test') + + db_parameters = conn.describe_db_parameters(DBParameterGroupName='test') + db_parameters['Parameters'][0]['ParameterName'].should.equal('foo') + db_parameters['Parameters'][0]['ParameterValue'].should.equal('foo_val') + db_parameters['Parameters'][0]['Description'].should.equal('test param') + db_parameters['Parameters'][0]['ApplyMethod'].should.equal('immediate') + + +@disable_on_py3() +@mock_rds2 +def test_delete_non_existant_db_parameter_group(): + conn = boto3.client('rds', region_name='us-west-2') + conn.delete_db_parameter_group.when.called_with(DBParameterGroupName='non-existant').should.throw(ClientError) + +@disable_on_py3() +@mock_rds2 +def test_create_parameter_group_with_tags(): + conn = boto3.client('rds', region_name='us-west-2') + conn.create_db_parameter_group(DBParameterGroupName='test', + DBParameterGroupFamily='mysql5.6', + Description='test parameter group', + Tags=[{ + 'Key': 'foo', + 'Value': 'bar', + }]) + result = conn.list_tags_for_resource(ResourceName='arn:aws:rds:us-west-2:1234567890:pg:test') + result['TagList'].should.equal([{'Value': 'bar', 'Key': 'foo'}])