diff --git a/lib/ansible/modules/extras/network/f5/bigip_monitor_tcp.py b/lib/ansible/modules/extras/network/f5/bigip_monitor_tcp.py index e0a7d48def..f7443e2878 100644 --- a/lib/ansible/modules/extras/network/f5/bigip_monitor_tcp.py +++ b/lib/ansible/modules/extras/network/f5/bigip_monitor_tcp.py @@ -23,171 +23,176 @@ DOCUMENTATION = ''' module: bigip_monitor_tcp short_description: "Manages F5 BIG-IP LTM tcp monitors" description: - - "Manages F5 BIG-IP LTM tcp monitors via iControl SOAP API" + - "Manages F5 BIG-IP LTM tcp monitors via iControl SOAP API" version_added: "1.4" author: - - Serge van Ginderachter (@srvg) - - Tim Rupp (@caphrim007) + - Serge van Ginderachter (@srvg) + - 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" - - "Monitor API documentation: https://devcentral.f5.com/wiki/iControl.LocalLB__Monitor.ashx" + - "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" + - "Monitor API documentation: https://devcentral.f5.com/wiki/iControl.LocalLB__Monitor.ashx" requirements: - - bigsuds + - bigsuds options: - server: - description: - - BIG-IP host - required: true - default: null - server_port: - description: - - BIG-IP server port - required: false - default: 443 - version_added: "2.2" - user: - description: - - BIG-IP username - required: true - default: null - password: - description: - - BIG-IP password - required: true - default: null - 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: - - Monitor state - required: false - default: 'present' - choices: ['present', 'absent'] - name: - description: - - Monitor name - required: true - default: null - aliases: ['monitor'] - partition: - description: - - Partition for the monitor - required: false - default: 'Common' - type: - description: - - The template type of this monitor template - required: false - default: 'tcp' - choices: [ 'TTYPE_TCP', 'TTYPE_TCP_ECHO', 'TTYPE_TCP_HALF_OPEN'] - parent: - description: - - The parent template of this monitor template - required: false - default: 'tcp' - choices: [ 'tcp', 'tcp_echo', 'tcp_half_open'] - parent_partition: - description: - - Partition for the parent monitor - required: false - default: 'Common' - send: - description: - - The send string for the monitor call - required: true - default: none - receive: - description: - - The receive string for the monitor call - required: true - default: none - ip: - description: - - IP address part of the ipport definition. The default API setting - is "0.0.0.0". - required: false - default: none - port: - description: - - port address part op the ipport definition. The default API - setting is 0. - required: false - default: none - interval: - description: - - The interval specifying how frequently the monitor instance - of this template will run. By default, this interval is used for up and - down states. The default API setting is 5. - required: false - default: none - timeout: - description: - - The number of seconds in which the node or service must respond to - the monitor request. If the target responds within the set time - period, it is considered up. If the target does not respond within - the set time period, it is considered down. You can change this - number to any number you want, however, it should be 3 times the - interval number of seconds plus 1 second. The default API setting - is 16. - required: false - default: none - time_until_up: - description: - - Specifies the amount of time in seconds after the first successful - response before a node will be marked up. A value of 0 will cause a - node to be marked up immediately after a valid response is received - from the node. The default API setting is 0. - required: false - default: none + server: + description: + - BIG-IP host + required: true + default: null + server_port: + description: + - BIG-IP server port + required: false + default: 443 + version_added: "2.2" + user: + description: + - BIG-IP username + required: true + default: null + password: + description: + - BIG-IP password + required: true + default: null + 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: + - Monitor state + required: false + default: 'present' + choices: + - present + - absent + name: + description: + - Monitor name + required: true + default: null + aliases: + - monitor + partition: + description: + - Partition for the monitor + required: false + default: 'Common' + type: + description: + - The template type of this monitor template + required: false + default: 'tcp' + choices: + - TTYPE_TCP + - TTYPE_TCP_ECHO + - TTYPE_TCP_HALF_OPEN + parent: + description: + - The parent template of this monitor template + required: false + default: 'tcp' + choices: + - tcp + - tcp_echo + - tcp_half_open + parent_partition: + description: + - Partition for the parent monitor + required: false + default: 'Common' + send: + description: + - The send string for the monitor call + required: true + default: none + receive: + description: + - The receive string for the monitor call + required: true + default: none + ip: + description: + - IP address part of the ipport definition. The default API setting + is "0.0.0.0". + required: false + default: none + port: + description: + - Port address part op the ipport definition. The default API + setting is 0. + required: false + default: none + interval: + description: + - The interval specifying how frequently the monitor instance + of this template will run. By default, this interval is used for up and + down states. The default API setting is 5. + required: false + default: none + timeout: + description: + - The number of seconds in which the node or service must respond to + the monitor request. If the target responds within the set time + period, it is considered up. If the target does not respond within + the set time period, it is considered down. You can change this + number to any number you want, however, it should be 3 times the + interval number of seconds plus 1 second. The default API setting + is 16. + required: false + default: none + time_until_up: + description: + - Specifies the amount of time in seconds after the first successful + response before a node will be marked up. A value of 0 will cause a + node to be marked up immediately after a valid response is received + from the node. The default API setting is 0. + required: false + default: none ''' EXAMPLES = ''' +- name: Create TCP Monitor + bigip_monitor_tcp: + state: "present" + server: "lb.mydomain.com" + user: "admin" + password: "secret" + name: "my_tcp_monitor" + type: "tcp" + send: "tcp string to send" + receive: "tcp string to receive" + delegate_to: localhost -- name: BIGIP F5 | Create TCP Monitor - local_action: - module: bigip_monitor_tcp - state: present - server: "{{ f5server }}" - user: "{{ f5user }}" - password: "{{ f5password }}" - name: "{{ item.monitorname }}" - type: tcp - send: "{{ item.send }}" - receive: "{{ item.receive }}" - with_items: f5monitors-tcp -- name: BIGIP F5 | Create TCP half open Monitor - local_action: - module: bigip_monitor_tcp - state: present - server: "{{ f5server }}" - user: "{{ f5user }}" - password: "{{ f5password }}" - name: "{{ item.monitorname }}" - type: tcp - send: "{{ item.send }}" - receive: "{{ item.receive }}" - with_items: f5monitors-halftcp -- name: BIGIP F5 | Remove TCP Monitor - local_action: - module: bigip_monitor_tcp - state: absent - server: "{{ f5server }}" - user: "{{ f5user }}" - password: "{{ f5password }}" - name: "{{ monitorname }}" - with_flattened: - - f5monitors-tcp - - f5monitors-halftcp +- name: Create TCP half open Monitor + bigip_monitor_tcp: + state: "present" + server: "lb.mydomain.com" + user: "admin" + password: "secret" + name: "my_tcp_monitor" + type: "tcp" + send: "tcp string to send" + receive: "http string to receive" + delegate_to: localhost +- name: Remove TCP Monitor + bigip_monitor_tcp: + state: "absent" + server: "lb.mydomain.com" + user: "admin" + password: "secret" + name: "my_tcp_monitor" ''' TEMPLATE_TYPE = DEFAULT_TEMPLATE_TYPE = 'TTYPE_TCP' @@ -196,7 +201,6 @@ DEFAULT_PARENT = DEFAULT_TEMPLATE_TYPE_CHOICE = DEFAULT_TEMPLATE_TYPE.replace('T def check_monitor_exists(module, api, monitor, parent): - # hack to determine if monitor exists result = False try: @@ -206,7 +210,7 @@ def check_monitor_exists(module, api, monitor, parent): result = True else: module.fail_json(msg='Monitor already exists, but has a different type (%s) or parent(%s)' % (ttype, parent)) - except bigsuds.OperationFailed, e: + except bigsuds.OperationFailed as e: if "was not found" in str(e): result = False else: @@ -216,10 +220,15 @@ def check_monitor_exists(module, api, monitor, parent): def create_monitor(api, monitor, template_attributes): - try: - api.LocalLB.Monitor.create_template(templates=[{'template_name': monitor, 'template_type': TEMPLATE_TYPE}], template_attributes=[template_attributes]) - except bigsuds.OperationFailed, e: + api.LocalLB.Monitor.create_template( + templates=[{ + 'template_name': monitor, + 'template_type': TEMPLATE_TYPE + }], + template_attributes=[template_attributes] + ) + except bigsuds.OperationFailed as e: if "already exists" in str(e): return False else: @@ -229,10 +238,9 @@ def create_monitor(api, monitor, template_attributes): def delete_monitor(api, monitor): - try: api.LocalLB.Monitor.delete_template(template_names=[monitor]) - except bigsuds.OperationFailed, e: + except bigsuds.OperationFailed as e: # maybe it was deleted since we checked if "was not found" in str(e): return False @@ -243,41 +251,46 @@ def delete_monitor(api, monitor): def check_string_property(api, monitor, str_property): - try: - return str_property == api.LocalLB.Monitor.get_template_string_property([monitor], [str_property['type']])[0] - except bigsuds.OperationFailed, e: + template_prop = api.LocalLB.Monitor.get_template_string_property( + [monitor], [str_property['type']] + )[0] + return str_property == template_prop + except bigsuds.OperationFailed as e: # happens in check mode if not created yet if "was not found" in str(e): return True else: # genuine exception raise - return True def set_string_property(api, monitor, str_property): - - api.LocalLB.Monitor.set_template_string_property(template_names=[monitor], values=[str_property]) + api.LocalLB.Monitor.set_template_string_property( + template_names=[monitor], + values=[str_property] + ) def check_integer_property(api, monitor, int_property): - try: - return int_property == api.LocalLB.Monitor.get_template_integer_property([monitor], [int_property['type']])[0] - except bigsuds.OperationFailed, e: + return int_property == api.LocalLB.Monitor.get_template_integer_property( + [monitor], [int_property['type']] + )[0] + except bigsuds.OperationFailed as e: # happens in check mode if not created yet if "was not found" in str(e): return True else: # genuine exception raise - return True def set_integer_property(api, monitor, int_property): - - api.LocalLB.Monitor.set_template_integer_property(template_names=[monitor], values=[int_property]) + api.LocalLB.Monitor.set_template_integer_property( + template_names=[monitor], + values=[int_property] + ) def update_monitor_properties(api, module, monitor, template_string_properties, template_integer_properties): @@ -287,6 +300,7 @@ def update_monitor_properties(api, module, monitor, template_string_properties, if not module.check_mode: set_string_property(api, monitor, str_property) changed = True + for int_property in template_integer_properties: if int_property['value'] is not None and not check_integer_property(api, monitor, int_property): if not module.check_mode: @@ -297,56 +311,47 @@ def update_monitor_properties(api, module, monitor, template_string_properties, def get_ipport(api, monitor): - return api.LocalLB.Monitor.get_template_destination(template_names=[monitor])[0] def set_ipport(api, monitor, ipport): - try: - api.LocalLB.Monitor.set_template_destination(template_names=[monitor], destinations=[ipport]) + api.LocalLB.Monitor.set_template_destination( + template_names=[monitor], destinations=[ipport] + ) return True, "" - except bigsuds.OperationFailed, e: + except bigsuds.OperationFailed as e: if "Cannot modify the address type of monitor" in str(e): return False, "Cannot modify the address type of monitor if already assigned to a pool." else: # genuine exception raise -# =========================================== -# main loop -# -# writing a module for other monitor types should -# only need an updated main() (and monitor specific functions) def main(): + argument_spec = f5_argument_spec() - # begin monitor specific stuff - argument_spec=f5_argument_spec() - argument_spec.update(dict( - name = dict(required=True), - type = dict(default=DEFAULT_TEMPLATE_TYPE_CHOICE, choices=TEMPLATE_TYPE_CHOICES), - parent = dict(default=DEFAULT_PARENT), - parent_partition = dict(default='Common'), - send = dict(required=False), - receive = dict(required=False), - ip = dict(required=False), - port = dict(required=False, type='int'), - interval = dict(required=False, type='int'), - timeout = dict(required=False, type='int'), - time_until_up = dict(required=False, type='int', default=0) - ) + meta_args = dict( + name=dict(required=True), + type=dict(default=DEFAULT_TEMPLATE_TYPE_CHOICE, choices=TEMPLATE_TYPE_CHOICES), + parent=dict(default=DEFAULT_PARENT), + parent_partition=dict(default='Common'), + send=dict(required=False), + receive=dict(required=False), + ip=dict(required=False), + port=dict(required=False, type='int'), + interval=dict(required=False, type='int'), + timeout=dict(required=False, type='int'), + time_until_up=dict(required=False, type='int', default=0) ) + 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'): @@ -382,26 +387,27 @@ def main(): api = bigip_api(server, user, password, validate_certs, port=server_port) monitor_exists = check_monitor_exists(module, api, monitor, parent) - # ipport is a special setting - if monitor_exists: # make sure to not update current settings if not asked + if monitor_exists: + # make sure to not update current settings if not asked cur_ipport = get_ipport(api, monitor) if ip is None: ip = cur_ipport['ipport']['address'] if port is None: port = cur_ipport['ipport']['port'] - else: # use API defaults if not defined to create it - if interval is None: + else: + # use API defaults if not defined to create it + if interval is None: interval = 5 - if timeout is None: + if timeout is None: timeout = 16 - if ip is None: + if ip is None: ip = '0.0.0.0' - if port is None: + if port is None: port = 0 - if send is None: + if send is None: send = '' - if receive is None: + if receive is None: receive = '' # define and set address type @@ -414,76 +420,90 @@ def main(): else: address_type = 'ATYPE_UNSET' - ipport = {'address_type': address_type, - 'ipport': {'address': ip, - 'port': port}} + ipport = { + 'address_type': address_type, + 'ipport': { + 'address': ip, + 'port': port + } + } - template_attributes = {'parent_template': parent, - 'interval': interval, - 'timeout': timeout, - 'dest_ipport': ipport, - 'is_read_only': False, - 'is_directly_usable': True} + template_attributes = { + 'parent_template': parent, + 'interval': interval, + 'timeout': timeout, + 'dest_ipport': ipport, + 'is_read_only': False, + 'is_directly_usable': True + } # monitor specific stuff if type == 'TTYPE_TCP': - template_string_properties = [{'type': 'STYPE_SEND', - 'value': send}, - {'type': 'STYPE_RECEIVE', - 'value': receive}] + template_string_properties = [ + { + 'type': 'STYPE_SEND', + 'value': send + }, + { + 'type': 'STYPE_RECEIVE', + 'value': receive + } + ] else: template_string_properties = [] - template_integer_properties = [{'type': 'ITYPE_INTERVAL', - 'value': interval}, - {'type': 'ITYPE_TIMEOUT', - 'value': timeout}, - {'type': 'ITYPE_TIME_UNTIL_UP', - 'value': interval}] + template_integer_properties = [ + { + 'type': 'ITYPE_INTERVAL', + 'value': interval + }, + { + 'type': 'ITYPE_TIMEOUT', + 'value': timeout + }, + { + 'type': 'ITYPE_TIME_UNTIL_UP', + 'value': time_until_up + } + ] # main logic, monitor generic try: result = {'changed': False} # default - if state == 'absent': if monitor_exists: if not module.check_mode: - # possible race condition if same task + # possible race condition if same task # on other node deleted it first result['changed'] |= delete_monitor(api, monitor) else: result['changed'] |= True - - else: # state present - ## check for monitor itself - if not monitor_exists: # create it - if not module.check_mode: + else: + # check for monitor itself + if not monitor_exists: + if not module.check_mode: # again, check changed status here b/c race conditions # if other task already created it result['changed'] |= create_monitor(api, monitor, template_attributes) - else: + else: result['changed'] |= True - ## check for monitor parameters + # check for monitor parameters # whether it already existed, or was just created, now update # the update functions need to check for check mode but # cannot update settings if it doesn't exist which happens in check mode - if monitor_exists and not module.check_mode: - result['changed'] |= update_monitor_properties(api, module, monitor, - template_string_properties, - template_integer_properties) - # else assume nothing changed + result['changed'] |= update_monitor_properties(api, module, monitor, + template_string_properties, + template_integer_properties) # we just have to update the ipport if monitor already exists and it's different if monitor_exists and cur_ipport != ipport: - set_ipport(api, monitor, ipport) + set_ipport(api, monitor, ipport) result['changed'] |= True - #else: monitor doesn't exist (check mode) or ipport is already ok - - - except Exception, e: + # else: monitor doesn't exist (check mode) or ipport is already ok + except Exception as e: module.fail_json(msg="received exception: %s" % e) module.exit_json(**result)