#!/usr/bin/env python # -*- coding: utf-8 -*- # (c) 2013, Matt Hite # # 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: bigip_node short_description: "Manages F5 BIG-IP LTM pool members" description: - "Manages F5 BIG-IP LTM pool members via iControl SOAP API" version_added: "TBD" author: Matt Hite notes: - "Requires BIG-IP software version >= 11" - "F5 developed module 'bigsuds' required (see http://devcentral.f5.com)" - "Best run as a local_action in your playbook" requirements: - bigsuds options: server: description: - BIG-IP host required: true default: null choices: [] aliases: [] user: description: - BIG-IP username required: true default: null choices: [] aliases: [] password: description: - BIG-IP password required: true default: null choices: [] aliases: [] state: description: - Pool member state required: true default: present choices: ['present', 'absent'] aliases: [] partition: description: - Partition required: false default: 'Common' choices: [] aliases: [] name: description - "Node name" required: false default: null choices: [] host: description: - "Node IP. Required when state=present. Ignored when state=absent." required: true default: null choices: [] aliases: ['address'] description: description: - "Optional node description." required: false default: null choices: [] ''' EXAMPLES = ''' TBD TBD TBD TBD ''' try: import bigsuds except ImportError: bigsuds_found = False else: bigsuds_found = True import traceback # =========================================== # bigip_pool_member module specific support methods. # def bigip_api(bigip, user, password): api = bigsuds.BIGIP(hostname=bigip, username=user, password=password) return api def node_exists(api, address): # hack to determine if node exists result = False try: api.LocalLB.NodeAddressV2.get_object_status(nodes=[address]) result = True except bigsuds.OperationFailed, e: if "was not found" in str(e): result = False else: # genuine exception raise return result def create_node_address(api, address, name): try: api.LocalLB.NodeAddressV2.create(nodes=[name], addresses=[address], limits=[0]) return True, "" except bigsuds.OperationFailed, e: if "invalid node address" in str(e) and "already exists" in str(e): return (False, "The referenced IP address is already in use.") else: # genuine exception raise def get_node_address(api, name): return api.LocalLB.NodeAddressV2.get_address(nodes=[name])[0] def delete_node_address(api, address): result = False try: api.LocalLB.NodeAddressV2.delete_node_address(nodes=[address]) result = True except bigsuds.OperationFailed, e: if "is referenced by a member of pool" in str(e): result = False else: # genuine exception raise return result def set_node_description(api, name, description): api.LocalLB.NodeAddressV2.set_description(nodes=[name], descriptions=[description]) def get_node_description(api, name): return api.LocalLB.NodeAddressV2.get_description(nodes=[name])[0] def main(): module = AnsibleModule( argument_spec = dict( server = dict(type='str', required=True), user = dict(type='str', required=True), password = dict(type='str', required=True), state = dict(type='str', default='present', choices=['present', 'absent']), partition = dict(type='str', default='Common'), name = dict(type='str', required=True), host = dict(type='str', aliases=['address', 'ip']), description = dict(type='str', default='') ), supports_check_mode=True ) if not bigsuds_found: module.fail_json(msg="the python bigsuds module is required") server = module.params['server'] user = module.params['user'] password = module.params['password'] state = module.params['state'] partition = module.params['partition'] host = module.params['host'] name = module.params['name'] address = "/%s/%s" % (partition, name) description = module.params['description'] # sanity check user supplied values if state == 'present' and host is None: module.fail_json(msg='host required when state=present') try: api = bigip_api(server, user, password) result = {'changed': False} # default if state == 'absent': if node_exists(api, address): if not module.check_mode: deleted = delete_node_address(api, address) if not deleted: module.fail_json(msg="unable to delete: node referenced by pool") else: result = {'changed': True} else: result = {'changed': True} elif state == 'present': if not node_exists(api, address): if not module.check_mode: if not node_exists(api, address): res, desc = create_node_address(api, address=host, name=address) if not res: module.fail_json(msg=desc) result = {'changed': True} else: # node exists -- potentially modify attributes if host is not None: current_address = get_node_address(api, address) if current_address != host: module.fail_json(msg="""Changing the node address is not supported by the API, delete and recreate the node.""") current_descrip = get_node_description(api, address) if current_descrip != description: if not module.check_mode: set_node_description(api, address, description) result = {'changed': True} except Exception, e: traceback.print_exc(file=sys.stdout) module.fail_json(msg="received exception: %s" % e) module.exit_json(**result) # include magic from lib/ansible/module_common.py #<> main()