@ -1,6 +1,6 @@
#!/usr/bin/python
#!/usr/bin/python
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
#
# (c) 2013, serge van Ginderachter <serge@vanginderachter.be>
# (c) 2013, serge van Ginderachter <serge@vanginderachter.be>
# based on Matt Hite's bigip_pool module
# based on Matt Hite's bigip_pool module
# (c) 2013, Matt Hite <mhite@hotmail.com>
# (c) 2013, Matt Hite <mhite@hotmail.com>
@ -25,160 +25,163 @@ DOCUMENTATION = '''
module : bigip_monitor_http
module : bigip_monitor_http
short_description : " Manages F5 BIG-IP LTM http monitors "
short_description : " Manages F5 BIG-IP LTM http monitors "
description :
description :
- " Manages F5 BIG-IP LTM monitors via iControl SOAP API "
- Manages F5 BIG - IP LTM monitors via iControl SOAP API
version_added : " 1.4 "
version_added : " 1.4 "
author :
author :
- Serge van Ginderachter ( @srvg )
- Serge van Ginderachter ( @srvg )
- Tim Rupp ( @caphrim007 )
- Tim Rupp ( @caphrim007 )
notes :
notes :
- " Requires BIG-IP software version >= 11 "
- " Requires BIG-IP software version >= 11 "
- " F5 developed module ' bigsuds ' required (see http://devcentral.f5.com) "
- " F5 developed module ' bigsuds ' required (see http://devcentral.f5.com) "
- " Best run as a local_action in your playbook "
- " Best run as a local_action in your playbook "
- " Monitor API documentation: https://devcentral.f5.com/wiki/iControl.LocalLB__Monitor.ashx "
- " Monitor API documentation: https://devcentral.f5.com/wiki/iControl.LocalLB__Monitor.ashx "
requirements :
requirements :
- bigsuds
- bigsuds
options :
options :
server :
server :
description :
description :
- BIG - IP host
- BIG - IP host
required : true
required : true
default : null
default : null
server_port :
server_port :
description :
description :
- BIG - IP server port
- BIG - IP server port
required : false
required : false
default : 443
default : 443
version_added : " 2.2 "
version_added : " 2.2 "
user :
user :
description :
description :
- BIG - IP username
- BIG - IP username
required : true
required : true
default : null
default : null
password :
password :
description :
description :
- BIG - IP password
- BIG - IP password
required : true
required : true
default : null
default : null
validate_certs :
validate_certs :
description :
description :
- If C ( no ) , SSL certificates will not be validated . This should only be used
- 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
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
validate on python > = 2.7 .9 and never validate on python < = 2.7 .8
required : false
required : false
default : ' yes '
default : ' yes '
choices : [ ' yes ' , ' no ' ]
choices :
version_added : 2.0
- yes
state :
- no
description :
version_added : 2.0
- Monitor state
state :
required : false
description :
default : ' present '
- Monitor state
choices : [ ' present ' , ' absent ' ]
required : false
name :
default : ' present '
description :
choices :
- Monitor name
- present
required : true
- absent
default : null
name :
aliases : [ ' monitor ' ]
description :
partition :
- Monitor name
description :
required : true
- Partition for the monitor
default : null
required : false
aliases :
default : ' Common '
- monitor
parent :
partition :
description :
description :
- The parent template of this monitor template
- Partition for the monitor
required : false
required : false
default : ' http '
default : ' Common '
parent_partition :
parent :
description :
description :
- Partition for the parent monitor
- The parent template of this monitor template
required : false
required : false
default : ' Common '
default : ' http '
send :
parent_partition :
description :
description :
- The send string for the monitor call
- Partition for the parent monitor
required : true
required : false
default : none
default : ' Common '
receive :
send :
description :
description :
- The receive string for the monitor call
- The send string for the monitor call
required : true
required : true
default : none
default : none
receive_disable :
receive :
description :
description :
- The receive disable string for the monitor call
- The receive string for the monitor call
required : true
required : true
default : none
default : none
ip :
receive_disable :
description :
description :
- IP address part of the ipport definition . The default API setting
- The receive disable string for the monitor call
is " 0.0.0.0 " .
required : true
required : false
default : none
default : none
ip :
port :
description :
description :
- IP address part of the ipport definition . The default API setting
- port address part op the ipport definition . The default API
is " 0.0.0.0 " .
setting is 0.
required : false
required : false
default : none
default : none
port :
interval :
description :
description :
- Port address part of the ip / port definition . The default API
- The interval specifying how frequently the monitor instance
setting is 0.
of this template will run . By default , this interval is used for up and
required : false
down states . The default API setting is 5.
default : none
required : false
interval :
default : none
description :
timeout :
- The interval specifying how frequently the monitor instance
description :
of this template will run . By default , this interval is used for up and
- The number of seconds in which the node or service must respond to
down states . The default API setting is 5.
the monitor request . If the target responds within the set time
required : false
period , it is considered up . If the target does not respond within
default : none
the set time period , it is considered down . You can change this
timeout :
number to any number you want , however , it should be 3 times the
description :
interval number of seconds plus 1 second . The default API setting
- The number of seconds in which the node or service must respond to
is 16.
the monitor request . If the target responds within the set time
required : false
period , it is considered up . If the target does not respond within
default : none
the set time period , it is considered down . You can change this
time_until_up :
number to any number you want , however , it should be 3 times the
description :
interval number of seconds plus 1 second . The default API setting
- Specifies the amount of time in seconds after the first successful
is 16.
response before a node will be marked up . A value of 0 will cause a
required : false
node to be marked up immediately after a valid response is received
default : none
from the node . The default API setting is 0.
time_until_up :
required : false
description :
default : none
- 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 = '''
EXAMPLES = '''
- name : BIGIP F5 | Create HTTP Monitor
- name : BIGIP F5 | Create HTTP Monitor
local_action :
bigip_monitor_http :
module : bigip_monitor_http
state : " present "
state : present
server : " lb.mydomain.com "
server : " {{ f5server }} "
user : " admin "
user : " {{ f5user }} "
password : " secret "
password : " {{ f5password }} "
name : " my_http_monitor "
name : " {{ item.monitorname }} "
send : " http string to send "
send : " {{ item.send }} "
receive : " http string to receive "
receive : " {{ item.receive }} "
delegate_to : localhost
with_items : f5monitors
- name : BIGIP F5 | Remove HTTP Monitor
- name : BIGIP F5 | Remove HTTP Monitor
local_action :
bigip_monitor_http :
module: bigip_monitor_http
state: " absent "
s tate: absent
s erver: " lb.mydomain.com "
server: " {{ f5server }} "
user: " admin "
user: " {{ f5user }} "
password: " secret "
password: " {{ f5password }} "
name: " my_http_monitor "
name : " {{ monitorname }} "
delegate_to : localhost
'''
'''
TEMPLATE_TYPE = ' TTYPE_HTTP '
TEMPLATE_TYPE = ' TTYPE_HTTP '
DEFAULT_PARENT_TYPE = ' http '
DEFAULT_PARENT_TYPE = ' http '
def check_monitor_exists ( module , api , monitor , parent ) :
def check_monitor_exists ( module , api , monitor , parent ) :
# hack to determine if monitor exists
# hack to determine if monitor exists
result = False
result = False
try :
try :
@ -188,7 +191,7 @@ def check_monitor_exists(module, api, monitor, parent):
result = True
result = True
else :
else :
module . fail_json ( msg = ' Monitor already exists, but has a different type ( %s ) or parent( %s ) ' % ( ttype , parent ) )
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 ) :
if " was not found " in str ( e ) :
result = False
result = False
else :
else :
@ -198,10 +201,15 @@ def check_monitor_exists(module, api, monitor, parent):
def create_monitor ( api , monitor , template_attributes ) :
def create_monitor ( api , monitor , template_attributes ) :
try :
try :
api . LocalLB . Monitor . create_template ( templates = [ { ' template_name ' : monitor , ' template_type ' : TEMPLATE_TYPE } ] , template_attributes = [ template_attributes ] )
api . LocalLB . Monitor . create_template (
except bigsuds . OperationFailed , e :
templates = [ {
' template_name ' : monitor ,
' template_type ' : TEMPLATE_TYPE
} ] ,
template_attributes = [ template_attributes ]
)
except bigsuds . OperationFailed as e :
if " already exists " in str ( e ) :
if " already exists " in str ( e ) :
return False
return False
else :
else :
@ -211,10 +219,9 @@ def create_monitor(api, monitor, template_attributes):
def delete_monitor ( api , monitor ) :
def delete_monitor ( api , monitor ) :
try :
try :
api . LocalLB . Monitor . delete_template ( template_names = [ monitor ] )
api . LocalLB . Monitor . delete_template ( template_names = [ monitor ] )
except bigsuds . OperationFailed , e :
except bigsuds . OperationFailed as e :
# maybe it was deleted since we checked
# maybe it was deleted since we checked
if " was not found " in str ( e ) :
if " was not found " in str ( e ) :
return False
return False
@ -225,10 +232,12 @@ def delete_monitor(api, monitor):
def check_string_property ( api , monitor , str_property ) :
def check_string_property ( api , monitor , str_property ) :
try :
try :
return str_property == api . LocalLB . Monitor . get_template_string_property ( [ monitor ] , [ str_property [ ' type ' ] ] ) [ 0 ]
template_prop = api . LocalLB . Monitor . get_template_string_property (
except bigsuds . OperationFailed , e :
[ monitor ] , [ str_property [ ' type ' ] ]
) [ 0 ]
return str_property == template_prop
except bigsuds . OperationFailed as e :
# happens in check mode if not created yet
# happens in check mode if not created yet
if " was not found " in str ( e ) :
if " was not found " in str ( e ) :
return True
return True
@ -238,15 +247,19 @@ def check_string_property(api, monitor, str_property):
def set_string_property ( api , monitor , str_property ) :
def set_string_property ( api , monitor , str_property ) :
api . LocalLB . Monitor . set_template_string_property (
api . LocalLB . Monitor . set_template_string_property ( template_names = [ monitor ] , values = [ str_property ] )
template_names = [ monitor ] ,
values = [ str_property ]
)
def check_integer_property ( api , monitor , int_property ) :
def check_integer_property ( api , monitor , int_property ) :
try :
try :
return int_property == api . LocalLB . Monitor . get_template_integer_property ( [ monitor ] , [ int_property [ ' type ' ] ] ) [ 0 ]
template_prop = api . LocalLB . Monitor . get_template_integer_property (
except bigsuds . OperationFailed , e :
[ monitor ] , [ int_property [ ' type ' ] ]
) [ 0 ]
return int_property == template_prop
except bigsuds . OperationFailed as e :
# happens in check mode if not created yet
# happens in check mode if not created yet
if " was not found " in str ( e ) :
if " was not found " in str ( e ) :
return True
return True
@ -255,10 +268,11 @@ def check_integer_property(api, monitor, int_property):
raise
raise
def set_integer_property ( api , monitor , int_property ) :
def set_integer_property ( api , monitor , int_property ) :
api . LocalLB . Monitor . set_template_integer_property (
api . LocalLB . Monitor . set_template_integer_property ( template_names = [ monitor ] , values = [ int_property ] )
template_names = [ monitor ] ,
values = [ int_property ]
)
def update_monitor_properties ( api , module , monitor , template_string_properties , template_integer_properties ) :
def update_monitor_properties ( api , module , monitor , template_string_properties , template_integer_properties ) :
@ -278,61 +292,46 @@ def update_monitor_properties(api, module, monitor, template_string_properties,
def get_ipport ( api , monitor ) :
def get_ipport ( api , monitor ) :
return api . LocalLB . Monitor . get_template_destination ( template_names = [ monitor ] ) [ 0 ]
return api . LocalLB . Monitor . get_template_destination ( template_names = [ monitor ] ) [ 0 ]
def set_ipport ( api , monitor , ipport ) :
def set_ipport ( api , monitor , ipport ) :
try :
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 , " "
return True , " "
except bigsuds . OperationFailed as e :
except bigsuds . OperationFailed , e :
if " Cannot modify the address type of monitor " in str ( 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. "
return False , " Cannot modify the address type of monitor if already assigned to a pool. "
else :
else :
# genuine exception
# genuine exception
raise
raise
# ===========================================
# main loop
#
# writing a module for other monitor types should
# only need an updated main() (and monitor specific functions)
def main ( ) :
def main ( ) :
argument_spec = f5_argument_spec ( )
# begin monitor specific stuff
argument_spec = f5_argument_spec ( ) ;
meta_args = dict (
argument_spec . update ( dict (
name = dict ( required = True ) ,
name = dict ( required = True ) ,
parent = dict ( default = DEFAULT_PARENT_TYPE ) ,
parent = dict ( default = DEFAULT_PARENT_TYPE ) ,
parent_partition = dict ( default = ' Common ' ) ,
parent_partition = dict ( default = ' Common ' ) ,
send = dict ( required = False ) ,
send = dict ( required = False ) ,
receive = dict ( required = False ) ,
receive = dict ( required = False ) ,
receive_disable = dict ( required = False ) ,
receive_disable = dict ( required = False ) ,
ip = dict ( required = False ) ,
ip = dict ( required = False ) ,
port = dict ( required = False , type = ' int ' ) ,
port = dict ( required = False , type = ' int ' ) ,
interval = dict ( required = False , type = ' int ' ) ,
interval = dict ( required = False , type = ' int ' ) ,
timeout = dict ( required = False , type = ' int ' ) ,
timeout = dict ( required = False , type = ' int ' ) ,
time_until_up = dict ( required = False , type = ' int ' , default = 0 )
time_until_up = dict ( required = False , type = ' int ' , default = 0 )
)
)
)
argument_spec . update ( meta_args )
module = AnsibleModule (
module = AnsibleModule (
argument_spec = argument_spec ,
argument_spec = argument_spec ,
supports_check_mode = True
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 ' ) :
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 ' ]
server = module . params [ ' server ' ]
server_port = module . params [ ' server_port ' ]
server_port = module . params [ ' server_port ' ]
user = module . params [ ' user ' ]
user = module . params [ ' user ' ]
@ -359,15 +358,14 @@ def main():
api = bigip_api ( server , user , password , validate_certs , port = server_port )
api = bigip_api ( server , user , password , validate_certs , port = server_port )
monitor_exists = check_monitor_exists ( module , api , monitor , parent )
monitor_exists = check_monitor_exists ( module , api , monitor , parent )
# ipport is a special setting
# ipport is a special setting
if monitor_exists : # make sure to not update current settings if not asked
if monitor_exists :
cur_ipport = get_ipport ( api , monitor )
cur_ipport = get_ipport ( api , monitor )
if ip is None :
if ip is None :
ip = cur_ipport [ ' ipport ' ] [ ' address ' ]
ip = cur_ipport [ ' ipport ' ] [ ' address ' ]
if port is None :
if port is None :
port = cur_ipport [ ' ipport ' ] [ ' port ' ]
port = cur_ipport [ ' ipport ' ] [ ' port ' ]
else : # use API defaults if not defined to create it
else :
if interval is None :
if interval is None :
interval = 5
interval = 5
if timeout is None :
if timeout is None :
@ -412,19 +410,26 @@ def main():
{ ' type ' : ' STYPE_RECEIVE_DRAIN ' ,
{ ' type ' : ' STYPE_RECEIVE_DRAIN ' ,
' value ' : receive_disable } ]
' value ' : receive_disable } ]
template_integer_properties = [ { ' type ' : ' ITYPE_INTERVAL ' ,
template_integer_properties = [
' value ' : interval } ,
{
{ ' type ' : ' ITYPE_TIMEOUT ' ,
' type ' : ' ITYPE_INTERVAL ' ,
' value ' : timeout } ,
' value ' : interval
{ ' type ' : ' ITYPE_TIME_UNTIL_UP ' ,
} ,
' value ' : time_until_up } ]
{
' type ' : ' ITYPE_TIMEOUT ' ,
' value ' : timeout
} ,
{
' type ' : ' ITYPE_TIME_UNTIL_UP ' ,
' value ' : time_until_up
}
]
# main logic, monitor generic
# main logic, monitor generic
try :
try :
result = { ' changed ' : False } # default
result = { ' changed ' : False } # default
if state == ' absent ' :
if state == ' absent ' :
if monitor_exists :
if monitor_exists :
if not module . check_mode :
if not module . check_mode :
@ -433,10 +438,9 @@ def main():
result [ ' changed ' ] | = delete_monitor ( api , monitor )
result [ ' changed ' ] | = delete_monitor ( api , monitor )
else :
else :
result [ ' changed ' ] | = True
result [ ' changed ' ] | = True
else :
else : # state present
# check for monitor itself
## check for monitor itself
if not monitor_exists :
if not monitor_exists : # create it
if not module . check_mode :
if not module . check_mode :
# again, check changed status here b/c race conditions
# again, check changed status here b/c race conditions
# if other task already created it
# if other task already created it
@ -444,22 +448,20 @@ def main():
else :
else :
result [ ' changed ' ] | = True
result [ ' changed ' ] | = True
# # check for monitor parameters
# check for monitor parameters
# whether it already existed, or was just created, now update
# whether it already existed, or was just created, now update
# the update functions need to check for check mode but
# the update functions need to check for check mode but
# cannot update settings if it doesn't exist which happens in check mode
# cannot update settings if it doesn't exist which happens in check mode
result [ ' changed ' ] | = update_monitor_properties ( api , module , monitor ,
result [ ' changed ' ] | = update_monitor_properties ( api , module , monitor ,
template_string_properties ,
template_string_properties ,
template_integer_properties )
template_integer_properties )
# we just have to update the ipport if monitor already exists and it's different
# we just have to update the ipport if monitor already exists and it's different
if monitor_exists and cur_ipport != ipport :
if monitor_exists and cur_ipport != ipport :
set_ipport ( api , monitor , ipport )
set_ipport ( api , monitor , ipport )
result [ ' changed ' ] | = True
result [ ' changed ' ] | = True
#else: monitor doesn't exist (check mode) or ipport is already ok
# else: monitor doesn't exist (check mode) or ipport is already ok
except Exception as e :
except Exception , e :
module . fail_json ( msg = " received exception: %s " % e )
module . fail_json ( msg = " received exception: %s " % e )
module . exit_json ( * * result )
module . exit_json ( * * result )