2013-08-09 17:23:46 +02:00
#!/usr/bin/python
# -*- coding: utf-8 -*-
# (c) 2013, serge van Ginderachter <serge@vanginderachter.be>
# based on Matt Hite's bigip_pool module
# (c) 2013, Matt Hite <mhite@hotmail.com>
#
# 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_http
short_description : " Manages F5 BIG-IP LTM http monitors "
description :
- " Manages F5 BIG-IP LTM monitors via iControl SOAP API "
2013-10-21 20:57:22 +02:00
version_added : " 1.4 "
2016-06-23 11:33:49 +02:00
author :
- Serge van Ginderachter ( @srvg )
- Tim Rupp ( @caphrim007 )
2013-08-09 17:23:46 +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 "
- " Monitor API documentation: https://devcentral.f5.com/wiki/iControl.LocalLB__Monitor.ashx "
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-08-09 17:23:46 +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-08-09 17:23:46 +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 :
2013-08-30 17:41:40 +02:00
- Partition for the monitor
2013-08-09 17:23:46 +02:00
required : false
default : ' Common '
parent :
description :
- The parent template of this monitor template
required : false
default : ' http '
2013-08-30 17:41:40 +02:00
parent_partition :
description :
- Partition for the parent monitor
required : false
default : ' Common '
2013-08-09 17:23:46 +02:00
send :
description :
2013-09-02 16:26:28 +02:00
- The send string for the monitor call
2013-08-09 17:23:46 +02:00
required : true
2013-09-05 15:34:12 +02:00
default : none
2013-08-09 17:23:46 +02:00
receive :
description :
2013-09-02 16:26:28 +02:00
- The receive string for the monitor call
2013-08-09 17:23:46 +02:00
required : true
2013-09-05 15:34:12 +02:00
default : none
2013-09-03 21:24:45 +02:00
receive_disable :
description :
- The receive disable string for the monitor call
required : true
2013-09-05 15:34:12 +02:00
default : none
2013-09-02 16:26:28 +02:00
ip :
2013-12-17 21:48:29 +01:00
description :
2013-09-05 15:34:12 +02:00
- IP address part of the ipport definition . The default API setting
is " 0.0.0.0 " .
2013-09-02 16:26:28 +02:00
required : false
2013-09-05 15:34:12 +02:00
default : none
2013-09-02 16:26:28 +02:00
port :
2013-12-17 21:48:29 +01:00
description :
2014-04-29 16:41:05 +02:00
- port address part op the ipport definition . The default API
2013-09-05 15:34:12 +02:00
setting is 0.
2013-09-02 16:26:28 +02:00
required : false
2013-09-05 15:34:12 +02:00
default : none
2013-09-02 16:26:28 +02:00
interval :
2013-12-17 21:48:29 +01:00
description :
2013-09-02 16:26:28 +02:00
- The interval specifying how frequently the monitor instance
of this template will run . By default , this interval is used for up and
2013-09-05 15:34:12 +02:00
down states . The default API setting is 5.
2013-09-02 16:26:28 +02:00
required : false
2013-09-05 15:34:12 +02:00
default : none
2013-09-02 16:26:28 +02:00
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
2013-09-05 15:34:12 +02:00
interval number of seconds plus 1 second . The default API setting
is 16.
2013-09-03 21:24:45 +02:00
required : false
2013-09-05 15:34:12 +02:00
default : none
2013-09-03 21:24:45 +02:00
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
2013-09-05 15:34:12 +02:00
from the node . The default API setting is 0.
2013-09-03 21:24:45 +02:00
required : false
2013-09-05 15:34:12 +02:00
default : none
2013-08-09 17:23:46 +02:00
'''
EXAMPLES = '''
2013-10-21 20:57:22 +02:00
- name : BIGIP F5 | Create HTTP Monitor
local_action :
module : bigip_monitor_http
state : present
server : " {{ f5server }} "
user : " {{ f5user }} "
password : " {{ f5password }} "
name : " {{ item.monitorname }} "
send : " {{ item.send }} "
receive : " {{ item.receive }} "
with_items : f5monitors
- name : BIGIP F5 | Remove HTTP Monitor
local_action :
module : bigip_monitor_http
state : absent
server : " {{ f5server }} "
user : " {{ f5user }} "
password : " {{ f5password }} "
name : " {{ monitorname }} "
2013-08-09 17:23:46 +02:00
'''
TEMPLATE_TYPE = ' TTYPE_HTTP '
DEFAULT_PARENT_TYPE = ' http '
2015-03-23 22:26:11 +01:00
2013-09-05 15:34:12 +02:00
def check_monitor_exists ( module , api , monitor , parent ) :
2013-08-09 17:23:46 +02:00
# 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 ) :
2013-12-17 21:48:29 +01:00
try :
2013-08-30 17:41:40 +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 ) :
2013-09-03 22:00:43 +02:00
return False
2013-08-30 17:41:40 +02:00
else :
# genuine exception
raise
2013-09-03 22:00:43 +02:00
return True
2013-08-09 17:23:46 +02:00
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
2013-09-03 22:00:43 +02:00
if " was not found " in str ( e ) :
return False
else :
2013-08-09 17:23:46 +02:00
# genuine exception
raise
2013-09-03 22:00:43 +02:00
return True
2013-08-09 17:23:46 +02:00
2013-09-05 15:34:12 +02:00
2013-08-09 17:23:46 +02:00
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
2013-08-09 17:23:46 +02:00
def set_string_property ( api , monitor , str_property ) :
api . LocalLB . Monitor . set_template_string_property ( template_names = [ monitor ] , values = [ str_property ] )
2013-09-02 16:26:28 +02:00
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
2013-09-02 16:26:28 +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-09-02 16:26:28 +02:00
2013-09-05 15:34:12 +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 ]
2013-09-02 16:26:28 +02:00
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
2013-08-09 17:23:46 +02:00
# ===========================================
# main loop
#
2013-12-17 21:48:29 +01:00
# writing a module for other monitor types should
2013-09-05 15:34:12 +02:00
# only need an updated main() (and monitor specific functions)
2013-08-30 17:41:40 +02:00
2013-08-09 17:23:46 +02:00
def main ( ) :
2013-10-25 22:47:35 +02:00
# begin monitor specific stuff
2015-06-03 08:22:18 +02:00
argument_spec = f5_argument_spec ( ) ;
argument_spec . update ( dict (
2013-08-09 17:23:46 +02:00
name = dict ( required = True ) ,
parent = dict ( default = DEFAULT_PARENT_TYPE ) ,
2013-08-30 17:41:40 +02:00
parent_partition = dict ( default = ' Common ' ) ,
2013-09-05 15:34:12 +02:00
send = dict ( required = False ) ,
receive = dict ( required = False ) ,
receive_disable = 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 ' ) ,
2013-09-03 21:24:45 +02:00
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-08-09 17:23:46 +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-08-30 17:41:40 +02:00
parent_partition = module . params [ ' parent_partition ' ]
2013-08-09 17:23:46 +02:00
name = module . params [ ' name ' ]
2015-06-03 08:22:18 +02:00
parent = fq_name ( parent_partition , module . params [ ' parent ' ] )
monitor = fq_name ( partition , name )
2013-08-09 17:23:46 +02:00
send = module . params [ ' send ' ]
receive = module . params [ ' receive ' ]
2013-09-03 21:24:45 +02:00
receive_disable = module . params [ ' receive_disable ' ]
2013-09-02 16:26:28 +02:00
ip = module . params [ ' ip ' ]
port = module . params [ ' port ' ]
interval = module . params [ ' interval ' ]
timeout = module . params [ ' timeout ' ]
2013-09-03 21:24:45 +02:00
time_until_up = module . params [ ' time_until_up ' ]
2013-08-09 17:23:46 +02:00
2013-10-25 22:47:35 +02:00
# end monitor specific stuff
2013-08-09 17:23:46 +02:00
2016-06-23 11:33:49 +02:00
api = bigip_api ( server , user , password , validate_certs , port = server_port )
2013-09-05 15:34:12 +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-12-17 21:48:29 +01:00
if interval is None :
2013-11-20 20:57:30 +01:00
interval = 5
2013-12-17 21:48:29 +01:00
if timeout is None :
2013-11-20 20:57:30 +01:00
timeout = 16
2013-12-17 21:48:29 +01:00
if ip is None :
2013-11-20 20:57:30 +01:00
ip = ' 0.0.0.0 '
2013-12-17 21:48:29 +01:00
if port is None :
2013-11-20 20:57:30 +01:00
port = 0
2013-12-17 21:48:29 +01:00
if send is None :
2013-11-20 20:57:30 +01:00
send = ' '
2013-12-17 21:48:29 +01:00
if receive is None :
2013-11-20 20:57:30 +01:00
receive = ' '
2013-12-17 21:48:29 +01:00
if receive_disable is None :
2013-11-20 20:57:30 +01:00
receive_disable = ' '
2013-09-05 15:34:12 +02:00
# define and set address type
2013-09-02 16:26:28 +02:00
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 '
2013-08-09 17:23:46 +02:00
2013-09-05 15:34:12 +02:00
ipport = { ' address_type ' : address_type ,
' ipport ' : { ' address ' : ip ,
' port ' : port } }
2013-08-09 17:23:46 +02:00
2013-09-05 15:34:12 +02:00
template_attributes = { ' parent_template ' : parent ,
2013-09-10 17:03:17 +02:00
' interval ' : interval ,
' timeout ' : timeout ,
2013-09-05 15:34:12 +02:00
' dest_ipport ' : ipport ,
' is_read_only ' : False ,
' is_directly_usable ' : True }
2013-10-25 22:47:35 +02:00
# monitor specific stuff
2013-09-05 15:34:12 +02:00
template_string_properties = [ { ' type ' : ' STYPE_SEND ' ,
' value ' : send } ,
{ ' type ' : ' STYPE_RECEIVE ' ,
' value ' : receive } ,
{ ' type ' : ' STYPE_RECEIVE_DRAIN ' ,
' value ' : receive_disable } ]
template_integer_properties = [ { ' type ' : ' ITYPE_INTERVAL ' ,
' value ' : interval } ,
{ ' type ' : ' ITYPE_TIMEOUT ' ,
' value ' : timeout } ,
{ ' type ' : ' ITYPE_TIME_UNTIL_UP ' ,
2013-12-17 21:48:29 +01:00
' value ' : time_until_up } ]
2013-09-05 15:34:12 +02:00
2013-10-25 22:47:35 +02:00
# main logic, monitor generic
2013-09-02 16:26:28 +02:00
2013-08-09 17:23:46 +02:00
try :
result = { ' changed ' : False } # default
if state == ' absent ' :
if monitor_exists :
2013-09-03 21:24:45 +02:00
if not module . check_mode :
2013-12-17 21:48:29 +01:00
# possible race condition if same task
2013-09-03 22:00:43 +02:00
# on other node deleted it first
2013-09-05 15:34:12 +02:00
result [ ' changed ' ] | = delete_monitor ( api , monitor )
2013-09-03 22:00:43 +02:00
else :
2013-09-05 15:34:12 +02:00
result [ ' changed ' ] | = True
else : # state present
2013-09-07 13:31:35 +02:00
## check for monitor itself
2013-09-05 15:34:12 +02:00
if not monitor_exists : # create it
2013-12-17 21:48:29 +01:00
if not module . check_mode :
2013-09-05 15:34:12 +02:00
# again, check changed status here b/c race conditions
# if other task already created it
result [ ' changed ' ] | = create_monitor ( api , monitor , template_attributes )
2013-12-17 21:48:29 +01:00
else :
2013-09-05 15:34:12 +02:00
result [ ' changed ' ] | = True
2013-09-07 13:31:35 +02:00
## check for monitor parameters
2013-09-05 15:34:12 +02:00
# whether it already existed, or was just created, now update
2013-09-07 13:31:35 +02:00
# the update functions need to check for check mode but
# cannot update settings if it doesn't exist which happens in check mode
2013-12-17 21:48:29 +01:00
result [ ' changed ' ] | = update_monitor_properties ( api , module , monitor ,
template_string_properties ,
template_integer_properties )
2013-09-05 15:34:12 +02:00
# we just have to update the ipport if monitor already exists and it's different
if monitor_exists and cur_ipport != ipport :
2013-12-17 21:48:29 +01:00
set_ipport ( api , monitor , ipport )
2013-09-05 15:34:12 +02:00
result [ ' changed ' ] | = True
2013-09-07 13:31:35 +02:00
#else: monitor doesn't exist (check mode) or ipport is already ok
2013-08-30 17:41:40 +02:00
2013-08-09 17:23:46 +02:00
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-08-09 17:23:46 +02:00
2016-04-25 21:34:18 +02:00
if __name__ == ' __main__ ' :
main ( )