From 8b5fee61bb6024f86bbd03ca84c15f2db7c5a280 Mon Sep 17 00:00:00 2001 From: Nicholas DeClario Date: Thu, 12 Dec 2013 17:16:59 -0500 Subject: [PATCH] Added ability to start and stop existing EC2 instances. --- cloud/ec2 | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 2 deletions(-) diff --git a/cloud/ec2 b/cloud/ec2 index 0e0b8aaf0fd..b9306ec97ea 100644 --- a/cloud/ec2 +++ b/cloud/ec2 @@ -17,9 +17,9 @@ DOCUMENTATION = ''' --- module: ec2 -short_description: create or terminate an instance in ec2, return instanceid +short_description: create, terminate, start or stop an instance in ec2, return instanceid description: - - Creates or terminates ec2 instances. When created optionally waits for it to be 'running'. This module has a dependency on python-boto >= 2.5 + - Creates or terminates ec2 instances. When created optionally waits for it to be 'running'. This module has a dependency on python-boto >= 2.5 version_added: "0.9" options: key_name: @@ -289,6 +289,49 @@ local_action: state: 'absent' instance_ids: {{ec2.instance_ids}} +# Start a few existing instances, run some tasks +# and stop the instances + +- name: Start sandbox instances + hosts: localhost + gather_facts: false + connection: local + vars: + instance_ids: + - 'i-xxxxxx' + - 'i-xxxxxx' + - 'i-xxxxxx' + region: us-east-1 + tasks: + - name: Start the sandbox instances + local_action: + module: ec2 + instance_ids: '{{ instance_ids }}' + region: '{{ region }}' + state: running + wait: True + role: + - do_neat_stuff + - do_more_neat_stuff + +- name: Stop sandbox instances + hosts: localhost + gather_facts: false + connection: local + vars: + instance_ids: + - 'i-xxxxxx' + - 'i-xxxxxx' + - 'i-xxxxxx' + region: us-east-1 + tasks: + - name: Stop the sanbox instances + local_action: + module: e2 + instance_ids: '{{ instance_ids }}' + region: {'{ region }}' + state: stopped + wait: True ''' import sys @@ -621,6 +664,80 @@ def terminate_instances(module, ec2, instance_ids): return (changed, instance_dict_array, terminated_instance_ids) +def startstop_instances(module, ec2, instance_ids): + """ + Starts or stops a list of existing instances + + module: Ansible module object + ec2: authenticated ec2 connection object + instance_ids: The list of instances to start in the form of + [ {id: }, ..] + + Returns a dictionary of instance information + about the instances started. + + If the instance was not able to change state, + "changed" will be set to False. + + """ + + wait = module.params.get('wait') + wait_timeout = int(module.params.get('wait_timeout')) + changed = False + instance_dict_array = [] + + if not isinstance(instance_ids, list) or len(instance_ids) < 1: + module.fail_json(msg='instance_ids should be a list of instances, aborting') + + dest_state = module.params.get('state') + dest_state_ec2 = 'stopped' if dest_state == 'stopped' else 'running' + + # Check that our instances are not in the state we want to take them to + # and change them to our desired state + running_instances_array = [] + for res in ec2.get_all_instances(instance_ids): + for inst in res.instances: + if not inst.state == dest_state_ec2: + instance_dict_array.append(get_instance_info(inst)) + try: + if dest_state == 'running': + inst.start() + else: + inst.stop() + except EC2ResponseError as e: + module.fail_json(msg='Unable to change state for instance {0}, error: {1}'.format(inst.id, e)) + changed = True + + ## Wait for all the instances to finish starting or stopping + instids = [ i.id for i in res.instances ] + this_res = [] + num_running = 0 + wait_timeout = time.time() + wait_timeout + while wait_timeout > time.time() and num_running < len(instids): + res_list = res.connection.get_all_instances(instids) + if len(res_list) > 0: + this_res = res_list[0] + num_running = len([ i for i in this_res.instances if i.state == dest_state_ec2 ]) + else: + # got a bad response of some sort, possibly due to + # stale/cached data. Wait a second and then try again + time.sleep(1) + continue + if wait and num_running < len(instids): + time.sleep(5) + else: + break + + if wait and wait_timeout <= time.time(): + # waiting took too long + module.fail_json(msg = "wait for instances running timeout on %s" % time.asctime()) + + for inst in this_res.instances: + running_instances_array.append(inst.id) + + return (changed, instance_dict_array, running_instances_array) + + def main(): module = AnsibleModule( argument_spec = dict( @@ -679,6 +796,13 @@ def main(): (changed, instance_dict_array, new_instance_ids) = terminate_instances(module, ec2, instance_ids) + elif module.params.get('state') == 'running' or module.params.get('state') == 'stopped': + instance_ids = module.params.get('instance_ids') + if not isinstance(instance_ids, list): + module.fail_json(msg='running list needs to be a list of instances to run: %s' % instance_ids) + + (changed, instance_dict_array, new_instance_ids) = startstop_instances(module, ec2, instance_ids) + elif module.params.get('state') == 'present': # Changed is always set to true when provisioning new instances if not module.params.get('key_name'):