ec2: Fix bug with running/stopped state and wait=yes.

If `get_all_instances` returns multiple reservations, the old wait loop only
dealt with the first reservation. Thus, the wait loop may end before all
instances get to be running/stopped.

Also clean up the code a little.
This commit is contained in:
Yap Sok Ann 2014-04-07 15:06:01 +08:00
parent ac72d44219
commit d2329baa93

View file

@ -193,7 +193,7 @@ options:
instance_ids: instance_ids:
version_added: "1.3" version_added: "1.3"
description: description:
- list of instance ids, currently only used when state='absent' - list of instance ids, currently used for states: absent, running, stopped
required: false required: false
default: null default: null
aliases: [] aliases: []
@ -1039,7 +1039,7 @@ def terminate_instances(module, ec2, instance_ids):
return (changed, instance_dict_array, terminated_instance_ids) return (changed, instance_dict_array, terminated_instance_ids)
def startstop_instances(module, ec2, instance_ids): def startstop_instances(module, ec2, instance_ids, state):
""" """
Starts or stops a list of existing instances Starts or stops a list of existing instances
@ -1047,9 +1047,10 @@ def startstop_instances(module, ec2, instance_ids):
ec2: authenticated ec2 connection object ec2: authenticated ec2 connection object
instance_ids: The list of instances to start in the form of instance_ids: The list of instances to start in the form of
[ {id: <inst-id>}, ..] [ {id: <inst-id>}, ..]
state: Intended state ("running" or "stopped")
Returns a dictionary of instance information Returns a dictionary of instance information
about the instances started. about the instances started/stopped.
If the instance was not able to change state, If the instance was not able to change state,
"changed" will be set to False. "changed" will be set to False.
@ -1064,18 +1065,15 @@ def startstop_instances(module, ec2, instance_ids):
if not isinstance(instance_ids, list) or len(instance_ids) < 1: if not isinstance(instance_ids, list) or len(instance_ids) < 1:
module.fail_json(msg='instance_ids should be a list of instances, aborting') 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 # Check that our instances are not in the state we want to take them to
# and change them to our desired state # and change them to our desired state
running_instances_array = [] running_instances_array = []
for res in ec2.get_all_instances(instance_ids): for res in ec2.get_all_instances(instance_ids):
for inst in res.instances: for inst in res.instances:
if not inst.state == dest_state_ec2: if inst.state != state:
instance_dict_array.append(get_instance_info(inst)) instance_dict_array.append(get_instance_info(inst))
try: try:
if dest_state == 'running': if state == 'running':
inst.start() inst.start()
else: else:
inst.stop() inst.stop()
@ -1084,21 +1082,14 @@ def startstop_instances(module, ec2, instance_ids):
changed = True changed = True
## Wait for all the instances to finish starting or stopping ## 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 wait_timeout = time.time() + wait_timeout
while wait_timeout > time.time() and num_running < len(instids): while wait and wait_timeout > time.time():
res_list = res.connection.get_all_instances(instids) matched_instances = []
if len(res_list) > 0: for res in ec2.get_all_instances(instance_ids):
this_res = res_list[0] for i in res.instances:
num_running = len([ i for i in this_res.instances if i.state == dest_state_ec2 ]) if i.state == state:
else: matched_instances.append(i)
# got a bad response of some sort, possibly due to if len(matched_instances) < len(instance_ids):
# stale/cached data. Wait a second and then try again
time.sleep(1)
continue
if wait and num_running < len(instids):
time.sleep(5) time.sleep(5)
else: else:
break break
@ -1107,10 +1098,7 @@ def startstop_instances(module, ec2, instance_ids):
# waiting took too long # waiting took too long
module.fail_json(msg = "wait for instances running timeout on %s" % time.asctime()) module.fail_json(msg = "wait for instances running timeout on %s" % time.asctime())
for inst in this_res.instances: return (changed, instance_dict_array, instance_ids)
running_instances_array.append(inst.id)
return (changed, instance_dict_array, running_instances_array)
def main(): def main():
@ -1160,21 +1148,23 @@ def main():
tagged_instances = [] tagged_instances = []
if module.params.get('state') == 'absent': state = module.params.get('state')
if state == 'absent':
instance_ids = module.params.get('instance_ids') instance_ids = module.params.get('instance_ids')
if not isinstance(instance_ids, list): if not isinstance(instance_ids, list):
module.fail_json(msg='termination_list needs to be a list of instances to terminate') module.fail_json(msg='termination_list needs to be a list of instances to terminate')
(changed, instance_dict_array, new_instance_ids) = terminate_instances(module, ec2, instance_ids) (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': elif state in ('running', 'stopped'):
instance_ids = module.params.get('instance_ids') instance_ids = module.params.get('instance_ids')
if not isinstance(instance_ids, list): if not isinstance(instance_ids, list):
module.fail_json(msg='running list needs to be a list of instances to run: %s' % instance_ids) 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) (changed, instance_dict_array, new_instance_ids) = startstop_instances(module, ec2, instance_ids, state)
elif module.params.get('state') == 'present': elif state == 'present':
# Changed is always set to true when provisioning new instances # Changed is always set to true when provisioning new instances
if not module.params.get('image'): if not module.params.get('image'):
module.fail_json(msg='image parameter is required for new instance') module.fail_json(msg='image parameter is required for new instance')