2013-10-25 22:52:48 +02:00
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, serge van Ginderachter <serge@vanginderachter.be>
#
# 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 <http://www.gnu.org/licenses/>.
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 "
version_added : " 1.4 "
2016-06-23 11:33:49 +02:00
author :
- Serge van Ginderachter ( @srvg )
- Tim Rupp ( @caphrim007 )
2013-10-25 22:52:48 +02:00
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 "
2013-11-15 20:44:53 +01:00
- " Monitor API documentation: https://devcentral.f5.com/wiki/iControl.LocalLB__Monitor.ashx "
2013-10-25 22:52:48 +02:00
requirements :
- bigsuds
options :
server :
description :
- BIG - IP host
required : true
default : null
2016-06-23 11:33:49 +02:00
server_port :
description :
- BIG - IP server port
required : false
default : 443
version_added : " 2.2 "
2013-10-25 22:52:48 +02:00
user :
description :
- BIG - IP username
required : true
default : null
password :
description :
- BIG - IP password
required : true
default : null
2015-03-23 22:26:11 +01:00
validate_certs :
description :
- If C ( no ) , SSL certificates will not be validated . This should only be used
2015-12-24 20:57:15 +01:00
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
2015-03-23 22:26:11 +01:00
required : false
default : ' yes '
choices : [ ' yes ' , ' no ' ]
2015-03-26 03:04:44 +01:00
version_added : 2.0
2013-10-25 22:52:48 +02:00
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 :
2014-04-29 16:41:05 +02:00
- port address part op the ipport definition . The default API
2013-10-25 22:52:48 +02:00
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 = '''
2013-10-28 17:13:36 +01:00
- 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
2013-10-25 22:52:48 +02:00
'''
TEMPLATE_TYPE = DEFAULT_TEMPLATE_TYPE = ' TTYPE_TCP '
TEMPLATE_TYPE_CHOICES = [ ' tcp ' , ' tcp_echo ' , ' tcp_half_open ' ]
DEFAULT_PARENT = DEFAULT_TEMPLATE_TYPE_CHOICE = DEFAULT_TEMPLATE_TYPE . replace ( ' TTYPE_ ' , ' ' ) . lower ( )
def check_monitor_exists ( module , api , monitor , parent ) :
# hack to determine if monitor exists
result = False
try :
ttype = api . LocalLB . Monitor . get_template_type ( template_names = [ monitor ] ) [ 0 ]
parent2 = api . LocalLB . Monitor . get_parent_template ( template_names = [ monitor ] ) [ 0 ]
if ttype == TEMPLATE_TYPE and parent == parent2 :
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 :
if " was not found " in str ( e ) :
result = False
else :
# genuine exception
raise
return result
def create_monitor ( api , monitor , template_attributes ) :
2015-06-03 08:22:18 +02:00
try :
2013-10-25 22:52:48 +02:00
api . LocalLB . Monitor . create_template ( templates = [ { ' template_name ' : monitor , ' template_type ' : TEMPLATE_TYPE } ] , template_attributes = [ template_attributes ] )
except bigsuds . OperationFailed , e :
if " already exists " in str ( e ) :
return False
else :
# genuine exception
raise
return True
def delete_monitor ( api , monitor ) :
try :
api . LocalLB . Monitor . delete_template ( template_names = [ monitor ] )
except bigsuds . OperationFailed , e :
# maybe it was deleted since we checked
if " was not found " in str ( e ) :
return False
else :
# genuine exception
raise
return True
def check_string_property ( api , monitor , str_property ) :
2014-06-06 16:48:36 +02:00
try :
return str_property == api . LocalLB . Monitor . get_template_string_property ( [ monitor ] , [ str_property [ ' type ' ] ] ) [ 0 ]
except bigsuds . OperationFailed , e :
# happens in check mode if not created yet
if " was not found " in str ( e ) :
return True
else :
# genuine exception
raise
return True
2013-10-25 22:52:48 +02:00
def set_string_property ( api , monitor , str_property ) :
api . LocalLB . Monitor . set_template_string_property ( template_names = [ monitor ] , values = [ str_property ] )
def check_integer_property ( api , monitor , int_property ) :
2014-06-06 16:48:36 +02:00
try :
return int_property == api . LocalLB . Monitor . get_template_integer_property ( [ monitor ] , [ int_property [ ' type ' ] ] ) [ 0 ]
except bigsuds . OperationFailed , e :
# happens in check mode if not created yet
if " was not found " in str ( e ) :
return True
else :
# genuine exception
raise
return True
2013-10-25 22:52:48 +02:00
def set_integer_property ( api , monitor , int_property ) :
2016-03-11 22:16:58 +01:00
api . LocalLB . Monitor . set_template_integer_property ( template_names = [ monitor ] , values = [ int_property ] )
2013-10-25 22:52:48 +02:00
def update_monitor_properties ( api , module , monitor , template_string_properties , template_integer_properties ) :
changed = False
for str_property in template_string_properties :
if str_property [ ' value ' ] is not None and not check_string_property ( api , monitor , str_property ) :
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 :
set_integer_property ( api , monitor , int_property )
changed = True
return changed
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 ] )
return True , " "
except bigsuds . OperationFailed , 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 ( ) :
# begin monitor specific stuff
2016-05-04 07:50:45 +02:00
argument_spec = f5_argument_spec ( )
2015-06-03 08:22:18 +02:00
argument_spec . update ( dict (
2013-10-25 22:52:48 +02:00
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 )
2015-06-03 08:22:18 +02:00
)
)
module = AnsibleModule (
argument_spec = argument_spec ,
2013-10-25 22:52:48 +02:00
supports_check_mode = True
)
2016-05-04 07:50:45 +02:00
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 ' ) :
module . fail_json ( msg = ' bigsuds does not support verifying certificates with python < 2.7.9. Either update python or set validate_certs=False on the task ' )
server = module . params [ ' server ' ]
2016-06-23 11:33:49 +02:00
server_port = module . params [ ' server_port ' ]
2016-05-04 07:50:45 +02:00
user = module . params [ ' user ' ]
password = module . params [ ' password ' ]
state = module . params [ ' state ' ]
partition = module . params [ ' partition ' ]
validate_certs = module . params [ ' validate_certs ' ]
2015-06-03 08:22:18 +02:00
2013-10-25 22:52:48 +02:00
parent_partition = module . params [ ' parent_partition ' ]
name = module . params [ ' name ' ]
type = ' TTYPE_ ' + module . params [ ' type ' ] . upper ( )
2015-06-03 08:22:18 +02:00
parent = fq_name ( parent_partition , module . params [ ' parent ' ] )
monitor = fq_name ( partition , name )
2013-10-25 22:52:48 +02:00
send = module . params [ ' send ' ]
receive = module . params [ ' receive ' ]
ip = module . params [ ' ip ' ]
port = module . params [ ' port ' ]
interval = module . params [ ' interval ' ]
timeout = module . params [ ' timeout ' ]
time_until_up = module . params [ ' time_until_up ' ]
# tcp monitor has multiple types, so overrule
global TEMPLATE_TYPE
TEMPLATE_TYPE = type
# end monitor specific stuff
2016-06-23 11:33:49 +02:00
api = bigip_api ( server , user , password , validate_certs , port = server_port )
2013-10-25 22:52:48 +02:00
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
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
2013-11-19 22:38:16 +01:00
if interval is None :
interval = 5
if timeout is None :
timeout = 16
if ip is None :
ip = ' 0.0.0.0 '
if port is None :
port = 0
if send is None :
send = ' '
if receive is None :
receive = ' '
2013-10-25 22:52:48 +02:00
# define and set address type
if ip == ' 0.0.0.0 ' and port == 0 :
address_type = ' ATYPE_STAR_ADDRESS_STAR_PORT '
elif ip == ' 0.0.0.0 ' and port != 0 :
address_type = ' ATYPE_STAR_ADDRESS_EXPLICIT_PORT '
elif ip != ' 0.0.0.0 ' and port != 0 :
address_type = ' ATYPE_EXPLICIT_ADDRESS_EXPLICIT_PORT '
else :
address_type = ' ATYPE_UNSET '
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 }
# monitor specific stuff
if type == ' TTYPE_TCP ' :
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 } ]
# 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
# 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 :
# again, check changed status here b/c race conditions
# if other task already created it
result [ ' changed ' ] | = create_monitor ( api , monitor , template_attributes )
else :
result [ ' changed ' ] | = True
## 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
# 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 )
result [ ' changed ' ] | = True
#else: monitor doesn't exist (check mode) or ipport is already ok
except Exception , e :
module . fail_json ( msg = " received exception: %s " % e )
module . exit_json ( * * result )
2013-12-02 21:11:23 +01:00
# import module snippets
from ansible . module_utils . basic import *
2015-06-03 08:22:18 +02:00
from ansible . module_utils . f5 import *
2013-10-25 22:52:48 +02:00
2016-04-25 21:34:18 +02:00
if __name__ == ' __main__ ' :
main ( )