diff --git a/lib/ansible/modules/extras/network/f5/bigip_pool_member.py b/lib/ansible/modules/extras/network/f5/bigip_pool_member.py index dc6b71fc19..99fc515ede 100644 --- a/lib/ansible/modules/extras/network/f5/bigip_pool_member.py +++ b/lib/ansible/modules/extras/network/f5/bigip_pool_member.py @@ -1,6 +1,6 @@ #!/usr/bin/python # -*- coding: utf-8 -*- - +# # (c) 2013, Matt Hite # # This file is part of Ansible @@ -21,229 +21,212 @@ DOCUMENTATION = ''' --- module: bigip_pool_member -short_description: "Manages F5 BIG-IP LTM pool members" +short_description: Manages F5 BIG-IP LTM pool members description: - - "Manages F5 BIG-IP LTM pool members via iControl SOAP API" -version_added: "1.4" + - Manages F5 BIG-IP LTM pool members via iControl SOAP API +version_added: 1.4 author: - - Matt Hite (@mhite) - - Tim Rupp (@caphrim007) + - Matt Hite (@mhite) + - Tim Rupp (@caphrim007) 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" - - "Supersedes bigip_pool for managing pool members" - + - 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 + - Supersedes bigip_pool for managing pool members requirements: - - bigsuds + - bigsuds options: - server: - description: - - BIG-IP host - required: true - server_port: - description: - - BIG-IP server port - required: false - default: 443 - version_added: "2.2" - user: - description: - - BIG-IP username - required: true - default: null - choices: [] - aliases: [] - password: - description: - - BIG-IP password - required: true - default: null - choices: [] - aliases: [] - validate_certs: - description: - - If C(no), SSL certificates will not be validated. This should only be used - on personally controlled sites. Prior to 2.0, this module would always - validate on python >= 2.7.9 and never validate on python <= 2.7.8 - required: false - default: 'yes' - choices: ['yes', 'no'] - version_added: 1.9.1 - state: - description: - - Pool member state - required: true - default: present - choices: ['present', 'absent'] - aliases: [] - session_state: - description: - - Set new session availability status for pool member - version_added: "1.9" - required: false - default: null - choices: ['enabled', 'disabled'] - aliases: [] - monitor_state: - description: - - Set monitor availability status for pool member - version_added: "1.9" - required: false - default: null - choices: ['enabled', 'disabled'] - aliases: [] - pool: - description: - - Pool name. This pool must exist. - required: true - default: null - choices: [] - aliases: [] - partition: - description: - - Partition - required: false - default: 'Common' - choices: [] - aliases: [] - host: - description: - - Pool member IP - required: true - default: null - choices: [] - aliases: ['address', 'name'] - port: - description: - - Pool member port - required: true - default: null - choices: [] - aliases: [] - connection_limit: - description: - - Pool member connection limit. Setting this to 0 disables the limit. - required: false - default: null - choices: [] - aliases: [] + server: description: - description: - - Pool member description - required: false - default: null - choices: [] - aliases: [] - rate_limit: - description: - - Pool member rate limit (connections-per-second). Setting this to 0 disables the limit. - required: false - default: null - choices: [] - aliases: [] - ratio: - description: - - Pool member ratio weight. Valid values range from 1 through 100. New pool members -- unless overriden with this value -- default to 1. - required: false - default: null - preserve_node: - description: - - When state is absent and the pool member is no longer referenced in other pools, the default behavior removes the unused node object. Setting this to 'yes' disables this behavior. - required: false - default: 'no' - choices: ['yes', 'no'] - version_added: 2.1 + - BIG-IP host + required: true + server_port: + description: + - BIG-IP server port + required: false + default: 443 + version_added: "2.2" + user: + description: + - BIG-IP username + required: true + password: + description: + - BIG-IP password + required: true + validate_certs: + description: + - If C(no), SSL certificates will not be validated. This should only be used + on personally controlled sites. Prior to 2.0, this module would always + validate on python >= 2.7.9 and never validate on python <= 2.7.8 + required: false + default: 'yes' + choices: + - yes + - no + version_added: 2.0 + state: + description: + - Pool member state + required: true + default: present + choices: + - present + - absent + session_state: + description: + - Set new session availability status for pool member + version_added: 2.0 + required: false + default: null + choices: + - enabled + - disabled + monitor_state: + description: + - Set monitor availability status for pool member + version_added: 2.0 + required: false + default: null + choices: + - enabled + - disabled + pool: + description: + - Pool name. This pool must exist. + required: true + partition: + description: + - Partition + required: false + default: 'Common' + host: + description: + - Pool member IP + required: true + aliases: + - address + - name + port: + description: + - Pool member port + required: true + connection_limit: + description: + - Pool member connection limit. Setting this to 0 disables the limit. + required: false + default: null + description: + description: + - Pool member description + required: false + default: null + rate_limit: + description: + - Pool member rate limit (connections-per-second). Setting this to 0 + disables the limit. + required: false + default: null + ratio: + description: + - Pool member ratio weight. Valid values range from 1 through 100. + New pool members -- unless overriden with this value -- default + to 1. + required: false + default: null + preserve_node: + description: + - When state is absent and the pool member is no longer referenced + in other pools, the default behavior removes the unused node + o bject. Setting this to 'yes' disables this behavior. + required: false + default: 'no' + choices: + - yes + - no + version_added: 2.1 ''' EXAMPLES = ''' +- name: Add pool member + bigip_pool_member: + server: "lb.mydomain.com" + user: "admin" + password: "secret" + state: "present" + pool: "my-pool" + partition: "Common" + host: "{{ ansible_default_ipv4["address"] }}" + port: 80 + description: "web server" + connection_limit: 100 + rate_limit: 50 + ratio: 2 + delegate_to: localhost -## playbook task examples: +- name: Modify pool member ratio and description + bigip_pool_member: + server: "lb.mydomain.com" + user: "admin" + password: "secret" + state: "present" + pool: "my-pool" + partition: "Common" + host: "{{ ansible_default_ipv4["address"] }}" + port: 80 + ratio: 1 + description: "nginx server" + delegate_to: localhost ---- -# file bigip-test.yml -# ... -- hosts: bigip-test - tasks: - - name: Add pool member - local_action: > - bigip_pool_member - server=lb.mydomain.com - user=admin - password=mysecret - state=present - pool=matthite-pool - partition=matthite - host="{{ ansible_default_ipv4["address"] }}" - port=80 - description="web server" - connection_limit=100 - rate_limit=50 - ratio=2 - - - name: Modify pool member ratio and description - local_action: > - bigip_pool_member - server=lb.mydomain.com - user=admin - password=mysecret - state=present - pool=matthite-pool - partition=matthite - host="{{ ansible_default_ipv4["address"] }}" - port=80 - ratio=1 - description="nginx server" - - - name: Remove pool member from pool - local_action: > - bigip_pool_member - server=lb.mydomain.com - user=admin - password=mysecret - state=absent - pool=matthite-pool - partition=matthite - host="{{ ansible_default_ipv4["address"] }}" - port=80 +- name: Remove pool member from pool + bigip_pool_member: + server: "lb.mydomain.com" + user: "admin" + password: "secret" + state: "absent" + pool: "my-pool" + partition: "Common" + host: "{{ ansible_default_ipv4["address"] }}" + port: 80 + delegate_to: localhost - # The BIG-IP GUI doesn't map directly to the API calls for "Pool -> - # Members -> State". The following states map to API monitor - # and session states. - # - # Enabled (all traffic allowed): - # monitor_state=enabled, session_state=enabled - # Disabled (only persistent or active connections allowed): - # monitor_state=enabled, session_state=disabled - # Forced offline (only active connections allowed): - # monitor_state=disabled, session_state=disabled - # - # See https://devcentral.f5.com/questions/icontrol-equivalent-call-for-b-node-down - - - name: Force pool member offline - local_action: > - bigip_pool_member - server=lb.mydomain.com - user=admin - password=mysecret - state=present - session_state=disabled - monitor_state=disabled - pool=matthite-pool - partition=matthite - host="{{ ansible_default_ipv4["address"] }}" - port=80 +# The BIG-IP GUI doesn't map directly to the API calls for "Pool -> +# Members -> State". The following states map to API monitor +# and session states. +# +# Enabled (all traffic allowed): +# monitor_state=enabled, session_state=enabled +# Disabled (only persistent or active connections allowed): +# monitor_state=enabled, session_state=disabled +# Forced offline (only active connections allowed): +# monitor_state=disabled, session_state=disabled +# +# See https://devcentral.f5.com/questions/icontrol-equivalent-call-for-b-node-down +- name: Force pool member offline + bigip_pool_member: + server: "lb.mydomain.com" + user: "admin" + password: "secret" + state: "present" + session_state: "disabled" + monitor_state: "disabled" + pool: "my-pool" + partition: "Common" + host: "{{ ansible_default_ipv4["address"] }}" + port: 80 + delegate_to: localhost ''' + def pool_exists(api, pool): # hack to determine if pool exists result = False try: api.LocalLB.Pool.get_object_status(pool_names=[pool]) result = True - except bigsuds.OperationFailed, e: + except bigsuds.OperationFailed as e: if "was not found" in str(e): result = False else: @@ -251,6 +234,7 @@ def pool_exists(api, pool): raise return result + def member_exists(api, pool, address, port): # hack to determine if member exists result = False @@ -259,7 +243,7 @@ def member_exists(api, pool, address, port): api.LocalLB.Pool.get_member_object_status(pool_names=[pool], members=[members]) result = True - except bigsuds.OperationFailed, e: + except bigsuds.OperationFailed as e: if "was not found" in str(e): result = False else: @@ -267,12 +251,13 @@ def member_exists(api, pool, address, port): raise return result + def delete_node_address(api, address): result = False try: api.LocalLB.NodeAddressV2.delete_node_address(nodes=[address]) result = True - except bigsuds.OperationFailed, e: + except bigsuds.OperationFailed as e: if "is referenced by a member of pool" in str(e): result = False else: @@ -280,96 +265,157 @@ def delete_node_address(api, address): raise return result + def remove_pool_member(api, pool, address, port): members = [{'address': address, 'port': port}] - api.LocalLB.Pool.remove_member_v2(pool_names=[pool], members=[members]) + api.LocalLB.Pool.remove_member_v2( + pool_names=[pool], + members=[members] + ) + def add_pool_member(api, pool, address, port): members = [{'address': address, 'port': port}] - api.LocalLB.Pool.add_member_v2(pool_names=[pool], members=[members]) + api.LocalLB.Pool.add_member_v2( + pool_names=[pool], + members=[members] + ) + def get_connection_limit(api, pool, address, port): members = [{'address': address, 'port': port}] - result = api.LocalLB.Pool.get_member_connection_limit(pool_names=[pool], members=[members])[0][0] + result = api.LocalLB.Pool.get_member_connection_limit( + pool_names=[pool], + members=[members] + )[0][0] return result + def set_connection_limit(api, pool, address, port, limit): members = [{'address': address, 'port': port}] - api.LocalLB.Pool.set_member_connection_limit(pool_names=[pool], members=[members], limits=[[limit]]) + api.LocalLB.Pool.set_member_connection_limit( + pool_names=[pool], + members=[members], + limits=[[limit]] + ) + def get_description(api, pool, address, port): members = [{'address': address, 'port': port}] - result = api.LocalLB.Pool.get_member_description(pool_names=[pool], members=[members])[0][0] + result = api.LocalLB.Pool.get_member_description( + pool_names=[pool], + members=[members] + )[0][0] return result + def set_description(api, pool, address, port, description): members = [{'address': address, 'port': port}] - api.LocalLB.Pool.set_member_description(pool_names=[pool], members=[members], descriptions=[[description]]) + api.LocalLB.Pool.set_member_description( + pool_names=[pool], + members=[members], + descriptions=[[description]] + ) + def get_rate_limit(api, pool, address, port): members = [{'address': address, 'port': port}] - result = api.LocalLB.Pool.get_member_rate_limit(pool_names=[pool], members=[members])[0][0] + result = api.LocalLB.Pool.get_member_rate_limit( + pool_names=[pool], + members=[members] + )[0][0] return result + def set_rate_limit(api, pool, address, port, limit): members = [{'address': address, 'port': port}] - api.LocalLB.Pool.set_member_rate_limit(pool_names=[pool], members=[members], limits=[[limit]]) + api.LocalLB.Pool.set_member_rate_limit( + pool_names=[pool], + members=[members], + limits=[[limit]] + ) + def get_ratio(api, pool, address, port): members = [{'address': address, 'port': port}] - result = api.LocalLB.Pool.get_member_ratio(pool_names=[pool], members=[members])[0][0] + result = api.LocalLB.Pool.get_member_ratio( + pool_names=[pool], + members=[members] + )[0][0] return result + def set_ratio(api, pool, address, port, ratio): members = [{'address': address, 'port': port}] - api.LocalLB.Pool.set_member_ratio(pool_names=[pool], members=[members], ratios=[[ratio]]) + api.LocalLB.Pool.set_member_ratio( + pool_names=[pool], + members=[members], + ratios=[[ratio]] + ) + def set_member_session_enabled_state(api, pool, address, port, session_state): members = [{'address': address, 'port': port}] session_state = ["STATE_%s" % session_state.strip().upper()] - api.LocalLB.Pool.set_member_session_enabled_state(pool_names=[pool], members=[members], session_states=[session_state]) + api.LocalLB.Pool.set_member_session_enabled_state( + pool_names=[pool], + members=[members], + session_states=[session_state] + ) + def get_member_session_status(api, pool, address, port): members = [{'address': address, 'port': port}] - result = api.LocalLB.Pool.get_member_session_status(pool_names=[pool], members=[members])[0][0] + result = api.LocalLB.Pool.get_member_session_status( + pool_names=[pool], + members=[members] + )[0][0] result = result.split("SESSION_STATUS_")[-1].lower() return result + def set_member_monitor_state(api, pool, address, port, monitor_state): members = [{'address': address, 'port': port}] monitor_state = ["STATE_%s" % monitor_state.strip().upper()] - api.LocalLB.Pool.set_member_monitor_state(pool_names=[pool], members=[members], monitor_states=[monitor_state]) + api.LocalLB.Pool.set_member_monitor_state( + pool_names=[pool], + members=[members], + monitor_states=[monitor_state] + ) + def get_member_monitor_status(api, pool, address, port): members = [{'address': address, 'port': port}] - result = api.LocalLB.Pool.get_member_monitor_status(pool_names=[pool], members=[members])[0][0] + result = api.LocalLB.Pool.get_member_monitor_status( + pool_names=[pool], + members=[members] + )[0][0] result = result.split("MONITOR_STATUS_")[-1].lower() return result + def main(): argument_spec = f5_argument_spec() - argument_spec.update(dict( - session_state = dict(type='str', choices=['enabled', 'disabled']), - monitor_state = dict(type='str', choices=['enabled', 'disabled']), - pool = dict(type='str', required=True), - host = dict(type='str', required=True, aliases=['address', 'name']), - port = dict(type='int', required=True), - connection_limit = dict(type='int'), - description = dict(type='str'), - rate_limit = dict(type='int'), - ratio = dict(type='int'), - preserve_node = dict(type='bool', default=False) - ) + + meta_args = dict( + session_state=dict(type='str', choices=['enabled', 'disabled']), + monitor_state=dict(type='str', choices=['enabled', 'disabled']), + pool=dict(type='str', required=True), + host=dict(type='str', required=True, aliases=['address', 'name']), + port=dict(type='int', required=True), + connection_limit=dict(type='int'), + description=dict(type='str'), + rate_limit=dict(type='int'), + ratio=dict(type='int'), + preserve_node=dict(type='bool', default=False) ) + argument_spec.update(meta_args) module = AnsibleModule( - argument_spec = argument_spec, + argument_spec=argument_spec, supports_check_mode=True ) - if not bigsuds_found: - module.fail_json(msg="the python bigsuds module is required") - if module.params['validate_certs']: import ssl if not hasattr(ssl, 'SSLContext'): @@ -395,9 +441,6 @@ def main(): port = module.params['port'] preserve_node = module.params['preserve_node'] - - # sanity check user supplied values - if (host and port is None) or (port is not None and not host): module.fail_json(msg="both host and port must be supplied") @@ -478,12 +521,11 @@ def main(): set_member_monitor_state(api, pool, address, port, monitor_state) result = {'changed': True} - except Exception, e: + except Exception as e: module.fail_json(msg="received exception: %s" % e) module.exit_json(**result) -# import module snippets from ansible.module_utils.basic import * from ansible.module_utils.f5 import *