# # Copyright (c) 2009 RightScale Inc # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # module RightAws class RdsInterface < RightAwsBase include RightAwsBaseInterface API_VERSION = "2009-10-16" DEFAULT_HOST = 'rds.amazonaws.com' DEFAULT_PORT = 443 DEFAULT_PROTOCOL = 'https' DEFAULT_PATH = '/' DEFAULT_INSTANCE_CLASS = 'db.m1.small' INSTANCE_CLASSES = ['db.m1.small', 'db.m1.large', 'db.m1.xlarge', 'db.m2.2xlarge', 'db.m2.4xlarge'] @@bench = AwsBenchmarkingBlock.new def self.bench_xml @@bench.xml end def self.bench_service @@bench.service end # Create a new handle to a RDS account. All handles share the same per process or per thread # HTTP connection to RDS. Each handle is for a specific account. The params have the # following options: # * :endpoint_url a fully qualified url to Amazon API endpoint (this overwrites: :server, :port, :service, :protocol). Example: 'https://rds.amazonaws.com' # * :server: RDS service host, default: DEFAULT_HOST # * :port: RDS service port, default: DEFAULT_PORT # * :protocol: 'http' or 'https', default: DEFAULT_PROTOCOL # * :multi_thread: true=HTTP connection per thread, false=per process # * :logger: for log messages, default: RAILS_DEFAULT_LOGGER else STDOUT # # rds = RightAws::RdsInterface.new('xxxxxxxxxxxxxxxxxxxxx','xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', # {:logger => Logger.new('/tmp/x.log')}) #=> # # def initialize(aws_access_key_id=nil, aws_secret_access_key=nil, params={}) init({ :name => 'RDS', :default_host => ENV['RDS_URL'] ? URI.parse(ENV['RDS_URL']).host : DEFAULT_HOST, :default_port => ENV['RDS_URL'] ? URI.parse(ENV['RDS_URL']).port : DEFAULT_PORT, :default_service => ENV['RDS_URL'] ? URI.parse(ENV['RDS_URL']).path : DEFAULT_PATH, :default_protocol => ENV['RDS_URL'] ? URI.parse(ENV['RDS_URL']).scheme : DEFAULT_PROTOCOL, :default_api_version => ENV['RDS_API_VERSION'] || API_VERSION }, aws_access_key_id || ENV['AWS_ACCESS_KEY_ID'], aws_secret_access_key || ENV['AWS_SECRET_ACCESS_KEY'], params) end #----------------------------------------------------------------- # Requests #----------------------------------------------------------------- # Generates request hash for REST API. def generate_request(action, params={}) #:nodoc: generate_request_impl(:get, action, params ) end # Sends request to Amazon and parses the response. # Raises AwsError if any banana happened. def request_info(request, parser, &block) # :nodoc: request_info_impl(:rds_connection, @@bench, request, parser, &block) end # Incrementally lists something. def incrementally_list_items(action, parser_class, params={}, &block) # :nodoc: params = params.dup params['MaxRecords'] = params.delete(:max_records) if params[:max_records] params['Marker'] = params.delete(:marker) if params[:marker] last_response = nil loop do link = generate_request(action, params) last_response = request_info( link, parser_class.new(:logger => @logger)) params['Marker'] = last_response[:marker] break unless block && block.call(last_response) && !last_response[:marker].blank? end last_response end #----------------------------------------------------------------- # API Calls: #----------------------------------------------------------------- # -------------------------------------------- # DB Instances # -------------------------------------------- # List DB instances. # # Optional params: +:aws_id+, +:max_records+, +:marker+ # # # Get a list of DB instances. The response is an +Array+ of instances. # rds.describe_db_instances #=> # [{:instance_class=>"Medium", # :status=>"creating", # :engine=>"MySQL5.1", # :allocated_storage=>50, # :pending_modified_values=>{}, # :db_parameter_group=>{:status=>"in-sync", :name=>"default.MySQL5.1"}, # :db_security_groups=> # [{:status=>"active", :name=>"kd-2-test"}, # {:status=>"active", :name=>"default"}, # {:status=>"active", :name=>"kd-1-test"}], # :availability_zone=>"us-east-1b", # :master_username=>"username", # :aws_id=>"kd-my-awesome-db-2", # :preferred_maintenance_window=>"Sun:05:00-Sun:09:00"}] # # # Retrieve a custom DB instance. # # The response is an +Array+ with a single instance record. # rds.describe_db_instances("kd-test-n3") # # # Incrementally a list DB instances. Every response part is a +Hash+. # rds.describe_db_instances(:max_records => 30) do |x| # puts x.inspect #=> # {:db_instances=> # [{:instance_class=>"Medium", # :status=>"creating", # :engine=>"MySQL5.1", # :allocated_storage=>50, # :pending_modified_values=>{}, # :db_parameter_group=>{:status=>"in-sync", :name=>"default.MySQL5.1"}, # :db_security_groups=> # [{:status=>"active", :name=>"kd-2-test"}, # {:status=>"active", :name=>"default"}, # {:status=>"active", :name=>"kd-1-test"}], # :availability_zone=>"us-east-1b", # :master_username=>"username", # :aws_id=>"kd-my-awesome-db-2", # :preferred_maintenance_window=>"Sun:05:00-Sun:09:00"}]} # true # end # def describe_db_instances(params={}, &block) item, params = AwsUtils::split_items_and_params(params) params = params.dup params['DBInstanceIdentifier'] = item if item result = [] incrementally_list_items('DescribeDBInstances', DescribeDbInstancesParser, params) do |response| result += response[:db_instances] block ? block.call(response) : true end result end # Create a new RDS instance of the type and size specified by you. The default storage engine for RDS Instances is InnoDB. # # Mandatory arguments: +aws_id+, +master_username+, +master_user_password+ # Optional params: +:allocated_storage+ (25 by def), +:instance_class+, +:engine+ ('MySQL5.1' by def), # +:endpoint_port+, +:db_name+, +:db_security_groups+, +:db_parameter_group+, +:availability_zone+, +:preferred_maintenance_window+ # +:backup_retention_period+, +:preferred_backup_window+ # # ds.create_db_instance('my-awesome-db', 'username', 'password') #=> # {:instance_class=>"Medium", # :status=>"creating", # :engine=>"MySQL5.1", # :allocated_storage=>50, # :pending_modified_values=>{}, # :db_security_groups=> # [{:status=>"active", :name=>"kd-2-test"}, # {:status=>"active", :name=>"default"}, # {:status=>"active", :name=>"kd-1-test"}], # :availability_zone=>"us-east-1b", # :master_username=>"username", # :aws_id=>"kd-my-awesome-db-2", # :preferred_maintenance_window=>"Sun:05:00-Sun:09:00"} # def create_db_instance(aws_id, master_username, master_user_password, params={}) request_hash = {} # Mandatory request_hash['DBInstanceIdentifier'] = aws_id request_hash['MasterUsername'] = master_username request_hash['MasterUserPassword'] = master_user_password # Mandatory with default values request_hash['DBInstanceClass'] = params[:instance_class].blank? ? DEFAULT_INSTANCE_CLASS : params[:instance_class].to_s request_hash['AllocatedStorage'] = params[:allocated_storage].blank? ? 25 : params[:allocated_storage] request_hash['Engine'] = params[:engine].blank? ? 'MySQL5.1' : params[:engine] # Optional request_hash['EndpointPort'] = params[:endpoint_port] unless params[:endpoint_port].blank? request_hash['DBName'] = params[:db_name] unless params[:db_name].blank? request_hash['AvailabilityZone'] = params[:availability_zone] unless params[:availability_zone].blank? request_hash['PreferredMaintenanceWindow'] = params[:preferred_maintenance_window] unless params[:preferred_maintenance_window].blank? request_hash['BackupRetentionPeriod'] = params[:backup_retention_period] unless params[:backup_retention_period].blank? request_hash['PreferredBackupWindow'] = params[:preferred_backup_window] unless params[:preferred_backup_window].blank? request_hash.merge!(amazonize_list('DBSecurityGroups.member', params[:db_security_groups])) # request_hash.merge!(amazonize_list('DBParameterGroups.member', params[:db_parameter_groups])) request_hash['DBParameterGroup'] = params[:db_parameter_group] unless params[:db_parameter_group].blank? link = generate_request('CreateDBInstance', request_hash) request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first end # Modify a DB instance. # # Mandatory arguments: +aws_id+. # Optional params: +:master_user_password+, +:instance_class+, +:db_security_groups+, # +:db_parameter_group+, +:preferred_maintenance_window+, +:allocated_storage+, +:apply_immediately+, # +:backup_retention_period+, +:preferred_backup_window+ # def modify_db_instance(aws_id, params={}) request_hash = {} # Mandatory request_hash['DBInstanceIdentifier'] = aws_id # Optional request_hash['MasterUserPassword'] = params[:master_user_password] unless params[:master_user_password].blank? request_hash['DBInstanceClass'] = params[:instance_class].to_s.capitalize unless params[:instance_class].blank? request_hash['PreferredMaintenanceWindow'] = params[:preferred_maintenance_window] unless params[:preferred_maintenance_window].blank? request_hash['BackupRetentionPeriod'] = params[:backup_retention_period] unless params[:backup_retention_period].blank? request_hash['PreferredBackupWindow'] = params[:preferred_backup_window] unless params[:preferred_backup_window].blank? request_hash['AllocatedStorage'] = params[:allocated_storage] unless params[:allocated_storage].blank? request_hash['ApplyImmediately'] = params[:apply_immediately].to_s unless params[:apply_immediately].blank? request_hash.merge!(amazonize_list('DBSecurityGroups.member', params[:db_security_groups])) # request_hash.merge!(amazonize_list('DBParameterGroups.member', params[:db_parameter_groups])) request_hash['DBParameterGroupName'] = params[:db_parameter_group] unless params[:db_parameter_group].blank? link = generate_request('ModifyDBInstance', request_hash) request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first end # Reboot Db instance. # # rds.reboot_db_instance('kd-my-awesome-db') #=> # {:status=>"rebooting", # :pending_modified_values=>{}, # :allocated_storage=>42, # :master_username=>"kd", # :db_security_groups=>[], # :instance_class=>"Medium", # :availability_zone=>"us-east-1a", # :aws_id=>"kd-my-awesome-db", # :create_time=>"2009-08-28T08:34:21.858Z", # :engine=>"MySQL5.1", # :preferred_maintenance_window=>"Sun:05:00-Sun:09:00"} # def reboot_db_instance(aws_id, params={}) params = params.dup params['DBInstanceIdentifier'] = aws_id link = generate_request('RebootDBInstance', params) request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first end # Delete a DB instance # # Mandatory arguments: aws_id # Optional params: :skip_final_snapshot ('false' by def), # :snapshot_aws_id ('{instance_aws_id}-final-snapshot-YYYYMMDDHHMMSS') # # rds.delete_db_instance('my-awesome-db-g2') #=> true # def delete_db_instance(aws_id, params={}) request_hash = {} request_hash['DBInstanceIdentifier'] = aws_id request_hash['SkipFinalSnapshot'] = params.has_key?(:skip_final_snapshot) ? params[:skip_final_snapshot].to_s : 'false' if request_hash['SkipFinalSnapshot'] == 'false' && params[:snapshot_aws_id].blank? params = params.dup params[:snapshot_aws_id] = "#{aws_id}-final-snapshot-#{Time.now.utc.strftime('%Y%m%d%H%M%S')}" end request_hash['FinalDBSnapshotIdentifier'] = params[:snapshot_aws_id] unless params[:snapshot_aws_id].blank? link = generate_request('DeleteDBInstance', request_hash) request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first end # -------------------------------------------- # DB SecurityGroups # -------------------------------------------- # # rds.describe_db_security_groups #=> # [{:owner_id=>"82...25", # :description=>"Default", # :ec2_security_groups=>[], # :ip_ranges=>[], # :name=>"Default"}, # {:owner_id=>"82...25", # :description=>"kd", # :ec2_security_groups=>[], # :ip_ranges=>[], # :name=>"kd2"}, # {:owner_id=>"82...25", # :description=>"kd", # :ec2_security_groups=> # [{:status=>"Authorized", :owner_id=>"82...23", :name=>"default"}, # {:status=>"Authorized", :owner_id=>"82...24", :name=>"default1"}, # {:status=>"Authorized", :owner_id=>"82...25", :name=>"default"}, # {:status=>"Authorized", :owner_id=>"82...26", :name=>"default"}, # {:status=>"Authorized", :owner_id=>"82...26", :name=>"default1"}, # {:status=>"Authorized", :owner_id=>"82...29", :name=>"default22"}], # :ip_ranges=> # [{:status=>"Authorized", :cidrip=>"127.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"128.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"129.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"130.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"131.0.0.1/8"}], # :name=>"kd3"}] # # # get a custom group # rds.describe_db_security_groups('kd3') # def describe_db_security_groups(*db_security_group_name, &block) item, params = AwsUtils::split_items_and_params(db_security_group_name) params['DBSecurityGroupName'] = item if item result = [] incrementally_list_items('DescribeDBSecurityGroups', DescribeDbSecurityGroupsParser, params) do |response| result += response[:db_security_groups] block ? block.call(response) : true end result end # Create a database security group so that ingress to an RDS Instance can be controlled. # A new security group cannot have the same name as an existing group. # # ds.create_db_security_group('kd3', 'kd') #=> # {:ec2_security_groups=>[], # :description=>"kd", # :ip_ranges=>[], # :name=>"kd3", # :owner_id=>"82...25"} # def create_db_security_group(db_security_group_name, db_security_group_description) link = generate_request('CreateDBSecurityGroup', 'DBSecurityGroupName' => db_security_group_name, 'DBSecurityGroupDescription' => db_security_group_description) request_info(link, DescribeDbSecurityGroupsParser.new(:logger => @logger))[:db_security_groups].first end def modify_db_security_group_ingress(action, db_security_group_name, params={}) # :nodoc: request_hash = { 'DBSecurityGroupName' => db_security_group_name} request_hash['CIDRIP'] = params[:cidrip] unless params[:cidrip].blank? request_hash['EC2SecurityGroupName'] = params[:ec2_security_group_name] unless params[:ec2_security_group_name].blank? request_hash['EC2SecurityGroupOwnerId'] = params[:ec2_security_group_owner] unless params[:ec2_security_group_owner].blank? link = generate_request(action, request_hash) request_info(link, DescribeDbSecurityGroupsParser.new(:logger => @logger))[:db_security_groups].first end # Authorize an ingress. Params: +:cidrip+ or (+:ec2_security_group_name+ and +:ec2_security_group_owner+) # # rds.authorize_db_security_group_ingress('kd3', :cidrip => '131.0.0.1/8') # {:owner_id=>"82...25", # :ec2_security_groups=>[], # :description=>"kd", # :ip_ranges=> # [{:status=>"Authorized", :cidrip=>"127.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"128.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"129.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"130.0.0.1/8"}, # {:status=>"Authorizing", :cidrip=>"131.0.0.1/8"}], # :name=>"kd3"} # # rds.authorize_db_security_group_ingress('kd3',:ec2_security_group_owner => '82...27', # :ec2_security_group_name => 'default') #=> # {:owner_id=>"82...25", # :ec2_security_groups=> # [{:status=>"Authorized", :owner_id=>"82...25", :name=>"g1"}, # {:status=>"Authorized", :owner_id=>"82...26", :name=>"g2"}, # {:status=>"Authorizing", :owner_id=>"82...27", :name=>"default"}], # :ip_ranges=> # [{:status=>"Authorized", :cidrip=>"127.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"128.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"129.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"130.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"131.0.0.1/8"}], # :name=>"kd3"} # def authorize_db_security_group_ingress(db_security_group_name, params={}) modify_db_security_group_ingress('AuthorizeDBSecurityGroupIngress', db_security_group_name, params) end # Revoke an ingress. # Optional params: +:cidrip+ or (+:ec2_security_group_name+ and +:ec2_security_group_owner+) # # rds.revoke_db_security_group_ingress('kd3', :ec2_security_group_owner => '82...25', # :ec2_security_group_name => 'default') #=> # {:owner_id=>"82...25", # :ec2_security_groups=> # [{:status=>"Revoking", :owner_id=>"826693181925", :name=>"default"}], # :name=>"kd3", # :description=>"kd", # :ip_ranges=> # [{:status=>"Authorized", :cidrip=>"127.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"128.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"129.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"130.0.0.1/8"}, # {:status=>"Authorized", :cidrip=>"131.0.0.1/8"}]} # def revoke_db_security_group_ingress(db_security_group_name, params={}) modify_db_security_group_ingress('RevokeDBSecurityGroupIngress', db_security_group_name, params) end # Delete a database security group. Database security group must not be associated with any # RDS Instances. # # rds.delete_db_security_group('kd3') #=> true # def delete_db_security_group(db_security_group_name) link = generate_request('DeleteDBSecurityGroup', 'DBSecurityGroupName' => db_security_group_name) request_info(link, RightHttp2xxParser.new(:logger => @logger)) end # -------------------------------------------- # DB ParameterGroups # -------------------------------------------- # Describe DBParameterGroups. # # rds.describe_db_parameter_groups #=> # [{:engine=>"MySQL5.1", # :description=>"Default parameter group for MySQL5.1", # :name=>"default.MySQL5.1"}] # # # List parameter groups by 20 # rds.describe_db_parameter_groups(:max_records=>20) do |response| # puts response.inspect # true # end # def describe_db_parameter_groups(*db_parameter_group_name, &block) item, params = AwsUtils::split_items_and_params(db_parameter_group_name) params['DBParameterGroupName'] = item if item result = [] incrementally_list_items('DescribeDBParameterGroups', DescribeDbParameterGroupsParser, params) do |response| result += response[:db_parameter_groups] block ? block.call(response) : true end result end # Creates a database parameter group so that configuration of an RDS Instance can be controlled. # # rds.create_db_parameter_group('my-new-group-1','My new group') #=> {} # # TODO: this call returns an empty hash, but should be a parameter group data - ask Amazon guys. # def create_db_parameter_group(db_parameter_group_name, db_parameter_group_description, engine='MySQL5.1', params={}) params['DBParameterGroupName'] = db_parameter_group_name params['Description'] = db_parameter_group_description params['Engine'] = engine link = generate_request('CreateDBParameterGroup', params ) request_info(link, DescribeDbParameterGroupsParser.new(:logger => @logger))[:db_parameter_groups].first end # Modify DBParameterGroup paramaters. Up to 20 params can be midified at once. # # rds.modify_db_parameter_group('kd1', 'max_allowed_packet' => 2048) #=> true # # rds.modify_db_parameter_group('kd1', 'max_allowed_packet' => {:value => 2048, :method => 'immediate') #=> true # def modify_db_parameter_group(db_parameter_group_name, params={}) # :nodoc: request_hash = { 'DBParameterGroupName' => db_parameter_group_name} parameters = [] params.each do |key, value| method = 'pending-reboot' if value.is_a?(Hash) method = value[:method] unless value[:method].blank? value = value[:value] end parameters << [key, value, method] end request_hash.merge!( amazonize_list(['Parameters.member.?.ParameterName', 'Parameters.member.?.ParameterValue', 'Parameters.member.?.ApplyMethod'], parameters )) link = generate_request('ModifyDBParameterGroup', request_hash) request_info(link, RightHttp2xxParser.new(:logger => @logger)) end # Delete DBParameter Group. # # rds.delete_db_parameter_group('kd1') #=> true # def delete_db_parameter_group(db_parameter_group_name) link = generate_request('DeleteDBParameterGroup', 'DBParameterGroupName' => db_parameter_group_name) request_info(link, RightHttp2xxParser.new(:logger => @logger)) end # Modify the parameters of a DBParameterGroup to the engine/system default value. # # # Reset all parameters # rds.reset_db_parameter_group('kd2', :all ) #=> true # # # Reset custom parameters # rds.reset_db_parameter_group('kd2', 'max_allowed_packet', 'auto_increment_increment' ) #=> true # rds.reset_db_parameter_group('kd2', 'max_allowed_packet', 'auto_increment_increment' => 'immediate' ) #=> true # def reset_db_parameter_group(db_parameter_group_name, *params) params = params.flatten request_hash = { 'DBParameterGroupName' => db_parameter_group_name } if params.first.to_s == 'all' request_hash['ResetAllParameters'] = true else tmp = [] params.each{ |item| tmp |= item.to_a } params = [] tmp.each do |key, method| method = 'pending-reboot' unless method params << [key, method] end request_hash.merge!( amazonize_list(['Parameters.member.?.ParameterName', 'Parameters.member.?.ApplyMethod'], params )) end link = generate_request('ResetDBParameterGroup', request_hash) request_info(link, RightHttp2xxParser.new(:logger => @logger)) end # Get the detailed parameters list for a particular DBParameterGroup. # # rds.describe_db_parameters('kd1') #=> # [{:is_modifiable=>true, # :apply_type=>"static", # :source=>"engine-default", # :allowed_values=>"ON,OFF", # :description=>"Controls whether user-defined functions that have only an xxx symbol for the main function can be loaded", # :name=>"allow-suspicious-udfs", # :data_type=>"boolean"}, # {:is_modifiable=>true, # :apply_type=>"dynamic", # :source=>"engine-default", # :allowed_values=>"1-65535", # :description=>"Intended for use with master-to-master replication, and can be used to control the operation of AUTO_INCREMENT columns", # :name=>"auto_increment_increment", # :data_type=>"integer"}, ... ] # # # List parameters by 20 # rds.describe_db_parameters('kd1', :max_records=>20) do |response| # puts response.inspect # true # end # def describe_db_parameters(*db_parameter_group_name, &block) item, params = AwsUtils::split_items_and_params(db_parameter_group_name) params['DBParameterGroupName'] = item result = [] incrementally_list_items('DescribeDBParameters', DescribeDbParametersParser, params) do |response| result += response[:parameters] block ? block.call(response) : true end result end # Describe a default parameters for the engine. # # rds.describe_engine_default_parameters('MySQL5.1') #=> # [{:is_modifiable=>true, # :apply_type=>"static", # :source=>"engine-default", # :allowed_values=>"ON,OFF", # :description=>"Controls whether user-defined functions that have only an xxx symbol for the main function can be loaded", # :name=>"allow-suspicious-udfs", # :data_type=>"boolean"}, # {:is_modifiable=>true, # :apply_type=>"dynamic", # :source=>"engine-default", # :allowed_values=>"1-65535", # :description=>"Intended for use with master-to-master replication, and can be used to control the operation of AUTO_INCREMENT columns", # :name=>"auto_increment_increment", # :data_type=>"integer"}, ... ] # def describe_engine_default_parameters(*engine, &block) engine = ['MySQL5.1'] if engine.blank? item, params = AwsUtils::split_items_and_params(engine) params['Engine'] = item if item result = [] incrementally_list_items('DescribeEngineDefaultParameters', DescribeDbParametersParser, params) do |response| result += response[:parameters] block ? block.call(response) : true end result end # -------------------------------------------- # DB Snapshots # -------------------------------------------- # Get DBSecurityGroup details for a particular customer or for a particular DBSecurityGroup if a name is specified. # Optional params: +:instance_aws_id+ # # # all snapshots # rds.describe_db_snapshots #=> # [{:status=>"Available", # :instance_aws_id=>"kd-test-n1", # :allocated_storage=>25, # :availability_zone=>"us-east-1b", # :aws_id=>"kd-test-n1-final-snapshot-at-20090630131215", # :engine=>"MySQL5.1", # :endpoint_port=>3306, # :instance_create_time=>"2009-06-30T12:48:15.590Z", # :master_username=>"payless", # :snapshot_time=>"2009-06-30T13:16:48.496Z"}, ...] # # # all snapshots for a custom instance # rds.describe_db_snapshots(:instance_aws_id => 'kd-test-n3') #=> # [{:status=>"Available", # :instance_aws_id=>"kd-test-n3", # :allocated_storage=>25, # :availability_zone=>"us-east-1a", # :aws_id=>"kd-test-n3-final-snapshot-20090713074916", # :engine=>"MySQL5.1", # :endpoint_port=>3306, # :instance_create_time=>"2009-06-30T12:51:32.540Z", # :master_username=>"payless", # :snapshot_time=>"2009-07-13T07:52:35.542Z"}] # # # a snapshot by id # rds.describe_db_snapshots('my-awesome-db-final-snapshot-20090713075554') #=> # [{:status=>"Available", # :allocated_storage=>25, # :engine=>"MySQL5.1", # :instance_aws_id=>"my-awesome-db", # :availability_zone=>"us-east-1a", # :instance_create_time=>"2009-07-13T07:53:08.912Z", # :endpoint_port=>3306, # :master_username=>"medium", # :aws_id=>"my-awesome-db-final-snapshot-20090713075554", # :snapshot_time=>"2009-07-13T07:59:17.537Z"}] # def describe_db_snapshots(params={}, &block) item, params = AwsUtils::split_items_and_params(params) params['DBSnapshotIdentifier'] = item if item params['DBInstanceIdentifier'] = params.delete(:instance_aws_id) unless params[:instance_aws_id].blank? result = [] incrementally_list_items('DescribeDBSnapshots', DescribeDbSnapshotsParser, params) do |response| result += response[:db_snapshots] block ? block.call(response) : true end result end # Create a DBSnapshot. The source DBInstance must be in Available state # # rds.create_db_snapshot('remove-me-tomorrow-2', 'my-awesome-db-g7' ) #=> # {:status=>"PendingCreation", # :allocated_storage=>50, # :availability_zone=>"us-east-1b", # :engine=>"MySQL5.1", # :aws_id=>"remove-me-tomorrow-2", # :instance_create_time=>"2009-07-13T09:35:39.243Z", # :endpoint_port=>3306, # :instance_aws_id=>"my-awesome-db-g7", # :db_master_username=>"username"} # def create_db_snapshot(aws_id, instance_aws_id) link = generate_request('CreateDBSnapshot', 'DBSnapshotIdentifier' => aws_id, 'DBInstanceIdentifier' => instance_aws_id) request_info(link, DescribeDbSnapshotsParser.new(:logger => @logger))[:db_snapshots].first end # Create a new RDS instance from a DBSnapshot. The source DBSnapshot must be # in the "Available" state. The new RDS instance is created with the Default security group. # # Optional params: +:instance_class+, +:endpoint_port+, +:availability_zone+ # # rds.restore_db_instance_from_db_snapshot('ahahahaha-final-snapshot-20090828081159', 'q1') #=> # {:status=>"creating", # :pending_modified_values=>{}, # :allocated_storage=>42, # :db_security_groups=>[], # :master_username=>"kd", # :availability_zone=>"us-east-1a", # :aws_id=>"q1", # :create_time=>"2009-08-29T18:07:01.510Z", # :instance_class=>"Medium", # :preferred_maintenance_window=>"Sun:05:00-Sun:09:00", # :engine=>"MySQL5.1"} # def restore_db_instance_from_db_snapshot(snapshot_aws_id, instance_aws_id, params={}) request_hash = { 'DBSnapshotIdentifier' => snapshot_aws_id, 'DBInstanceIdentifier' => instance_aws_id } request_hash['DBInstanceClass'] = params[:instance_class] unless params[:instance_class].blank? request_hash['EndpointPort'] = params[:endpoint_port] unless params[:endpoint_port].blank? request_hash['AvailabilityZone'] = params[:availability_zone] unless params[:availability_zone].blank? link = generate_request('RestoreDBInstanceFromDBSnapshot', request_hash) request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first end # Create a new RDS instance from a point-in-time system snapshot. The target # database is created from the source database restore point with the same configuration as # the original source database, except that the new RDS instance is created with the default # security group. def restore_db_instance_to_point_in_time(instance_aws_id, new_instance_aws_id, restore_time) request_hash = { 'SourceDBInstanceIdentifier' => instance_aws_id, 'TargetDBInstanceIdentifier' => new_instance_aws_id, 'RestoreTime' => restore_time} link = generate_request('RestoreDBInstanceToPointInTime', request_hash) request_info(link, DescribeDbInstancesParser.new(:logger => @logger))[:db_instances].first end # Delete a DBSnapshot. The DBSnapshot must be in the Available state to be deleted. # # rds.delete_db_snapshot('remove-me-tomorrow-1') #=> # {:status=>"Deleted", # :allocated_storage=>50, # :instance_create_time=>"2009-07-13T09:27:01.053Z", # :availability_zone=>"us-east-1a", # :db_master_username=>"username", # :aws_id=>"remove-me-tomorrow-1", # :snapshot_time=>"2009-07-13T10:59:30.227Z", # :endpoint_port=>3306, # :instance_aws_id=>"my-awesome-db-g5", # :engine=>"MySQL5.1"} # def delete_db_snapshot(aws_id) link = generate_request('DeleteDBSnapshot', 'DBSnapshotIdentifier' => aws_id) request_info(link, DescribeDbSnapshotsParser.new(:logger => @logger))[:db_snapshots].first end # -------------------------------------------- # DB Events # -------------------------------------------- # Get events related to RDS instances and DBSecurityGroups for the past 14 days. # Optional params: +:duration+, +:start_time+, +:end_time+, +:aws_id+, # +:source_type+('db-instance', 'db-security-group', 'db-snapshot', 'db-parameter-group') # # # get all enevts # rds.describe_events #=> # [{:aws_id=>"my-awesome-db-g4", # :source_type=>"DBInstance", # :message=>"Started user snapshot for database instance:my-awesome-db-g4", # :date=>"2009-07-13T10:54:13.661Z"}, # {:aws_id=>"my-awesome-db-g5", # :source_type=>"DBInstance", # :message=>"Started user snapshot for database instance:my-awesome-db-g5", # :date=>"2009-07-13T10:55:13.674Z"}, # {:aws_id=>"my-awesome-db-g7", # :source_type=>"DBInstance", # :message=>"Started user snapshot for database instance:my-awesome-db-g7", # :date=>"2009-07-13T10:56:34.226Z"}] # # # get all events since yesterday # rds.describe_events(:start_date => 1.day.ago) # # # get last 60 min events # rds.describe_events(:duration => 60) # def describe_events(params={}, &block) params = params.dup params['SourceIdentifier'] = params.delete(:aws_id) unless params[:aws_id].blank? params['SourceType'] = params.delete(:source_type) unless params[:source_type].blank? params['Duration'] = params.delete(:duration) unless params[:duration].blank? params['StartDate'] = fix_date(params.delete(:start_date)) unless params[:start_date].blank? params['EndDate'] = fix_date(params.delete(:end_date)) unless params[:end_date].blank? result = [] incrementally_list_items('DescribeEvents', DescribeEventsParser, params) do |response| result += response[:events] block ? block.call(response) : true end result end def fix_date(date) # :nodoc: date = Time.at(date) if date.is_a?(Fixnum) date = date.utc.strftime('%Y-%m-%dT%H:%M:%SZ') if date.is_a?(Time) date end # -------------------------------------------- # Parsers # -------------------------------------------- # -------------------------------------------- # DB Instances # -------------------------------------------- class DescribeDbInstancesParser < RightAWSParser # :nodoc: def reset @m = [ 'DBInstance', 'CreateDBInstanceResult', 'DeleteDBInstanceResult', 'ModifyDBInstanceResult', 'RebootDBInstanceResult', 'RestoreDBInstanceToPointInTimeResponse', 'RestoreDBInstanceFromDBSnapshotResult' ] @result = { :db_instances => [] } end def tagstart(name, attributes) case name when *@m then @db_instance = { :db_security_groups => [], :pending_modified_values => {} } when 'DBSecurityGroup' then @db_security_group = {} when 'DBParameterGroup', 'DBParameterGroupStatus' then @db_parameter_group = {} end end def tagend(name) case name when 'Marker' then @result[:marker] = @text when 'MaxRecords' then @result[:max_records] = @text.to_i when 'DBInstanceIdentifier' then @db_instance[:aws_id] = @text when 'DBName' then @db_instance[:name] = @text # ? is this one used? when 'InstanceCreateTime' then @db_instance[:create_time] = @text when 'Engine' then @db_instance[:engine] = @text when 'DBInstanceStatus' then @db_instance[:status] = @text when 'AllocatedStorage' then @db_instance[:allocated_storage] = @text.to_i when 'Port' then @db_instance[:endpoint_port] = @text.to_i when 'Address' then @db_instance[:endpoint_address] = @text when 'MasterUsername' then @db_instance[:master_username] = @text when 'AvailabilityZone' then @db_instance[:availability_zone] = @text when 'PreferredMaintenanceWindow' then @db_instance[:preferred_maintenance_window] = @text when 'BackupRetentionPeriod' then @db_instance[:backup_retention_period] = @text when 'PreferredBackupWindow' then @db_instance[:preferred_backup_window] = @text when 'DBInstanceClass' case @xmlpath when /PendingModifiedValues$/ then @db_instance[:pending_modified_values][:instance_class] = @text else @db_instance[:instance_class] = @text end when 'MasterUserPassword' then @db_instance[:pending_modified_values][:master_user_password] = @text when 'DBSecurityGroupName' then @db_security_group[:name] = @text when 'Status' then @db_security_group[:status] = @text when 'DBParameterGroupName' then @db_parameter_group[:name] = @text when 'ParameterApplyStatus' then @db_parameter_group[:status] = @text when 'DBSecurityGroup' then @db_instance[:db_security_groups] << @db_security_group when 'DBParameterGroup','DBParameterGroupStatus' @db_instance[:db_parameter_group] = @db_parameter_group when *@m then @result[:db_instances] << @db_instance end end end # -------------------------------------------- # DB Security Groups # -------------------------------------------- class DescribeDbSecurityGroupsParser < RightAWSParser # :nodoc: def reset @m = [ 'DBSecurityGroup', 'CreateDBSecurityGroupResult', 'AuthorizeDBSecurityGroupIngressResult', 'RevokeDBSecurityGroupIngressResult' ] @result = { :db_security_groups => [] } end def tagstart(name, attributes) case name when *@m then @item = { :ec2_security_groups => [], :ip_ranges => [] } when 'IPRange' then @ip_range = {} when 'EC2SecurityGroup' then @ec2_security_group = {} end end def tagend(name) case name when 'Marker' then @result[:marker] = @text when 'MaxRecords' then @result[:max_records] = @text.to_i when 'DBSecurityGroupDescription' then @item[:description] = @text when 'OwnerId' then @item[:owner_id] = @text when 'DBSecurityGroupName' then @item[:name] = @text when 'Status' case @xmlpath when /IPRange$/ then @ip_range[:status] = @text when /EC2SecurityGroup$/ then @ec2_security_group[:status] = @text end when 'EC2SecurityGroupName' then @ec2_security_group[:name] = @text when 'EC2SecurityGroupOwnerId' then @ec2_security_group[:owner_id] = @text when 'CIDRIP' then @ip_range[:cidrip] = @text when 'IPRange' then @item[:ip_ranges] << @ip_range when 'EC2SecurityGroup' then @item[:ec2_security_groups] << @ec2_security_group when *@m # Sort the ip_ranges and ec2_security_groups @item[:ip_ranges].sort!{ |i1,i2| "#{i1[:cidrip]}" <=> "#{i2[:cidrip]}" } @item[:ec2_security_groups].sort!{ |i1,i2| "#{i1[:owner_id]}#{i1[:name]}" <=> "#{i2[:owner_id]}#{i2[:name]}" } @result[:db_security_groups] << @item end end end # -------------------------------------------- # DB Security Groups # -------------------------------------------- class DescribeDbParameterGroupsParser < RightAWSParser # :nodoc: def reset @m = [ 'DBParameterGroup', 'CreateDBParameterGroupResult', 'ModifyDBParameterGroupResult' ] @result = { :db_parameter_groups => [] } end def tagstart(name, attributes) case name when *@m then @item = { } end end def tagend(name) case name when 'Marker' then @result[:marker] = @text when 'MaxRecords' then @result[:max_records] = @text.to_i when 'DBParameterGroupName' then @item[:name] = @text when 'Description' then @item[:description] = @text when 'Engine' then @item[:engine] = @text when *@m then @result[:db_parameter_groups] << @item end end end class DescribeDbParametersParser < RightAWSParser # :nodoc: def reset @result = { :parameters => [] } end def tagstart(name, attributes) case name when 'Parameter' then @item = {} end end def tagend(name) case name when 'Marker' then @result[:marker] = @text when 'MaxRecords' then @result[:max_records] = @text.to_i when 'DBParameterGroupName' then @result[:group_name] = @text # DescribeDbParametersResponse when 'Engine' then @result[:engine] = @text # DescribeDBEngineDefaultParametersResponse when 'DataType' then @item[:data_type] = @text when 'Source' then @item[:source] = @text when 'Description' then @item[:description] = @text when 'IsModifiable' then @item[:is_modifiable] = @text[/true/] ? true : false when 'ApplyType' then @item[:apply_type] = @text when 'AllowedValues' then @item[:allowed_values] = @text when 'ParameterName' then @item[:name] = @text when 'ParameterValue' then @item[:value] = @text when 'Parameter' then @result[:parameters] << @item end end end # -------------------------------------------- # DB Snapshots # -------------------------------------------- class DescribeDbSnapshotsParser < RightAWSParser # :nodoc: def reset @m = ['DBSnapshot', 'CreateDBSnapshotResult', 'DeleteDBSnapshotResult'] @result = { :db_snapshots => [] } end def tagstart(name, attributes) case name when *@m @db_snapshot = {} end end def tagend(name) case name when 'Marker' then @result[:marker] = @text when 'MaxRecords' then @result[:max_records] = @text.to_i # ? when 'Engine' then @db_snapshot[:engine] = @text when 'InstanceCreateTime' then @db_snapshot[:instance_create_time] = @text when 'EndpointPort' then @db_snapshot[:endpoint_port] = @text.to_i when 'Status' then @db_snapshot[:status] = @text when 'AvailabilityZone' then @db_snapshot[:availability_zone] = @text when 'MasterUsername' then @db_snapshot[:master_username] = @text when 'AllocatedStorage' then @db_snapshot[:allocated_storage] = @text.to_i when 'SnapshotCreateTime' then @db_snapshot[:create_time] = @text when 'DBInstanceIdentifier' then @db_snapshot[:instance_aws_id] = @text when 'DBSnapshotIdentifier' then @db_snapshot[:aws_id] = @text when *@m then @result[:db_snapshots] << @db_snapshot end end end # -------------------------------------------- # DB Events # -------------------------------------------- class DescribeEventsParser < RightAWSParser # :nodoc: def reset @result = { :events => [] } end def tagstart(name, attributes) case name when 'Event' then @event = {} end end def tagend(name) case name when 'Marker' then @result[:marker] = @text when 'MaxRecords' then @result[:max_records] = @text.to_i # ? when 'Date' then @event[:date] = @text when 'SourceIdentifier' then @event[:aws_id] = @text when 'SourceType' then @event[:source_type] = @text when 'Message' then @event[:message] = @text when 'Event' then @result[:events] << @event end end end end end