From e9254fbc5a9ff6b2725c59dce9615b6487f05cbb Mon Sep 17 00:00:00 2001 From: John Jarvis Date: Mon, 29 Apr 2013 23:40:17 -0400 Subject: [PATCH 1/5] Adding module to de-register/register EC2 instances This module handles AWS EC2 ELB registration. * De-registration requires the instance id which can be looked up using the ec2_facts module. * Registration requires both the instance id and the elbs that the instance belongs to. De-registration will return an ec2_elb fact that can be used for registration in post_tasks. --- library/cloud/ec2_elb | 190 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) create mode 100644 library/cloud/ec2_elb diff --git a/library/cloud/ec2_elb b/library/cloud/ec2_elb new file mode 100644 index 0000000000..7c1c7d870b --- /dev/null +++ b/library/cloud/ec2_elb @@ -0,0 +1,190 @@ +#!/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 . + +DOCUMENTATION = """ +--- +module: ec2_elb +short_description: Registers and Deregisters instances from EC2 ELB(s) +description: + - This module deregisters or registers an AWS EC2 instance from the ELB(s) + that it belongs to. + Returns fact "elb_ec2" which is a list of elbs attached the instance + if deregister is called. +version_added: "1.2" +requirements: [ "boto" ] +author: John Jarvis +options: + action: + description: + - register or deregister the instance + required: true + + instance_id: + description: + - EC2 Instance ID + + elb_names: + description: + - List of ELB names, required for registration + required: false + default: None + ec2_secret_key: + description: + - AWS Secret API key + required: false + default: None + ec2_access_key: + description: + - AWS Access API key + required: false + default: None + +""" + +EXAMPLES = """ +# basic pre_task and post_task example +pre_tasks: + - name: Gathering ec2 facts + ec2_facts: + - name: Instance De-register + local_action: ec2_elb + args: + instance_id: "{{ ansible_ec2_instance_id }}" + action: 'deregister' +roles: + - myrole +post_tasks: + - name: Instance Register + local_action: ec2_elb + args: + instance_id: "{{ ansible_ec2_instance_id }}" + elb_names: "{{ ec2_elbs }}" + action: 'register' +""" + +import time +import sys +import os + +try: + import boto +except ImportError: + print "failed=True msg='boto required for this module'" + sys.exit(1) + + +class ElbManager: + """Handles EC2 instance ELB registration and deregistration""" + + def __init__(self, module, instance_id=None, elb_names=None, + ec2_access_key=None, ec2_secret_key=None): + self.ec2_access_key = ec2_access_key + self.ec2_secret_key = ec2_secret_key + self.module = module + self.instance_id = instance_id + self.lbs = self._get_instance_lbs(elb_names) + + def deregister(self): + """Deregister the instance from all ELBs and wait for the ELB + to report it out-of-service""" + + for lb in self.lbs: + lb.deregister_instances([self.instance_id]) + self._await_elb_instance_state(lb, 'OutOfService') + + def register(self): + """Register the instance for all ELBs and wait for the ELB + to report the instance in-service""" + + for lb in self.lbs: + lb.register_instances([self.instance_id]) + self._await_elb_instance_state(lb, 'InService') + + def _await_elb_instance_state(self, lb, awaited_state): + """Wait for an ELB to change state + lb: load balancer + awaited_state : state to poll for (string)""" + + while True: + state = lb.get_instance_health([self.instance_id])[0].state + if state == awaited_state: + break + else: + time.sleep(1) + + def _get_instance_lbs(self, elb_names=None): + """Returns a list of ELBs attached to self.instance_id""" + + try: + elb = boto.connect_elb(self.ec2_access_key, self.ec2_secret_key) + except boto.exception.NoAuthHandlerFound, e: + self.module.fail_json(msg=str(e)) + elbs = elb.get_all_load_balancers() + + if elb_names: + lbs = sorted(lb for lb in elbs if lb.name in elb_names) + else: + lbs = [] + for lb in elbs: + for info in lb.instances: + if self.instance_id == info.id: + lbs.append(lb) + return lbs + + +def main(): + + module = AnsibleModule( + argument_spec=dict( + action={'required': True, + 'choices': ['register', 'deregister']}, + instance_id={'required': True}, + elb_names={'default': None, 'required': False}, + ec2_secret_key={'default': None, 'aliases': ['EC2_SECRET_KEY']}, + ec2_access_key={'default': None, 'aliases': ['EC2_ACCESS_KEY']} + ) + ) + + ec2_secret_key = module.params['ec2_secret_key'] + ec2_access_key = module.params['ec2_access_key'] + elb_names = module.params['elb_names'] + + if module.params['action'] == 'register' and 'elb_names' not in module.params: + module.fail_json(msg="ELBs are required for registration") + + if not ec2_secret_key and 'EC2_SECRET_KEY' in os.environ: + ec2_secret_key = os.environ['EC2_SECRET_KEY'] + if not ec2_access_key and 'EC2_ACCESS_KEY' in os.environ: + ec2_access_key = os.environ['EC2_ACCESS_KEY'] + + instance_id = module.params['instance_id'] + elb_man = ElbManager(module, instance_id, elb_names, ec2_access_key, + ec2_secret_key) + + if module.params['action'] == 'register': + elb_man.register() + elif module.params['action'] == 'deregister': + elb_man.deregister() + + ansible_facts = {'ec2_elbs': [lb.name for lb in elb_man.lbs]} + ec2_facts_result = dict(changed=False, ansible_facts=ansible_facts) + + module.exit_json(**ec2_facts_result) + +# this is magic, see lib/ansible/module_common.py +#<> + +main() From 70b61f13e4b974571f370c51c554efe53197184f Mon Sep 17 00:00:00 2001 From: John Jarvis Date: Tue, 30 Apr 2013 09:00:10 -0400 Subject: [PATCH 2/5] marking the state changed only when there are ELBs to work on --- library/cloud/ec2_elb | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/library/cloud/ec2_elb b/library/cloud/ec2_elb index 7c1c7d870b..1302cf07bb 100644 --- a/library/cloud/ec2_elb +++ b/library/cloud/ec2_elb @@ -17,9 +17,9 @@ DOCUMENTATION = """ --- module: ec2_elb -short_description: Registers and Deregisters instances from EC2 ELB(s) +short_description: De-registers or registers instances from EC2 ELB(s) description: - - This module deregisters or registers an AWS EC2 instance from the ELB(s) + - This module de-registers or registers an AWS EC2 instance from the ELB(s) that it belongs to. Returns fact "elb_ec2" which is a list of elbs attached the instance if deregister is called. @@ -35,10 +35,11 @@ options: instance_id: description: - EC2 Instance ID + required: true elb_names: description: - - List of ELB names, required for registration + - List of ELB names, required for registration. The elb_ec2 fact should be used if there was a previous de-register. required: false default: None ec2_secret_key: @@ -87,7 +88,7 @@ except ImportError: class ElbManager: - """Handles EC2 instance ELB registration and deregistration""" + """Handles EC2 instance ELB registration and de-registration""" def __init__(self, module, instance_id=None, elb_names=None, ec2_access_key=None, ec2_secret_key=None): @@ -96,9 +97,15 @@ class ElbManager: self.module = module self.instance_id = instance_id self.lbs = self._get_instance_lbs(elb_names) + # if there are no ELBs to operate on + # there will be no changes made + if len(self.lbs) > 0: + self.changed = True + else: + self.changed = False def deregister(self): - """Deregister the instance from all ELBs and wait for the ELB + """De-register the instance from all ELBs and wait for the ELB to report it out-of-service""" for lb in self.lbs: @@ -180,7 +187,7 @@ def main(): elb_man.deregister() ansible_facts = {'ec2_elbs': [lb.name for lb in elb_man.lbs]} - ec2_facts_result = dict(changed=False, ansible_facts=ansible_facts) + ec2_facts_result = dict(changed=elb_man.changed, ansible_facts=ansible_facts) module.exit_json(**ec2_facts_result) From e625155cd5a3a6ab9a0ee3d57677e4b7ca595f7e Mon Sep 17 00:00:00 2001 From: John Jarvis Date: Tue, 30 Apr 2013 09:03:31 -0400 Subject: [PATCH 3/5] doc update --- library/cloud/ec2_elb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/cloud/ec2_elb b/library/cloud/ec2_elb index 1302cf07bb..8259bf3e2e 100644 --- a/library/cloud/ec2_elb +++ b/library/cloud/ec2_elb @@ -21,8 +21,9 @@ short_description: De-registers or registers instances from EC2 ELB(s) description: - This module de-registers or registers an AWS EC2 instance from the ELB(s) that it belongs to. - Returns fact "elb_ec2" which is a list of elbs attached the instance + - Returns fact "elb_ec2" which is a list of elbs attached the instance if deregister is called. + - Will be marked changed when called only if there are ELBs found to operate on. version_added: "1.2" requirements: [ "boto" ] author: John Jarvis From a45b31290769c16fc24e4fbfb4956aa7c65b8bb9 Mon Sep 17 00:00:00 2001 From: John Jarvis Date: Thu, 2 May 2013 21:29:36 -0400 Subject: [PATCH 4/5] changing arg name for consistency, absent/present for registration --- library/cloud/ec2_elb | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/library/cloud/ec2_elb b/library/cloud/ec2_elb index 8259bf3e2e..d656f1b27a 100644 --- a/library/cloud/ec2_elb +++ b/library/cloud/ec2_elb @@ -28,7 +28,7 @@ version_added: "1.2" requirements: [ "boto" ] author: John Jarvis options: - action: + state: description: - register or deregister the instance required: true @@ -38,7 +38,7 @@ options: - EC2 Instance ID required: true - elb_names: + ec2_elbs: description: - List of ELB names, required for registration. The elb_ec2 fact should be used if there was a previous de-register. required: false @@ -65,7 +65,7 @@ pre_tasks: local_action: ec2_elb args: instance_id: "{{ ansible_ec2_instance_id }}" - action: 'deregister' + state: 'absent' roles: - myrole post_tasks: @@ -73,8 +73,8 @@ post_tasks: local_action: ec2_elb args: instance_id: "{{ ansible_ec2_instance_id }}" - elb_names: "{{ ec2_elbs }}" - action: 'register' + ec2_elbs: "{{ ec2_elbs }}" + state: 'present' """ import time @@ -91,13 +91,13 @@ except ImportError: class ElbManager: """Handles EC2 instance ELB registration and de-registration""" - def __init__(self, module, instance_id=None, elb_names=None, + def __init__(self, module, instance_id=None, ec2_elbs=None, ec2_access_key=None, ec2_secret_key=None): self.ec2_access_key = ec2_access_key self.ec2_secret_key = ec2_secret_key self.module = module self.instance_id = instance_id - self.lbs = self._get_instance_lbs(elb_names) + self.lbs = self._get_instance_lbs(ec2_elbs) # if there are no ELBs to operate on # there will be no changes made if len(self.lbs) > 0: @@ -133,7 +133,7 @@ class ElbManager: else: time.sleep(1) - def _get_instance_lbs(self, elb_names=None): + def _get_instance_lbs(self, ec2_elbs=None): """Returns a list of ELBs attached to self.instance_id""" try: @@ -142,8 +142,8 @@ class ElbManager: self.module.fail_json(msg=str(e)) elbs = elb.get_all_load_balancers() - if elb_names: - lbs = sorted(lb for lb in elbs if lb.name in elb_names) + if ec2_elbs: + lbs = sorted(lb for lb in elbs if lb.name in ec2_elbs) else: lbs = [] for lb in elbs: @@ -157,10 +157,10 @@ def main(): module = AnsibleModule( argument_spec=dict( - action={'required': True, - 'choices': ['register', 'deregister']}, + state={'required': True, + 'choices': ['present', 'absent']}, instance_id={'required': True}, - elb_names={'default': None, 'required': False}, + ec2_elbs={'default': None, 'required': False}, ec2_secret_key={'default': None, 'aliases': ['EC2_SECRET_KEY']}, ec2_access_key={'default': None, 'aliases': ['EC2_ACCESS_KEY']} ) @@ -168,9 +168,9 @@ def main(): ec2_secret_key = module.params['ec2_secret_key'] ec2_access_key = module.params['ec2_access_key'] - elb_names = module.params['elb_names'] + ec2_elbs = module.params['ec2_elbs'] - if module.params['action'] == 'register' and 'elb_names' not in module.params: + if module.params['state'] == 'register' and 'ec2_elbs' not in module.params: module.fail_json(msg="ELBs are required for registration") if not ec2_secret_key and 'EC2_SECRET_KEY' in os.environ: @@ -179,12 +179,12 @@ def main(): ec2_access_key = os.environ['EC2_ACCESS_KEY'] instance_id = module.params['instance_id'] - elb_man = ElbManager(module, instance_id, elb_names, ec2_access_key, + elb_man = ElbManager(module, instance_id, ec2_elbs, ec2_access_key, ec2_secret_key) - if module.params['action'] == 'register': + if module.params['state'] == 'register': elb_man.register() - elif module.params['action'] == 'deregister': + elif module.params['state'] == 'deregister': elb_man.deregister() ansible_facts = {'ec2_elbs': [lb.name for lb in elb_man.lbs]} From e5d9dad205dc44152ca4613665888b2e11c64ca8 Mon Sep 17 00:00:00 2001 From: John Jarvis Date: Fri, 3 May 2013 06:55:06 -0400 Subject: [PATCH 5/5] Parameter and variable naming issues --- library/cloud/ec2_elb | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/library/cloud/ec2_elb b/library/cloud/ec2_elb index d656f1b27a..ca776c4edf 100644 --- a/library/cloud/ec2_elb +++ b/library/cloud/ec2_elb @@ -21,8 +21,8 @@ short_description: De-registers or registers instances from EC2 ELB(s) description: - This module de-registers or registers an AWS EC2 instance from the ELB(s) that it belongs to. - - Returns fact "elb_ec2" which is a list of elbs attached the instance - if deregister is called. + - Returns fact "ec2_elbs" which is a list of elbs attached to the instance + if state=absent is passed as an argument. - Will be marked changed when called only if there are ELBs found to operate on. version_added: "1.2" requirements: [ "boto" ] @@ -40,7 +40,7 @@ options: ec2_elbs: description: - - List of ELB names, required for registration. The elb_ec2 fact should be used if there was a previous de-register. + - List of ELB names, required for registration. The ec2_elbs fact should be used if there was a previous de-register. required: false default: None ec2_secret_key: @@ -134,7 +134,10 @@ class ElbManager: time.sleep(1) def _get_instance_lbs(self, ec2_elbs=None): - """Returns a list of ELBs attached to self.instance_id""" + """Returns a list of ELBs attached to self.instance_id + ec2_elbs: an optional list of elb names that will be used + for elb lookup instead of returning what elbs + are attached to self.instance_id""" try: elb = boto.connect_elb(self.ec2_access_key, self.ec2_secret_key) @@ -170,7 +173,7 @@ def main(): ec2_access_key = module.params['ec2_access_key'] ec2_elbs = module.params['ec2_elbs'] - if module.params['state'] == 'register' and 'ec2_elbs' not in module.params: + if module.params['state'] == 'present' and 'ec2_elbs' not in module.params: module.fail_json(msg="ELBs are required for registration") if not ec2_secret_key and 'EC2_SECRET_KEY' in os.environ: @@ -182,9 +185,9 @@ def main(): elb_man = ElbManager(module, instance_id, ec2_elbs, ec2_access_key, ec2_secret_key) - if module.params['state'] == 'register': + if module.params['state'] == 'present': elb_man.register() - elif module.params['state'] == 'deregister': + elif module.params['state'] == 'absent': elb_man.deregister() ansible_facts = {'ec2_elbs': [lb.name for lb in elb_man.lbs]}