rds 26.5 KB
Newer Older
Bruce Pennypacker committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#!/usr/bin/python
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.

DOCUMENTATION = '''
---
module: rds
20
version_added: "1.3"
21
short_description: create, delete, or modify an Amazon rds instance
Bruce Pennypacker committed
22
description:
23
     - Creates, deletes, or modifies rds instances.  When creating an instance it can be either a new instance or a read-only replica of an existing instance. This module has a dependency on python-boto >= 2.5. The 'promote' command requires boto >= 2.18.0.
Bruce Pennypacker committed
24
options:
25
  command:
Bruce Pennypacker committed
26
    description:
27
      - Specifies the action to take.  
Bruce Pennypacker committed
28 29 30
    required: true
    default: null
    aliases: []
31
    choices: [ 'create', 'replicate', 'delete', 'facts', 'modify' , 'promote', 'snapshot', 'restore' ]
Bruce Pennypacker committed
32 33 34 35 36 37 38 39
  instance_name:
    description:
      - Database instance identifier.
    required: true
    default: null
    aliases: []
  source_instance:
    description:
40
      - Name of the database to replicate. Used only when command=replicate.
Bruce Pennypacker committed
41 42 43 44 45
    required: false
    default: null
    aliases: []
  db_engine:
    description:
46
      - The type of database.  Used only when command=create. 
Bruce Pennypacker committed
47 48 49
    required: false
    default: null
    aliases: []
50
    choices: [ 'MySQL', 'oracle-se1', 'oracle-se', 'oracle-ee', 'sqlserver-ee', 'sqlserver-se', 'sqlserver-ex', 'sqlserver-web', 'postgres']
Bruce Pennypacker committed
51 52
  size:
    description:
53
      - Size in gigabytes of the initial storage for the DB instance. Used only when command=create or command=modify.
Bruce Pennypacker committed
54 55 56 57 58
    required: false
    default: null
    aliases: []
  instance_type:
    description:
59
      - The instance type of the database.  Must be specified when command=create. Optional when command=replicate, command=modify or command=restore. If not specified then the replica inherits the same instance type as the source instance. 
Bruce Pennypacker committed
60 61 62
    required: false
    default: null
    aliases: []
63
    choices: [ '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.cr1.8xlarge' ]
Bruce Pennypacker committed
64 65
  username:
    description:
66
      - Master database username. Used only when command=create.
Bruce Pennypacker committed
67 68 69 70 71
    required: false
    default: null
    aliases: []
  password:
    description:
72
      - Password for the master database username. Used only when command=create or command=modify.
Bruce Pennypacker committed
73 74 75
    required: false
    default: null
    aliases: []
Bruce Pennypacker committed
76
  region:
Bruce Pennypacker committed
77
    description:
Bruce Pennypacker committed
78
      - The AWS region to use. If not specified then the value of the EC2_REGION environment variable, if any, is used.
Bruce Pennypacker committed
79 80
    required: true
    default: null
Bruce Pennypacker committed
81
    aliases: [ 'aws_region', 'ec2_region' ]
Bruce Pennypacker committed
82 83
  db_name:
    description:
84
      - Name of a database to create within the instance.  If not specified then no database is created. Used only when command=create.
Bruce Pennypacker committed
85 86 87 88 89
    required: false
    default: null
    aliases: []
  engine_version:
    description:
90
      - Version number of the database engine to use. Used only when command=create. If not specified then the current Amazon RDS default engine version is used.
Bruce Pennypacker committed
91 92 93 94 95
    required: false
    default: null
    aliases: []
  parameter_group:
    description:
96
      - Name of the DB parameter group to associate with this instance.  If omitted then the RDS default DBParameterGroup will be used. Used only when command=create or command=modify.
Bruce Pennypacker committed
97 98 99 100 101
    required: false
    default: null
    aliases: []
  license_model:
    description:
102
      - The license model for this DB instance. Used only when command=create or command=restore. 
Bruce Pennypacker committed
103 104 105
    required: false
    default: null
    aliases: []
106
    choices:  [ 'license-included', 'bring-your-own-license', 'general-public-license' ]
Bruce Pennypacker committed
107 108
  multi_zone:
    description:
109 110
      - Specifies if this is a Multi-availability-zone deployment. Can not be used in conjunction with zone parameter. Used only when command=create or command=modify.
    choices: [ "yes", "no" ] 
Bruce Pennypacker committed
111 112 113 114 115
    required: false
    default: null
    aliases: []
  iops:
    description:
116
      - Specifies the number of IOPS for the instance.  Used only when command=create or command=modify. Must be an integer greater than 1000.
Bruce Pennypacker committed
117 118 119 120 121
    required: false
    default: null
    aliases: []
  security_groups:
    description:
122 123 124 125 126 127
      - Comma separated list of one or more security groups.  Used only when command=create or command=modify.
    required: false
    default: null
    aliases: []
  vpc_security_groups:
    description:
128
      - Comma separated list of one or more vpc security group ids. Also requires `subnet` to be specified. Used only when command=create or command=modify.
Bruce Pennypacker committed
129 130 131 132 133
    required: false
    default: null
    aliases: []
  port:
    description:
134
      - Port number that the DB instance uses for connections.  Defaults to 3306 for mysql. Must be changed to 1521 for Oracle, 1443 for SQL Server, 5432 for PostgreSQL. Used only when command=create or command=replicate.
Bruce Pennypacker committed
135 136 137 138 139
    required: false
    default: null
    aliases: []
  upgrade:
    description:
140
      - Indicates that minor version upgrades should be applied automatically. Used only when command=create or command=replicate. 
Bruce Pennypacker committed
141 142 143 144 145 146
    required: false
    default: no
    choices: [ "yes", "no" ]
    aliases: []
  option_group:
    description:
147
      - The name of the option group to use.  If not specified then the default option group is used. Used only when command=create.
Bruce Pennypacker committed
148 149 150 151 152
    required: false
    default: null
    aliases: []
  maint_window:
    description:
153
      - "Maintenance window in format of ddd:hh24:mi-ddd:hh24:mi.  (Example: Mon:22:00-Mon:23:15) If not specified then a random maintenance window is assigned. Used only when command=create or command=modify."
Bruce Pennypacker committed
154 155 156 157 158
    required: false
    default: null
    aliases: []
  backup_window:
    description:
159
      - Backup window in format of hh24:mi-hh24:mi.  If not specified then a random backup window is assigned. Used only when command=create or command=modify.
Bruce Pennypacker committed
160 161 162 163 164
    required: false
    default: null
    aliases: []
  backup_retention:
    description:
165
      - "Number of days backups are retained.  Set to 0 to disable backups.  Default is 1 day.  Valid range: 0-35. Used only when command=create or command=modify."
Bruce Pennypacker committed
166 167 168 169 170
    required: false
    default: null
    aliases: []
  zone:
    description:
171
      - availability zone in which to launch the instance. Used only when command=create, command=replicate or command=restore.
Bruce Pennypacker committed
172 173
    required: false
    default: null
Bruce Pennypacker committed
174
    aliases: ['aws_zone', 'ec2_zone']
Bruce Pennypacker committed
175 176
  subnet:
    description:
177
      - VPC subnet group.  If specified then a VPC instance is created. Used only when command=create.
Bruce Pennypacker committed
178 179 180 181 182
    required: false
    default: null
    aliases: []
  snapshot:
    description:
183
      - Name of snapshot to take. When command=delete, if no snapshot name is provided then no snapshot is taken. Used only when command=delete or command=snapshot.
Bruce Pennypacker committed
184 185 186
    required: false
    default: null
    aliases: []
Bruce Pennypacker committed
187
  aws_secret_key:
Bruce Pennypacker committed
188
    description:
Bruce Pennypacker committed
189
      - AWS secret key. If not set then the value of the AWS_SECRET_KEY environment variable is used. 
Bruce Pennypacker committed
190 191
    required: false
    default: null
Bruce Pennypacker committed
192 193
    aliases: [ 'ec2_secret_key', 'secret_key' ]
  aws_access_key:
Bruce Pennypacker committed
194
    description:
Bruce Pennypacker committed
195
      - AWS access key. If not set then the value of the AWS_ACCESS_KEY environment variable is used.
Bruce Pennypacker committed
196 197
    required: false
    default: null
Bruce Pennypacker committed
198
    aliases: [ 'ec2_access_key', 'access_key' ]
Bruce Pennypacker committed
199 200
  wait:
    description:
201
      - When command=create, replicate, modify or restore then wait for the database to enter the 'available' state.  When command=delete wait for the database to be terminated.
Bruce Pennypacker committed
202 203 204 205 206 207 208 209 210
    required: false
    default: "no"
    choices: [ "yes", "no" ]
    aliases: []
  wait_timeout:
    description:
      - how long before wait gives up, in seconds
    default: 300
    aliases: []
Bruce Pennypacker committed
211 212
  apply_immediately:
    description:
213
      - Used only when command=modify.  If enabled, the modifications will be applied as soon as possible rather than waiting for the next preferred maintenance window.
Bruce Pennypacker committed
214 215 216
    default: no
    choices: [ "yes", "no" ]
    aliases: []
217 218 219
  new_instance_name:
    description:
      - Name to rename an instance to. Used only when command=modify.
220
    required: false
221 222
    default: null
    aliases: []
223
    version_added: 1.5
Bruce Pennypacker committed
224 225 226 227 228 229
requirements: [ "boto" ]
author: Bruce Pennypacker
'''

EXAMPLES = '''
# Basic mysql provisioning example
230 231 232 233 234 235 236 237
- rds: >
      command=create
      instance_name=new_database
      db_engine=MySQL
      size=10
      instance_type=db.m1.small
      username=mysql_admin
      password=1nsecure
Bruce Pennypacker committed
238 239

# Create a read-only replica and wait for it to become available
240 241 242 243 244 245
- rds: >
      command=replicate
      instance_name=new_database_replica
      source_instance=new_database
      wait=yes
      wait_timeout=600
Bruce Pennypacker committed
246 247

# Delete an instance, but create a snapshot before doing so
248 249 250 251
- rds: >
      command=delete
      instance_name=new_database
      snapshot=new_database_snapshot
Bruce Pennypacker committed
252 253

# Get facts about an instance
254 255 256 257
- rds: >
      command=facts
      instance_name=new_database
      register: new_database_facts
258 259 260 261 262 263 264

# Rename an instance and wait for the change to take effect
- rds: >
      command=modify
      instance_name=new_database
      new_instance_name=renamed_database
      wait=yes
Bruce Pennypacker committed
265
    
Bruce Pennypacker committed
266 267 268 269 270 271 272 273 274 275 276
'''

import sys
import time

try:
    import boto.rds
except ImportError:
    print "failed=True msg='boto required for this module'"
    sys.exit(1)

277 278 279 280 281 282 283 284
def get_current_resource(conn, resource, command):
    # There will be exceptions but we want the calling code to handle them
    if command == 'snapshot':
        return conn.get_all_dbsnapshots(snapshot_id=resource)[0]
    else:
        return conn.get_all_dbinstances(resource)[0]


Bruce Pennypacker committed
285
def main():
286 287
    argument_spec = ec2_argument_spec()
    argument_spec.update(dict(
288
            command           = dict(choices=['create', 'replicate', 'delete', 'facts', 'modify', 'promote', 'snapshot', 'restore'], required=True),
289 290
            instance_name     = dict(required=True),
            source_instance   = dict(required=False),
291
            db_engine         = dict(choices=['MySQL', 'oracle-se1', 'oracle-se', 'oracle-ee', 'sqlserver-ee', 'sqlserver-se', 'sqlserver-ex', 'sqlserver-web', 'postgres'], required=False),
292
            size              = dict(required=False), 
293
            instance_type     = dict(aliases=['type'], choices=['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.cr1.8xlarge'], required=False),
294 295 296 297 298 299 300 301 302
            username          = dict(required=False),
            password          = dict(no_log=True, required=False),
            db_name           = dict(required=False),
            engine_version    = dict(required=False),
            parameter_group   = dict(required=False),
            license_model     = dict(choices=['license-included', 'bring-your-own-license', 'general-public-license'], required=False),
            multi_zone        = dict(type='bool', default=False),
            iops              = dict(required=False), 
            security_groups   = dict(required=False),
303
            vpc_security_groups = dict(type='list', required=False),
304 305 306 307 308 309
            port              = dict(required=False),
            upgrade           = dict(type='bool', default=False),
            option_group      = dict(required=False),
            maint_window      = dict(required=False),
            backup_window     = dict(required=False),
            backup_retention  = dict(required=False), 
Bruce Pennypacker committed
310
            zone              = dict(aliases=['aws_zone', 'ec2_zone'], required=False),
311 312 313 314 315
            subnet            = dict(required=False),
            wait              = dict(type='bool', default=False),
            wait_timeout      = dict(default=300),
            snapshot          = dict(required=False),
            apply_immediately = dict(type='bool', default=False),
316
            new_instance_name = dict(required=False),
Bruce Pennypacker committed
317 318 319
        )
    )

320 321 322 323
    module = AnsibleModule(
        argument_spec=argument_spec,
    )

324
    command            = module.params.get('command')
Michael DeHaan committed
325 326 327 328 329 330 331 332 333 334 335 336 337 338
    instance_name      = module.params.get('instance_name')
    source_instance    = module.params.get('source_instance')
    db_engine          = module.params.get('db_engine')
    size               = module.params.get('size')
    instance_type      = module.params.get('instance_type')
    username           = module.params.get('username')
    password           = module.params.get('password')
    db_name            = module.params.get('db_name')
    engine_version     = module.params.get('engine_version')
    parameter_group    = module.params.get('parameter_group')
    license_model      = module.params.get('license_model')
    multi_zone         = module.params.get('multi_zone')
    iops               = module.params.get('iops')
    security_groups    = module.params.get('security_groups')
339
    vpc_security_groups = module.params.get('vpc_security_groups')
Michael DeHaan committed
340 341 342 343 344 345
    port               = module.params.get('port')
    upgrade            = module.params.get('upgrade')
    option_group       = module.params.get('option_group')
    maint_window       = module.params.get('maint_window')
    subnet             = module.params.get('subnet')
    backup_window      = module.params.get('backup_window')
346
    backup_retention   = module.params.get('backup_retention')
Bruce Pennypacker committed
347
    region             = module.params.get('region')
348
    zone               = module.params.get('zone')
Bruce Pennypacker committed
349 350
    aws_secret_key     = module.params.get('aws_secret_key')
    aws_access_key     = module.params.get('aws_access_key')
351 352 353 354
    wait               = module.params.get('wait')
    wait_timeout       = int(module.params.get('wait_timeout'))
    snapshot           = module.params.get('snapshot')
    apply_immediately  = module.params.get('apply_immediately')
355
    new_instance_name  = module.params.get('new_instance_name')
Bruce Pennypacker committed
356

357
    ec2_url, aws_access_key, aws_secret_key, region = get_ec2_creds(module)
358
    if not region:
Bruce Pennypacker committed
359
        module.fail_json(msg = str("region not specified and unable to determine region from EC2_REGION."))
Bruce Pennypacker committed
360 361 362

    # connect to the rds endpoint
    try:
363
        conn = boto.rds.connect_to_region(region, aws_access_key_id=aws_access_key, aws_secret_access_key=aws_secret_key)
Bruce Pennypacker committed
364 365 366
    except boto.exception.BotoServerError, e:
        module.fail_json(msg = e.error_message)

367 368 369 370 371 372
    def invalid_security_group_type(subnet):
        if subnet:
            return 'security_groups'
        else:
            return 'vpc_security_groups'

373 374 375
    # Package up the optional parameters
    params = {}

376 377
    # Validate parameters for each command
    if command == 'create':
Bruce Pennypacker committed
378
        required_vars = [ 'instance_name', 'db_engine', 'size', 'instance_type', 'username', 'password' ]
379
        invalid_vars  = [ 'source_instance', 'snapshot', 'apply_immediately', 'new_instance_name' ] + [invalid_security_group_type(subnet)]
380

381
    elif command == 'replicate':
Bruce Pennypacker committed
382
        required_vars = [ 'instance_name', 'source_instance' ]
383
        invalid_vars  = [ 'db_engine', 'size', 'username', 'password', 'db_name', 'engine_version', 'parameter_group', 'license_model', 'multi_zone', 'iops', 'vpc_security_groups', 'security_groups', 'option_group', 'maint_window', 'backup_window', 'backup_retention', 'subnet', 'snapshot', 'apply_immediately', 'new_instance_name' ]
384

385
    elif command == 'delete':
Bruce Pennypacker committed
386
        required_vars = [ 'instance_name' ]
387
        invalid_vars  = [ 'db_engine', 'size', 'instance_type', 'username', 'password', 'db_name', 'engine_version', 'parameter_group', 'license_model', 'multi_zone', 'iops', 'vpc_security_groups' ,'security_groups', 'option_group', 'maint_window', 'backup_window', 'backup_retention', 'port', 'upgrade', 'subnet', 'zone' , 'source_instance', 'apply_immediately', 'new_instance_name' ]
388

389
    elif command == 'facts':
Bruce Pennypacker committed
390
        required_vars = [ 'instance_name' ]
391
        invalid_vars  = [ 'db_engine', 'size', 'instance_type', 'username', 'password', 'db_name', 'engine_version', 'parameter_group', 'license_model', 'multi_zone', 'iops', 'vpc_security_groups', 'security_groups', 'option_group', 'maint_window', 'backup_window', 'backup_retention', 'port', 'upgrade', 'subnet', 'zone', 'wait', 'source_instance' 'apply_immediately', 'new_instance_name' ]
392

393
    elif command == 'modify':
Bruce Pennypacker committed
394
        required_vars = [ 'instance_name' ]
395 396
        if password:
            params["master_password"] = password
397
        invalid_vars = [ 'db_engine', 'username', 'db_name', 'engine_version',  'license_model', 'option_group', 'port', 'upgrade', 'subnet', 'zone', 'source_instance']
398 399 400

    elif command == 'promote':
        required_vars = [ 'instance_name' ]
401
        invalid_vars = [ 'db_engine', 'size', 'username', 'password', 'db_name', 'engine_version', 'parameter_group', 'license_model', 'multi_zone', 'iops', 'vpc_security_groups', 'security_groups', 'option_group', 'maint_window', 'subnet', 'source_instance', 'snapshot', 'apply_immediately', 'new_instance_name' ]
402 403 404
    
    elif command == 'snapshot':
        required_vars = [ 'instance_name', 'snapshot']
405
        invalid_vars = [ 'db_engine', 'size', 'username', 'password', 'db_name', 'engine_version', 'parameter_group', 'license_model', 'multi_zone', 'iops', 'vpc_security_groups', 'security_groups', 'option_group', 'maint_window', 'subnet', 'source_instance', 'apply_immediately', 'new_instance_name' ]
406 407 408

    elif command == 'restore':
        required_vars = [ 'instance_name', 'snapshot', 'instance_type' ]
409
        invalid_vars = [ 'db_engine', 'db_name', 'username', 'password', 'engine_version',  'option_group', 'source_instance', 'apply_immediately', 'new_instance_name', 'vpc_security_groups', 'security_groups' ]
Bruce Pennypacker committed
410 411 412
 
    for v in required_vars:
        if not module.params.get(v):
413
            module.fail_json(msg = str("Parameter %s required for %s command" % (v, command)))
Bruce Pennypacker committed
414 415 416
            
    for v in invalid_vars:
        if module.params.get(v):
417
            module.fail_json(msg = str("Parameter %s invalid for %s command" % (v, command)))
Bruce Pennypacker committed
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464

    if db_engine:
        params["engine"] = db_engine

    if port:
        params["port"] = port

    if db_name:
        params["db_name"] = db_name

    if parameter_group:
        params["param_group"] = parameter_group

    if zone:
        params["availability_zone"] = zone
  
    if maint_window:
        params["preferred_maintenance_window"] = maint_window

    if backup_window:
        params["preferred_backup_window"] = backup_window

    if backup_retention:
        params["backup_retention_period"] = backup_retention

    if multi_zone:
        params["multi_az"] = multi_zone

    if engine_version:
        params["engine_version"] = engine_version

    if upgrade:
        params["auto_minor_version_upgrade"] = upgrade

    if subnet:
        params["db_subnet_group_name"] = subnet

    if license_model:
        params["license_model"] = license_model

    if option_group:
        params["option_group_name"] = option_group

    if iops:
        params["iops"] = iops

    if security_groups:
465 466 467
        params["security_groups"] = security_groups.split(',')

    if vpc_security_groups:
468 469 470 471
        groups_list = []
        for x in vpc_security_groups:
            groups_list.append(boto.rds.VPCSecurityGroupMembership(vpc_group=x))
        params["vpc_security_groups"] = groups_list
Bruce Pennypacker committed
472

473 474 475
    if new_instance_name:
        params["new_instance_id"] = new_instance_name

476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
    changed = True

    if command in ['create', 'restore', 'facts']:
        try:
            result = conn.get_all_dbinstances(instance_name)[0]
            changed = False
        except boto.exception.BotoServerError, e:
            try:
                if command == 'create': 
                    result = conn.create_dbinstance(instance_name, size, instance_type, username, password, **params)
                if command == 'restore':
                    result = conn.restore_dbinstance_from_dbsnapshot(snapshot, instance_name, instance_type, **params)
                if command == 'facts':
                    module.fail_json(msg = "DB Instance %s does not exist" % instance_name)
            except boto.exception.BotoServerError, e:
                module.fail_json(msg = e.error_message)

    if command == 'snapshot':
        try:
            result = conn.get_all_dbsnapshots(snapshot)[0]
            changed = False
        except boto.exception.BotoServerError, e:
            try:
                result = conn.create_dbsnapshot(snapshot, instance_name)
            except boto.exception.BotoServerError, e:
                module.fail_json(msg = e.error_message)
        
    if command == 'delete':
        try:
            result = conn.get_all_dbinstances(instance_name)[0]
            if result.status == 'deleting':
                module.exit_json(changed=False)
        except boto.exception.BotoServerError, e:
            module.exit_json(changed=False)
        try:
Bruce Pennypacker committed
511 512 513
            if snapshot:
                params["skip_final_snapshot"] = False
                params["final_snapshot_id"] = snapshot
514 515
            else:
                params["skip_final_snapshot"] = True
516 517 518
            result = conn.delete_dbinstance(instance_name, **params)
        except boto.exception.BotoServerError, e:
            module.fail_json(msg = e.error_message)
519

520 521 522 523 524 525 526 527 528 529
    if command == 'replicate':
        try: 
            if instance_type:
                params["instance_class"] = instance_type
            result = conn.create_dbinstance_read_replica(instance_name, source_instance, **params)
        except boto.exception.BotoServerError, e:
            module.fail_json(msg = e.error_message)

    if command == 'modify':
        try:
Bruce Pennypacker committed
530
            params["apply_immediately"] = apply_immediately
531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
            result = conn.modify_dbinstance(instance_name, **params)
        except boto.exception.BotoServerError, e:
            module.fail_json(msg = e.error_message)
        if apply_immediately:
            if new_instance_name:
                # Wait until the new instance name is valid
                found = 0
                while found == 0:
                    instances = conn.get_all_dbinstances()
                    for i in instances:
                        if i.id == new_instance_name:
                            instance_name = new_instance_name
                            found = 1
                    if found == 0:
                        time.sleep(5)
546 547 548 549 550 551

                # The name of the database has now changed, so we have
                # to force result to contain the new instance, otherwise
                # the call below to get_current_resource will fail since it
                # will be looking for the old instance name.
                result.id = new_instance_name
552 553 554 555
            else:
                # Wait for a few seconds since it takes a while for AWS
                # to change the instance from 'available' to 'modifying'
                time.sleep(5)
Bruce Pennypacker committed
556

557 558 559 560 561
    if command == 'promote':
        try:
            result = conn.promote_read_replica(instance_name, **params)
        except boto.exception.BotoServerError, e:
            module.fail_json(msg = e.error_message)
Bruce Pennypacker committed
562 563 564

    # If we're not waiting for a delete to complete then we're all done
    # so just return
565
    if command == 'delete' and not wait:
Bruce Pennypacker committed
566 567
        module.exit_json(changed=True)

568 569
    try:
       resource = get_current_resource(conn, result.id, command)
570 571 572
    except boto.exception.BotoServerError, e:
        module.fail_json(msg = e.error_message)

573
    # Wait for the resource to be available if requested
Bruce Pennypacker committed
574 575
    if wait:
        try: 
Bruce Pennypacker committed
576
            wait_timeout = time.time() + wait_timeout
Bruce Pennypacker committed
577 578
            time.sleep(5)

579
            while wait_timeout > time.time() and resource.status != 'available':
Bruce Pennypacker committed
580 581
                time.sleep(5)
                if wait_timeout <= time.time():
582 583
                    module.fail_json(msg = "Timeout waiting for resource %s" % resource.id)
                resource = get_current_resource(conn, result.id, command)
Bruce Pennypacker committed
584 585 586 587
        except boto.exception.BotoServerError, e:
            # If we're waiting for an instance to be deleted then
            # get_all_dbinstances will eventually throw a 
            # DBInstanceNotFound error.
588
            if command == 'delete' and e.error_code == 'DBInstanceNotFound':
Bruce Pennypacker committed
589 590 591 592 593 594
                module.exit_json(changed=True)                
            else:
                module.fail_json(msg = e.error_message)

    # If we got here then pack up all the instance details to send
    # back to ansible
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
    if command == 'snapshot':
        d = { 
            'id'                 : resource.id,
            'create_time'        : resource.snapshot_create_time,
            'status'             : resource.status,
            'availability_zone'  : resource.availability_zone,
            'instance_id'        : resource.instance_id,
            'instance_created'   : resource.instance_create_time,
        }
        try:
            d["snapshot_type"] = resource.snapshot_type
            d["iops"] = resource.iops
        except AttributeError, e:
            pass # needs boto >= 2.21.0

        return module.exit_json(changed=changed, snapshot=d)

Bruce Pennypacker committed
612
    d = {
613 614 615 616 617 618 619 620 621 622 623
        'id'                 : resource.id,
        'create_time'        : resource.create_time,
        'status'             : resource.status,
        'availability_zone'  : resource.availability_zone,
        'backup_retention'   : resource.backup_retention_period,
        'backup_window'      : resource.preferred_backup_window,
        'maintenance_window' : resource.preferred_maintenance_window,
        'multi_zone'         : resource.multi_az,
        'instance_type'      : resource.instance_class,
        'username'           : resource.master_username,
        'iops'               : resource.iops
Bruce Pennypacker committed
624
        }
625 626

    # Endpoint exists only if the instance is available
627 628 629
    if resource.status == 'available' and command != 'snapshot':
        d["endpoint"] = resource.endpoint[0]
        d["port"] = resource.endpoint[1]
630 631 632 633
        if resource.vpc_security_groups is not None:
            d["vpc_security_groups"] = ','.join(x.vpc_group for x in resource.vpc_security_groups)
        else:
            d["vpc_security_groups"] = None
634 635 636
    else:
        d["endpoint"] = None
        d["port"] = None
637
        d["vpc_security_groups"] = None
638

639 640
    # ReadReplicaSourceDBInstanceIdentifier may or may not exist
    try:
641
        d["replication_source"] = resource.ReadReplicaSourceDBInstanceIdentifier
642 643 644
    except Exception, e:
        d["replication_source"] = None

645
    module.exit_json(changed=changed, instance=d)
Bruce Pennypacker committed
646

647
# import module snippets
648
from ansible.module_utils.basic import *
649
from ansible.module_utils.ec2 import *
Bruce Pennypacker committed
650 651

main()