ansible-later/env_27/lib/python2.7/site-packages/ansible/modules/network/netscaler/netscaler_servicegroup.py
2019-04-11 13:00:36 +02:00

1043 lines
34 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2017 Citrix Systems
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: netscaler_servicegroup
short_description: Manage service group configuration in Netscaler
description:
- Manage service group configuration in Netscaler.
- This module is intended to run either on the ansible control node or a bastion (jumpserver) with access to the actual netscaler instance.
version_added: "2.4"
author: George Nikolopoulos (@giorgos-nikolopoulos)
options:
servicegroupname:
description:
- >-
Name of the service group. Must begin with an ASCII alphabetic or underscore C(_) character, and must
contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), space C( ), colon C(:), at C(@), equals
C(=), and hyphen C(-) characters. Can be changed after the name is created.
- "Minimum length = 1"
servicetype:
choices:
- 'HTTP'
- 'FTP'
- 'TCP'
- 'UDP'
- 'SSL'
- 'SSL_BRIDGE'
- 'SSL_TCP'
- 'DTLS'
- 'NNTP'
- 'RPCSVR'
- 'DNS'
- 'ADNS'
- 'SNMP'
- 'RTSP'
- 'DHCPRA'
- 'ANY'
- 'SIP_UDP'
- 'SIP_TCP'
- 'SIP_SSL'
- 'DNS_TCP'
- 'ADNS_TCP'
- 'MYSQL'
- 'MSSQL'
- 'ORACLE'
- 'RADIUS'
- 'RADIUSListener'
- 'RDP'
- 'DIAMETER'
- 'SSL_DIAMETER'
- 'TFTP'
- 'SMPP'
- 'PPTP'
- 'GRE'
- 'SYSLOGTCP'
- 'SYSLOGUDP'
- 'FIX'
- 'SSL_FIX'
description:
- "Protocol used to exchange data with the service."
cachetype:
choices:
- 'TRANSPARENT'
- 'REVERSE'
- 'FORWARD'
description:
- "Cache type supported by the cache server."
maxclient:
description:
- "Maximum number of simultaneous open connections for the service group."
- "Minimum value = C(0)"
- "Maximum value = C(4294967294)"
maxreq:
description:
- "Maximum number of requests that can be sent on a persistent connection to the service group."
- "Note: Connection requests beyond this value are rejected."
- "Minimum value = C(0)"
- "Maximum value = C(65535)"
cacheable:
description:
- "Use the transparent cache redirection virtual server to forward the request to the cache server."
- "Note: Do not set this parameter if you set the Cache Type."
type: bool
cip:
choices:
- 'enabled'
- 'disabled'
description:
- "Insert the Client IP header in requests forwarded to the service."
cipheader:
description:
- >-
Name of the HTTP header whose value must be set to the IP address of the client. Used with the Client
IP parameter. If client IP insertion is enabled, and the client IP header is not specified, the value
of Client IP Header parameter or the value set by the set ns config command is used as client's IP
header name.
- "Minimum length = 1"
usip:
description:
- >-
Use client's IP address as the source IP address when initiating connection to the server. With the
NO setting, which is the default, a mapped IP (MIP) address or subnet IP (SNIP) address is used as
the source IP address to initiate server side connections.
pathmonitor:
description:
- "Path monitoring for clustering."
pathmonitorindv:
description:
- "Individual Path monitoring decisions."
useproxyport:
description:
- >-
Use the proxy port as the source port when initiating connections with the server. With the NO
setting, the client-side connection port is used as the source port for the server-side connection.
- "Note: This parameter is available only when the Use Source IP C(usip) parameter is set to C(yes)."
type: bool
healthmonitor:
description:
- "Monitor the health of this service. Available settings function as follows:"
- "C(yes) - Send probes to check the health of the service."
- >-
C(no) - Do not send probes to check the health of the service. With the NO option, the appliance shows
the service as UP at all times.
type: bool
sp:
description:
- "Enable surge protection for the service group."
type: bool
rtspsessionidremap:
description:
- "Enable RTSP session ID mapping for the service group."
type: bool
clttimeout:
description:
- "Time, in seconds, after which to terminate an idle client connection."
- "Minimum value = C(0)"
- "Maximum value = C(31536000)"
svrtimeout:
description:
- "Time, in seconds, after which to terminate an idle server connection."
- "Minimum value = C(0)"
- "Maximum value = C(31536000)"
cka:
description:
- "Enable client keep-alive for the service group."
type: bool
tcpb:
description:
- "Enable TCP buffering for the service group."
type: bool
cmp:
description:
- "Enable compression for the specified service."
type: bool
maxbandwidth:
description:
- "Maximum bandwidth, in Kbps, allocated for all the services in the service group."
- "Minimum value = C(0)"
- "Maximum value = C(4294967287)"
monthreshold:
description:
- >-
Minimum sum of weights of the monitors that are bound to this service. Used to determine whether to
mark a service as UP or DOWN.
- "Minimum value = C(0)"
- "Maximum value = C(65535)"
downstateflush:
choices:
- 'enabled'
- 'disabled'
description:
- >-
Flush all active transactions associated with all the services in the service group whose state
transitions from UP to DOWN. Do not enable this option for applications that must complete their
transactions.
tcpprofilename:
description:
- "Name of the TCP profile that contains TCP configuration settings for the service group."
- "Minimum length = 1"
- "Maximum length = 127"
httpprofilename:
description:
- "Name of the HTTP profile that contains HTTP configuration settings for the service group."
- "Minimum length = 1"
- "Maximum length = 127"
comment:
description:
- "Any information about the service group."
appflowlog:
choices:
- 'enabled'
- 'disabled'
description:
- "Enable logging of AppFlow information for the specified service group."
netprofile:
description:
- "Network profile for the service group."
- "Minimum length = 1"
- "Maximum length = 127"
autoscale:
choices:
- 'DISABLED'
- 'DNS'
- 'POLICY'
description:
- "Auto scale option for a servicegroup."
memberport:
description:
- "member port."
graceful:
description:
- "Wait for all existing connections to the service to terminate before shutting down the service."
type: bool
servicemembers:
description:
- A list of dictionaries describing each service member of the service group.
suboptions:
ip:
description:
- IP address of the service. Must not overlap with an existing server entity defined by name.
port:
description:
- Server port number.
- Range C(1) - C(65535)
- "* in CLI is represented as 65535 in NITRO API"
state:
choices:
- 'enabled'
- 'disabled'
description:
- Initial state of the service after binding.
hashid:
description:
- The hash identifier for the service.
- This must be unique for each service.
- This parameter is used by hash based load balancing methods.
- Minimum value = C(1)
serverid:
description:
- The identifier for the service.
- This is used when the persistency type is set to Custom Server ID.
servername:
description:
- Name of the server to which to bind the service group.
- The server must already be configured as a named server.
- Minimum length = 1
customserverid:
description:
- The identifier for this IP:Port pair.
- Used when the persistency type is set to Custom Server ID.
weight:
description:
- Weight to assign to the servers in the service group.
- Specifies the capacity of the servers relative to the other servers in the load balancing configuration.
- The higher the weight, the higher the percentage of requests sent to the service.
- Minimum value = C(1)
- Maximum value = C(100)
monitorbindings:
description:
- A list of monitornames to bind to this service
- Note that the monitors must have already been setup possibly using the M(netscaler_lb_monitor) module or some other method
suboptions:
monitorname:
description:
- The monitor name to bind to this servicegroup.
weight:
description:
- Weight to assign to the binding between the monitor and servicegroup.
disabled:
description:
- When set to C(yes) the service group state will be set to DISABLED.
- When set to C(no) the service group state will be set to ENABLED.
- >-
Note that due to limitations of the underlying NITRO API a C(disabled) state change alone
does not cause the module result to report a changed status.
type: bool
default: false
extends_documentation_fragment: netscaler
requirements:
- nitro python sdk
'''
EXAMPLES = '''
# The LB Monitors monitor-1 and monitor-2 must already exist
# Service members defined by C(ip) must not redefine an existing server's ip address.
# Service members defined by C(servername) must already exist.
- name: Setup http service with ip members
delegate_to: localhost
netscaler_servicegroup:
nsip: 172.18.0.2
nitro_user: nsroot
nitro_pass: nsroot
state: present
servicegroupname: service-group-1
servicetype: HTTP
servicemembers:
- ip: 10.78.78.78
port: 80
weight: 50
- ip: 10.79.79.79
port: 80
weight: 40
- servername: server-1
port: 80
weight: 10
monitorbindings:
- monitorname: monitor-1
weight: 50
- monitorname: monitor-2
weight: 50
'''
RETURN = '''
loglines:
description: list of logged messages by the module
returned: always
type: list
sample: ['message 1', 'message 2']
msg:
description: Message detailing the failure reason
returned: failure
type: str
sample: "Action does not exist"
diff:
description: List of differences between the actual configured object and the configuration specified in the module
returned: failure
type: dict
sample: { 'clttimeout': 'difference. ours: (float) 10.0 other: (float) 20.0' }
'''
from ansible.module_utils.basic import AnsibleModule
import copy
from ansible.module_utils.network.netscaler.netscaler import ConfigProxy, get_nitro_client, netscaler_common_arguments, log, \
loglines, get_immutables_intersection
try:
from nssrc.com.citrix.netscaler.nitro.resource.config.basic.servicegroup import servicegroup
from nssrc.com.citrix.netscaler.nitro.resource.config.basic.servicegroup_servicegroupmember_binding import servicegroup_servicegroupmember_binding
from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception
from nssrc.com.citrix.netscaler.nitro.resource.config.basic.servicegroup_lbmonitor_binding import servicegroup_lbmonitor_binding
from nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbmonitor_servicegroup_binding import lbmonitor_servicegroup_binding
PYTHON_SDK_IMPORTED = True
except ImportError as e:
PYTHON_SDK_IMPORTED = False
def servicegroup_exists(client, module):
log('Checking if service group exists')
count = servicegroup.count_filtered(client, 'servicegroupname:%s' % module.params['servicegroupname'])
log('count is %s' % count)
if count > 0:
return True
else:
return False
def servicegroup_identical(client, module, servicegroup_proxy):
log('Checking if service group is identical')
servicegroups = servicegroup.get_filtered(client, 'servicegroupname:%s' % module.params['servicegroupname'])
if servicegroup_proxy.has_equal_attributes(servicegroups[0]):
return True
else:
return False
def get_configured_service_members(client, module):
log('get_configured_service_members')
readwrite_attrs = [
'servicegroupname',
'ip',
'port',
'state',
'hashid',
'serverid',
'servername',
'customserverid',
'weight'
]
readonly_attrs = [
'delay',
'statechangetimesec',
'svrstate',
'tickssincelaststatechange',
'graceful',
]
members = []
if module.params['servicemembers'] is None:
return members
for config in module.params['servicemembers']:
# Make a copy to update
config = copy.deepcopy(config)
config['servicegroupname'] = module.params['servicegroupname']
member_proxy = ConfigProxy(
actual=servicegroup_servicegroupmember_binding(),
client=client,
attribute_values_dict=config,
readwrite_attrs=readwrite_attrs,
readonly_attrs=readonly_attrs
)
members.append(member_proxy)
return members
def get_actual_service_members(client, module):
try:
# count() raises nitro exception instead of returning 0
count = servicegroup_servicegroupmember_binding.count(client, module.params['servicegroupname'])
if count > 0:
servicegroup_members = servicegroup_servicegroupmember_binding.get(client, module.params['servicegroupname'])
else:
servicegroup_members = []
except nitro_exception as e:
if e.errorcode == 258:
servicegroup_members = []
else:
raise
return servicegroup_members
def servicemembers_identical(client, module):
log('servicemembers_identical')
servicegroup_members = get_actual_service_members(client, module)
log('servicemembers %s' % servicegroup_members)
module_servicegroups = get_configured_service_members(client, module)
log('Number of service group members %s' % len(servicegroup_members))
if len(servicegroup_members) != len(module_servicegroups):
return False
# Fallthrough to member evaluation
identical_count = 0
for actual_member in servicegroup_members:
for member in module_servicegroups:
if member.has_equal_attributes(actual_member):
identical_count += 1
break
if identical_count != len(servicegroup_members):
return False
# Fallthrough to success
return True
def sync_service_members(client, module):
log('sync_service_members')
configured_service_members = get_configured_service_members(client, module)
actual_service_members = get_actual_service_members(client, module)
skip_add = []
skip_delete = []
# Find positions of identical service members
for (configured_index, configured_service) in enumerate(configured_service_members):
for (actual_index, actual_service) in enumerate(actual_service_members):
if configured_service.has_equal_attributes(actual_service):
skip_add.append(configured_index)
skip_delete.append(actual_index)
# Delete actual that are not identical to any configured
for (actual_index, actual_service) in enumerate(actual_service_members):
# Skip identical
if actual_index in skip_delete:
log('Skipping actual delete at index %s' % actual_index)
continue
# Fallthrouth to deletion
if all([
hasattr(actual_service, 'ip'),
actual_service.ip is not None,
hasattr(actual_service, 'servername'),
actual_service.servername is not None,
]):
actual_service.ip = None
actual_service.servicegroupname = module.params['servicegroupname']
servicegroup_servicegroupmember_binding.delete(client, actual_service)
# Add configured that are not already present in actual
for (configured_index, configured_service) in enumerate(configured_service_members):
# Skip identical
if configured_index in skip_add:
log('Skipping configured add at index %s' % configured_index)
continue
# Fallthrough to addition
configured_service.add()
def monitor_binding_equal(configured, actual):
if any([configured.monitorname != actual.monitor_name,
configured.servicegroupname != actual.servicegroupname,
configured.weight != float(actual.weight)]):
return False
return True
def get_configured_monitor_bindings(client, module):
log('Entering get_configured_monitor_bindings')
bindings = {}
if 'monitorbindings' in module.params and module.params['monitorbindings'] is not None:
for binding in module.params['monitorbindings']:
readwrite_attrs = [
'monitorname',
'servicegroupname',
'weight',
]
readonly_attrs = []
attribute_values_dict = copy.deepcopy(binding)
attribute_values_dict['servicegroupname'] = module.params['servicegroupname']
binding_proxy = ConfigProxy(
actual=lbmonitor_servicegroup_binding(),
client=client,
attribute_values_dict=attribute_values_dict,
readwrite_attrs=readwrite_attrs,
readonly_attrs=readonly_attrs,
)
key = attribute_values_dict['monitorname']
bindings[key] = binding_proxy
return bindings
def get_actual_monitor_bindings(client, module):
log('Entering get_actual_monitor_bindings')
bindings = {}
try:
# count() raises nitro exception instead of returning 0
count = servicegroup_lbmonitor_binding.count(client, module.params['servicegroupname'])
except nitro_exception as e:
if e.errorcode == 258:
return bindings
else:
raise
if count == 0:
return bindings
# Fallthrough to rest of execution
for binding in servicegroup_lbmonitor_binding.get(client, module.params['servicegroupname']):
log('Gettign actual monitor with name %s' % binding.monitor_name)
key = binding.monitor_name
bindings[key] = binding
return bindings
def monitor_bindings_identical(client, module):
log('Entering monitor_bindings_identical')
configured_bindings = get_configured_monitor_bindings(client, module)
actual_bindings = get_actual_monitor_bindings(client, module)
configured_key_set = set(configured_bindings.keys())
actual_key_set = set(actual_bindings.keys())
symmetrical_diff = configured_key_set ^ actual_key_set
for default_monitor in ('tcp-default', 'ping-default'):
if default_monitor in symmetrical_diff:
log('Excluding %s monitor from key comparison' % default_monitor)
symmetrical_diff.remove(default_monitor)
if len(symmetrical_diff) > 0:
return False
# Compare key to key
for key in configured_key_set:
configured_proxy = configured_bindings[key]
# Follow nscli convention for missing weight value
if not hasattr(configured_proxy, 'weight'):
configured_proxy.weight = 1
log('configured_proxy %s' % [configured_proxy.monitorname, configured_proxy.servicegroupname, configured_proxy.weight])
log('actual_bindings %s' % [actual_bindings[key].monitor_name, actual_bindings[key].servicegroupname, actual_bindings[key].weight])
if not monitor_binding_equal(configured_proxy, actual_bindings[key]):
return False
# Fallthrought to success
return True
def sync_monitor_bindings(client, module):
log('Entering sync_monitor_bindings')
actual_bindings = get_actual_monitor_bindings(client, module)
# Exclude default monitors from deletion
for monitorname in ('tcp-default', 'ping-default'):
if monitorname in actual_bindings:
del actual_bindings[monitorname]
configured_bindings = get_configured_monitor_bindings(client, module)
to_remove = list(set(actual_bindings.keys()) - set(configured_bindings.keys()))
to_add = list(set(configured_bindings.keys()) - set(actual_bindings.keys()))
to_modify = list(set(configured_bindings.keys()) & set(actual_bindings.keys()))
# Delete existing and modifiable bindings
for key in to_remove + to_modify:
binding = actual_bindings[key]
b = lbmonitor_servicegroup_binding()
b.monitorname = binding.monitor_name
b.servicegroupname = module.params['servicegroupname']
# Cannot remove default monitor bindings
if b.monitorname in ('tcp-default', 'ping-default'):
continue
lbmonitor_servicegroup_binding.delete(client, b)
# Add new and modified bindings
for key in to_add + to_modify:
binding = configured_bindings[key]
log('Adding %s' % binding.monitorname)
binding.add()
def diff(client, module, servicegroup_proxy):
servicegroup_list = servicegroup.get_filtered(client, 'servicegroupname:%s' % module.params['servicegroupname'])
diff_object = servicegroup_proxy.diff_object(servicegroup_list[0])
return diff_object
def do_state_change(client, module, servicegroup_proxy):
if module.params['disabled']:
log('Disabling service')
result = servicegroup.disable(client, servicegroup_proxy.actual)
else:
log('Enabling service')
result = servicegroup.enable(client, servicegroup_proxy.actual)
return result
def main():
module_specific_arguments = dict(
servicegroupname=dict(type='str'),
servicetype=dict(
type='str',
choices=[
'HTTP',
'FTP',
'TCP',
'UDP',
'SSL',
'SSL_BRIDGE',
'SSL_TCP',
'DTLS',
'NNTP',
'RPCSVR',
'DNS',
'ADNS',
'SNMP',
'RTSP',
'DHCPRA',
'ANY',
'SIP_UDP',
'SIP_TCP',
'SIP_SSL',
'DNS_TCP',
'ADNS_TCP',
'MYSQL',
'MSSQL',
'ORACLE',
'RADIUS',
'RADIUSListener',
'RDP',
'DIAMETER',
'SSL_DIAMETER',
'TFTP',
'SMPP',
'PPTP',
'GRE',
'SYSLOGTCP',
'SYSLOGUDP',
'FIX',
'SSL_FIX',
]
),
cachetype=dict(
type='str',
choices=[
'TRANSPARENT',
'REVERSE',
'FORWARD',
]
),
maxclient=dict(type='float'),
maxreq=dict(type='float'),
cacheable=dict(type='bool'),
cip=dict(
type='str',
choices=[
'enabled',
'disabled',
]
),
cipheader=dict(type='str'),
usip=dict(type='bool'),
pathmonitor=dict(type='bool'),
pathmonitorindv=dict(type='bool'),
useproxyport=dict(type='bool'),
healthmonitor=dict(type='bool'),
sp=dict(type='bool'),
rtspsessionidremap=dict(type='bool'),
clttimeout=dict(type='float'),
svrtimeout=dict(type='float'),
cka=dict(type='bool'),
tcpb=dict(type='bool'),
cmp=dict(type='bool'),
maxbandwidth=dict(type='float'),
monthreshold=dict(type='float'),
downstateflush=dict(
type='str',
choices=[
'enabled',
'disabled',
]
),
tcpprofilename=dict(type='str'),
httpprofilename=dict(type='str'),
comment=dict(type='str'),
appflowlog=dict(
type='str',
choices=[
'enabled',
'disabled',
]
),
netprofile=dict(type='str'),
autoscale=dict(
type='str',
choices=[
'DISABLED',
'DNS',
'POLICY',
]
),
memberport=dict(type='int'),
graceful=dict(type='bool'),
)
hand_inserted_arguments = dict(
servicemembers=dict(type='list'),
monitorbindings=dict(type='list'),
disabled=dict(
type='bool',
default=False,
),
)
argument_spec = dict()
argument_spec.update(netscaler_common_arguments)
argument_spec.update(module_specific_arguments)
argument_spec.update(hand_inserted_arguments)
module = AnsibleModule(
argument_spec=argument_spec,
supports_check_mode=True,
)
module_result = dict(
changed=False,
failed=False,
loglines=loglines,
)
# Fail the module if imports failed
if not PYTHON_SDK_IMPORTED:
module.fail_json(msg='Could not load nitro python sdk')
# Fallthrough to rest of execution
client = get_nitro_client(module)
try:
client.login()
except nitro_exception as e:
msg = "nitro exception during login. errorcode=%s, message=%s" % (str(e.errorcode), e.message)
module.fail_json(msg=msg)
except Exception as e:
if str(type(e)) == "<class 'requests.exceptions.ConnectionError'>":
module.fail_json(msg='Connection error %s' % str(e))
elif str(type(e)) == "<class 'requests.exceptions.SSLError'>":
module.fail_json(msg='SSL Error %s' % str(e))
else:
module.fail_json(msg='Unexpected error during login %s' % str(e))
# Instantiate service group configuration object
readwrite_attrs = [
'servicegroupname',
'servicetype',
'cachetype',
'maxclient',
'maxreq',
'cacheable',
'cip',
'cipheader',
'usip',
'pathmonitor',
'pathmonitorindv',
'useproxyport',
'healthmonitor',
'sp',
'rtspsessionidremap',
'clttimeout',
'svrtimeout',
'cka',
'tcpb',
'cmp',
'maxbandwidth',
'monthreshold',
'downstateflush',
'tcpprofilename',
'httpprofilename',
'comment',
'appflowlog',
'netprofile',
'autoscale',
'memberport',
'graceful',
]
readonly_attrs = [
'numofconnections',
'serviceconftype',
'value',
'svrstate',
'ip',
'monstatcode',
'monstatparam1',
'monstatparam2',
'monstatparam3',
'statechangetimemsec',
'stateupdatereason',
'clmonowner',
'clmonview',
'groupcount',
'riseapbrstatsmsgcode2',
'serviceipstr',
'servicegroupeffectivestate'
]
immutable_attrs = [
'servicegroupname',
'servicetype',
'cachetype',
'td',
'cipheader',
'state',
'autoscale',
'memberport',
'servername',
'port',
'serverid',
'monitor_name_svc',
'dup_weight',
'riseapbrstatsmsgcode',
'delay',
'graceful',
'includemembers',
'newname',
]
transforms = {
'pathmonitorindv': ['bool_yes_no'],
'cacheable': ['bool_yes_no'],
'cka': ['bool_yes_no'],
'pathmonitor': ['bool_yes_no'],
'tcpb': ['bool_yes_no'],
'sp': ['bool_on_off'],
'usip': ['bool_yes_no'],
'healthmonitor': ['bool_yes_no'],
'useproxyport': ['bool_yes_no'],
'rtspsessionidremap': ['bool_on_off'],
'graceful': ['bool_yes_no'],
'cmp': ['bool_yes_no'],
'cip': [lambda v: v.upper()],
'downstateflush': [lambda v: v.upper()],
'appflowlog': [lambda v: v.upper()],
}
# Instantiate config proxy
servicegroup_proxy = ConfigProxy(
actual=servicegroup(),
client=client,
attribute_values_dict=module.params,
readwrite_attrs=readwrite_attrs,
readonly_attrs=readonly_attrs,
immutable_attrs=immutable_attrs,
transforms=transforms,
)
try:
if module.params['state'] == 'present':
log('Applying actions for state present')
if not servicegroup_exists(client, module):
if not module.check_mode:
log('Adding service group')
servicegroup_proxy.add()
if module.params['save_config']:
client.save_config()
module_result['changed'] = True
elif not servicegroup_identical(client, module, servicegroup_proxy):
# Check if we try to change value of immutable attributes
diff_dict = diff(client, module, servicegroup_proxy)
immutables_changed = get_immutables_intersection(servicegroup_proxy, diff_dict.keys())
if immutables_changed != []:
msg = 'Cannot update immutable attributes %s. Must delete and recreate entity.' % (immutables_changed,)
module.fail_json(msg=msg, diff=diff_dict, **module_result)
if not module.check_mode:
servicegroup_proxy.update()
if module.params['save_config']:
client.save_config()
module_result['changed'] = True
else:
module_result['changed'] = False
# Check bindings
if not monitor_bindings_identical(client, module):
if not module.check_mode:
sync_monitor_bindings(client, module)
if module.params['save_config']:
client.save_config()
module_result['changed'] = True
if not servicemembers_identical(client, module):
if not module.check_mode:
sync_service_members(client, module)
if module.params['save_config']:
client.save_config()
module_result['changed'] = True
if not module.check_mode:
res = do_state_change(client, module, servicegroup_proxy)
if res.errorcode != 0:
msg = 'Error when setting disabled state. errorcode: %s message: %s' % (res.errorcode, res.message)
module.fail_json(msg=msg, **module_result)
# Sanity check for state
if not module.check_mode:
log('Sanity checks for state present')
if not servicegroup_exists(client, module):
module.fail_json(msg='Service group is not present', **module_result)
if not servicegroup_identical(client, module, servicegroup_proxy):
module.fail_json(
msg='Service group is not identical to configuration',
diff=diff(client, module, servicegroup_proxy),
**module_result
)
if not servicemembers_identical(client, module):
module.fail_json(msg='Service group members differ from configuration', **module_result)
if not monitor_bindings_identical(client, module):
module.fail_json(msg='Monitor bindings are not identical', **module_result)
elif module.params['state'] == 'absent':
log('Applying actions for state absent')
if servicegroup_exists(client, module):
if not module.check_mode:
servicegroup_proxy.delete()
if module.params['save_config']:
client.save_config()
module_result['changed'] = True
else:
module_result['changed'] = False
# Sanity check for state
if not module.check_mode:
log('Sanity checks for state absent')
if servicegroup_exists(client, module):
module.fail_json(msg='Service group is present', **module_result)
except nitro_exception as e:
msg = "nitro exception errorcode=" + str(e.errorcode) + ",message=" + e.message
module.fail_json(msg=msg, **module_result)
client.logout()
module.exit_json(**module_result)
if __name__ == "__main__":
main()