mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
956 lines
33 KiB
Python
956 lines
33 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_gslb_vserver
|
||
|
short_description: Configure gslb vserver entities in Netscaler.
|
||
|
description:
|
||
|
- Configure gslb vserver entities in Netscaler.
|
||
|
|
||
|
|
||
|
author: George Nikolopoulos (@giorgos-nikolopoulos)
|
||
|
|
||
|
options:
|
||
|
|
||
|
name:
|
||
|
description:
|
||
|
- >-
|
||
|
Name for the GSLB virtual server. Must begin with an ASCII alphanumeric or underscore C(_) character,
|
||
|
and must contain only ASCII alphanumeric, underscore C(_), hash C(#), period C(.), space, colon C(:), at C(@),
|
||
|
equals C(=), and hyphen C(-) characters. Can be changed after the virtual server is created.
|
||
|
- "Minimum length = 1"
|
||
|
|
||
|
servicetype:
|
||
|
choices:
|
||
|
- 'HTTP'
|
||
|
- 'FTP'
|
||
|
- 'TCP'
|
||
|
- 'UDP'
|
||
|
- 'SSL'
|
||
|
- 'SSL_BRIDGE'
|
||
|
- 'SSL_TCP'
|
||
|
- 'NNTP'
|
||
|
- 'ANY'
|
||
|
- 'SIP_UDP'
|
||
|
- 'SIP_TCP'
|
||
|
- 'SIP_SSL'
|
||
|
- 'RADIUS'
|
||
|
- 'RDP'
|
||
|
- 'RTSP'
|
||
|
- 'MYSQL'
|
||
|
- 'MSSQL'
|
||
|
- 'ORACLE'
|
||
|
description:
|
||
|
- "Protocol used by services bound to the virtual server."
|
||
|
- >-
|
||
|
|
||
|
dnsrecordtype:
|
||
|
choices:
|
||
|
- 'A'
|
||
|
- 'AAAA'
|
||
|
- 'CNAME'
|
||
|
- 'NAPTR'
|
||
|
description:
|
||
|
- "DNS record type to associate with the GSLB virtual server's domain name."
|
||
|
- "Default value: A"
|
||
|
- "Possible values = A, AAAA, CNAME, NAPTR"
|
||
|
|
||
|
lbmethod:
|
||
|
choices:
|
||
|
- 'ROUNDROBIN'
|
||
|
- 'LEASTCONNECTION'
|
||
|
- 'LEASTRESPONSETIME'
|
||
|
- 'SOURCEIPHASH'
|
||
|
- 'LEASTBANDWIDTH'
|
||
|
- 'LEASTPACKETS'
|
||
|
- 'STATICPROXIMITY'
|
||
|
- 'RTT'
|
||
|
- 'CUSTOMLOAD'
|
||
|
description:
|
||
|
- "Load balancing method for the GSLB virtual server."
|
||
|
- "Default value: LEASTCONNECTION"
|
||
|
- >-
|
||
|
Possible values = ROUNDROBIN, LEASTCONNECTION, LEASTRESPONSETIME, SOURCEIPHASH, LEASTBANDWIDTH,
|
||
|
LEASTPACKETS, STATICPROXIMITY, RTT, CUSTOMLOAD
|
||
|
|
||
|
backuplbmethod:
|
||
|
choices:
|
||
|
- 'ROUNDROBIN'
|
||
|
- 'LEASTCONNECTION'
|
||
|
- 'LEASTRESPONSETIME'
|
||
|
- 'SOURCEIPHASH'
|
||
|
- 'LEASTBANDWIDTH'
|
||
|
- 'LEASTPACKETS'
|
||
|
- 'STATICPROXIMITY'
|
||
|
- 'RTT'
|
||
|
- 'CUSTOMLOAD'
|
||
|
description:
|
||
|
- >-
|
||
|
Backup load balancing method. Becomes operational if the primary load balancing method fails or
|
||
|
cannot be used. Valid only if the primary method is based on either round-trip time (RTT) or static
|
||
|
proximity.
|
||
|
|
||
|
netmask:
|
||
|
description:
|
||
|
- "IPv4 network mask for use in the SOURCEIPHASH load balancing method."
|
||
|
- "Minimum length = 1"
|
||
|
|
||
|
v6netmasklen:
|
||
|
description:
|
||
|
- >-
|
||
|
Number of bits to consider, in an IPv6 source IP address, for creating the hash that is required by
|
||
|
the C(SOURCEIPHASH) load balancing method.
|
||
|
- "Default value: C(128)"
|
||
|
- "Minimum value = C(1)"
|
||
|
- "Maximum value = C(128)"
|
||
|
|
||
|
tolerance:
|
||
|
description:
|
||
|
- >-
|
||
|
Site selection tolerance, in milliseconds, for implementing the RTT load balancing method. If a
|
||
|
site's RTT deviates from the lowest RTT by more than the specified tolerance, the site is not
|
||
|
considered when the NetScaler appliance makes a GSLB decision. The appliance implements the round
|
||
|
robin method of global server load balancing between sites whose RTT values are within the specified
|
||
|
tolerance. If the tolerance is 0 (zero), the appliance always sends clients the IP address of the
|
||
|
site with the lowest RTT.
|
||
|
- "Minimum value = C(0)"
|
||
|
- "Maximum value = C(100)"
|
||
|
|
||
|
persistencetype:
|
||
|
choices:
|
||
|
- 'SOURCEIP'
|
||
|
- 'NONE'
|
||
|
description:
|
||
|
- "Use source IP address based persistence for the virtual server."
|
||
|
- >-
|
||
|
After the load balancing method selects a service for the first packet, the IP address received in
|
||
|
response to the DNS query is used for subsequent requests from the same client.
|
||
|
|
||
|
persistenceid:
|
||
|
description:
|
||
|
- >-
|
||
|
The persistence ID for the GSLB virtual server. The ID is a positive integer that enables GSLB sites
|
||
|
to identify the GSLB virtual server, and is required if source IP address based or spill over based
|
||
|
persistence is enabled on the virtual server.
|
||
|
- "Minimum value = C(0)"
|
||
|
- "Maximum value = C(65535)"
|
||
|
|
||
|
persistmask:
|
||
|
description:
|
||
|
- >-
|
||
|
The optional IPv4 network mask applied to IPv4 addresses to establish source IP address based
|
||
|
persistence.
|
||
|
- "Minimum length = 1"
|
||
|
|
||
|
v6persistmasklen:
|
||
|
description:
|
||
|
- >-
|
||
|
Number of bits to consider in an IPv6 source IP address when creating source IP address based
|
||
|
persistence sessions.
|
||
|
- "Default value: C(128)"
|
||
|
- "Minimum value = C(1)"
|
||
|
- "Maximum value = C(128)"
|
||
|
|
||
|
timeout:
|
||
|
description:
|
||
|
- "Idle time, in minutes, after which a persistence entry is cleared."
|
||
|
- "Default value: C(2)"
|
||
|
- "Minimum value = C(2)"
|
||
|
- "Maximum value = C(1440)"
|
||
|
|
||
|
mir:
|
||
|
choices:
|
||
|
- 'enabled'
|
||
|
- 'disabled'
|
||
|
description:
|
||
|
- "Include multiple IP addresses in the DNS responses sent to clients."
|
||
|
|
||
|
disableprimaryondown:
|
||
|
choices:
|
||
|
- 'enabled'
|
||
|
- 'disabled'
|
||
|
description:
|
||
|
- >-
|
||
|
Continue to direct traffic to the backup chain even after the primary GSLB virtual server returns to
|
||
|
the UP state. Used when spillover is configured for the virtual server.
|
||
|
|
||
|
dynamicweight:
|
||
|
choices:
|
||
|
- 'SERVICECOUNT'
|
||
|
- 'SERVICEWEIGHT'
|
||
|
- 'DISABLED'
|
||
|
description:
|
||
|
- >-
|
||
|
Specify if the appliance should consider the service count, service weights, or ignore both when
|
||
|
using weight-based load balancing methods. The state of the number of services bound to the virtual
|
||
|
server help the appliance to select the service.
|
||
|
|
||
|
considereffectivestate:
|
||
|
choices:
|
||
|
- 'NONE'
|
||
|
- 'STATE_ONLY'
|
||
|
description:
|
||
|
- >-
|
||
|
If the primary state of all bound GSLB services is DOWN, consider the effective states of all the
|
||
|
GSLB services, obtained through the Metrics Exchange Protocol (MEP), when determining the state of
|
||
|
the GSLB virtual server. To consider the effective state, set the parameter to STATE_ONLY. To
|
||
|
disregard the effective state, set the parameter to NONE.
|
||
|
- >-
|
||
|
The effective state of a GSLB service is the ability of the corresponding virtual server to serve
|
||
|
traffic. The effective state of the load balancing virtual server, which is transferred to the GSLB
|
||
|
service, is UP even if only one virtual server in the backup chain of virtual servers is in the UP
|
||
|
state.
|
||
|
|
||
|
comment:
|
||
|
description:
|
||
|
- "Any comments that you might want to associate with the GSLB virtual server."
|
||
|
|
||
|
somethod:
|
||
|
choices:
|
||
|
- 'CONNECTION'
|
||
|
- 'DYNAMICCONNECTION'
|
||
|
- 'BANDWIDTH'
|
||
|
- 'HEALTH'
|
||
|
- 'NONE'
|
||
|
description:
|
||
|
- "Type of threshold that, when exceeded, triggers spillover. Available settings function as follows:"
|
||
|
- "* C(CONNECTION) - Spillover occurs when the number of client connections exceeds the threshold."
|
||
|
- >-
|
||
|
* C(DYNAMICCONNECTION) - Spillover occurs when the number of client connections at the GSLB virtual
|
||
|
server exceeds the sum of the maximum client (Max Clients) settings for bound GSLB services. Do not
|
||
|
specify a spillover threshold for this setting, because the threshold is implied by the Max Clients
|
||
|
settings of the bound GSLB services.
|
||
|
- >-
|
||
|
* C(BANDWIDTH) - Spillover occurs when the bandwidth consumed by the GSLB virtual server's incoming and
|
||
|
outgoing traffic exceeds the threshold.
|
||
|
- >-
|
||
|
* C(HEALTH) - Spillover occurs when the percentage of weights of the GSLB services that are UP drops
|
||
|
below the threshold. For example, if services gslbSvc1, gslbSvc2, and gslbSvc3 are bound to a virtual
|
||
|
server, with weights 1, 2, and 3, and the spillover threshold is 50%, spillover occurs if gslbSvc1
|
||
|
and gslbSvc3 or gslbSvc2 and gslbSvc3 transition to DOWN.
|
||
|
- "* C(NONE) - Spillover does not occur."
|
||
|
|
||
|
sopersistence:
|
||
|
choices:
|
||
|
- 'enabled'
|
||
|
- 'disabled'
|
||
|
description:
|
||
|
- >-
|
||
|
If spillover occurs, maintain source IP address based persistence for both primary and backup GSLB
|
||
|
virtual servers.
|
||
|
|
||
|
sopersistencetimeout:
|
||
|
description:
|
||
|
- "Timeout for spillover persistence, in minutes."
|
||
|
- "Default value: C(2)"
|
||
|
- "Minimum value = C(2)"
|
||
|
- "Maximum value = C(1440)"
|
||
|
|
||
|
sothreshold:
|
||
|
description:
|
||
|
- >-
|
||
|
Threshold at which spillover occurs. Specify an integer for the CONNECTION spillover method, a
|
||
|
bandwidth value in kilobits per second for the BANDWIDTH method (do not enter the units), or a
|
||
|
percentage for the HEALTH method (do not enter the percentage symbol).
|
||
|
- "Minimum value = C(1)"
|
||
|
- "Maximum value = C(4294967287)"
|
||
|
|
||
|
sobackupaction:
|
||
|
choices:
|
||
|
- 'DROP'
|
||
|
- 'ACCEPT'
|
||
|
- 'REDIRECT'
|
||
|
description:
|
||
|
- >-
|
||
|
Action to be performed if spillover is to take effect, but no backup chain to spillover is usable or
|
||
|
exists.
|
||
|
|
||
|
appflowlog:
|
||
|
choices:
|
||
|
- 'enabled'
|
||
|
- 'disabled'
|
||
|
description:
|
||
|
- "Enable logging appflow flow information."
|
||
|
|
||
|
domain_bindings:
|
||
|
description:
|
||
|
- >-
|
||
|
List of bindings for domains for this glsb vserver.
|
||
|
suboptions:
|
||
|
cookietimeout:
|
||
|
description:
|
||
|
- Timeout, in minutes, for the GSLB site cookie.
|
||
|
|
||
|
domainname:
|
||
|
description:
|
||
|
- Domain name for which to change the time to live (TTL) and/or backup service IP address.
|
||
|
|
||
|
ttl:
|
||
|
description:
|
||
|
- Time to live (TTL) for the domain.
|
||
|
|
||
|
sitedomainttl:
|
||
|
description:
|
||
|
- >-
|
||
|
TTL, in seconds, for all internally created site domains (created when a site prefix is
|
||
|
configured on a GSLB service) that are associated with this virtual server.
|
||
|
- Minimum value = C(1)
|
||
|
|
||
|
service_bindings:
|
||
|
description:
|
||
|
- List of bindings for gslb services bound to this gslb virtual server.
|
||
|
suboptions:
|
||
|
servicename:
|
||
|
description:
|
||
|
- Name of the GSLB service for which to change the weight.
|
||
|
weight:
|
||
|
description:
|
||
|
- Weight to assign to the GSLB service.
|
||
|
|
||
|
disabled:
|
||
|
description:
|
||
|
- When set to C(yes) the GSLB Vserver state will be set to C(disabled).
|
||
|
- When set to C(no) the GSLB Vserver state will be set to C(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:
|
||
|
- community.general.netscaler
|
||
|
|
||
|
requirements:
|
||
|
- nitro python sdk
|
||
|
'''
|
||
|
|
||
|
EXAMPLES = '''
|
||
|
'''
|
||
|
|
||
|
RETURN = '''
|
||
|
'''
|
||
|
|
||
|
|
||
|
import copy
|
||
|
|
||
|
try:
|
||
|
from nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver import gslbvserver
|
||
|
from nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver_gslbservice_binding import gslbvserver_gslbservice_binding
|
||
|
from nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver_domain_binding import gslbvserver_domain_binding
|
||
|
from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception
|
||
|
PYTHON_SDK_IMPORTED = True
|
||
|
except ImportError as e:
|
||
|
PYTHON_SDK_IMPORTED = False
|
||
|
|
||
|
from ansible.module_utils.basic import AnsibleModule
|
||
|
from ansible_collections.community.general.plugins.module_utils.network.netscaler.netscaler import (
|
||
|
ConfigProxy,
|
||
|
get_nitro_client,
|
||
|
netscaler_common_arguments,
|
||
|
log,
|
||
|
loglines,
|
||
|
ensure_feature_is_enabled,
|
||
|
get_immutables_intersection,
|
||
|
complete_missing_attributes
|
||
|
)
|
||
|
|
||
|
|
||
|
gslbvserver_domain_binding_rw_attrs = [
|
||
|
'name',
|
||
|
'domainname',
|
||
|
'backupipflag',
|
||
|
'cookietimeout',
|
||
|
'backupip',
|
||
|
'ttl',
|
||
|
'sitedomainttl',
|
||
|
'cookie_domainflag',
|
||
|
]
|
||
|
|
||
|
gslbvserver_gslbservice_binding_rw_attrs = [
|
||
|
'name',
|
||
|
'servicename',
|
||
|
'weight',
|
||
|
]
|
||
|
|
||
|
|
||
|
def get_actual_domain_bindings(client, module):
|
||
|
log('get_actual_domain_bindings')
|
||
|
# Get actual domain bindings and index them by domainname
|
||
|
actual_domain_bindings = {}
|
||
|
if gslbvserver_domain_binding.count(client, name=module.params['name']) != 0:
|
||
|
# Get all domain bindings associated with the named gslb vserver
|
||
|
fetched_domain_bindings = gslbvserver_domain_binding.get(client, name=module.params['name'])
|
||
|
# index by domainname
|
||
|
for binding in fetched_domain_bindings:
|
||
|
complete_missing_attributes(binding, gslbvserver_domain_binding_rw_attrs, fill_value=None)
|
||
|
actual_domain_bindings[binding.domainname] = binding
|
||
|
return actual_domain_bindings
|
||
|
|
||
|
|
||
|
def get_configured_domain_bindings_proxys(client, module):
|
||
|
log('get_configured_domain_bindings_proxys')
|
||
|
configured_domain_proxys = {}
|
||
|
# Get configured domain bindings and index them by domainname
|
||
|
if module.params['domain_bindings'] is not None:
|
||
|
for configured_domain_binding in module.params['domain_bindings']:
|
||
|
binding_values = copy.deepcopy(configured_domain_binding)
|
||
|
binding_values['name'] = module.params['name']
|
||
|
gslbvserver_domain_binding_proxy = ConfigProxy(
|
||
|
actual=gslbvserver_domain_binding(),
|
||
|
client=client,
|
||
|
attribute_values_dict=binding_values,
|
||
|
readwrite_attrs=gslbvserver_domain_binding_rw_attrs,
|
||
|
readonly_attrs=[],
|
||
|
)
|
||
|
configured_domain_proxys[configured_domain_binding['domainname']] = gslbvserver_domain_binding_proxy
|
||
|
return configured_domain_proxys
|
||
|
|
||
|
|
||
|
def sync_domain_bindings(client, module):
|
||
|
log('sync_domain_bindings')
|
||
|
|
||
|
actual_domain_bindings = get_actual_domain_bindings(client, module)
|
||
|
configured_domain_proxys = get_configured_domain_bindings_proxys(client, module)
|
||
|
|
||
|
# Delete actual bindings not in configured bindings
|
||
|
for domainname, actual_domain_binding in actual_domain_bindings.items():
|
||
|
if domainname not in configured_domain_proxys.keys():
|
||
|
log('Deleting absent binding for domain %s' % domainname)
|
||
|
gslbvserver_domain_binding.delete(client, actual_domain_binding)
|
||
|
|
||
|
# Delete actual bindings that differ from configured
|
||
|
for proxy_key, binding_proxy in configured_domain_proxys.items():
|
||
|
if proxy_key in actual_domain_bindings:
|
||
|
actual_binding = actual_domain_bindings[proxy_key]
|
||
|
if not binding_proxy.has_equal_attributes(actual_binding):
|
||
|
log('Deleting differing binding for domain %s' % binding_proxy.domainname)
|
||
|
gslbvserver_domain_binding.delete(client, actual_binding)
|
||
|
log('Adding anew binding for domain %s' % binding_proxy.domainname)
|
||
|
binding_proxy.add()
|
||
|
|
||
|
# Add configured domains that are missing from actual
|
||
|
for proxy_key, binding_proxy in configured_domain_proxys.items():
|
||
|
if proxy_key not in actual_domain_bindings.keys():
|
||
|
log('Adding domain binding for domain %s' % binding_proxy.domainname)
|
||
|
binding_proxy.add()
|
||
|
|
||
|
|
||
|
def domain_bindings_identical(client, module):
|
||
|
log('domain_bindings_identical')
|
||
|
actual_domain_bindings = get_actual_domain_bindings(client, module)
|
||
|
configured_domain_proxys = get_configured_domain_bindings_proxys(client, module)
|
||
|
|
||
|
actual_keyset = set(actual_domain_bindings.keys())
|
||
|
configured_keyset = set(configured_domain_proxys.keys())
|
||
|
|
||
|
symmetric_difference = actual_keyset ^ configured_keyset
|
||
|
|
||
|
log('symmetric difference %s' % symmetric_difference)
|
||
|
if len(symmetric_difference) != 0:
|
||
|
return False
|
||
|
|
||
|
# Item for item equality test
|
||
|
for key, proxy in configured_domain_proxys.items():
|
||
|
diff = proxy.diff_object(actual_domain_bindings[key])
|
||
|
if 'backupipflag' in diff:
|
||
|
del diff['backupipflag']
|
||
|
if not len(diff) == 0:
|
||
|
return False
|
||
|
# Fallthrough to True result
|
||
|
return True
|
||
|
|
||
|
|
||
|
def get_actual_service_bindings(client, module):
|
||
|
log('get_actual_service_bindings')
|
||
|
# Get actual domain bindings and index them by domainname
|
||
|
actual_bindings = {}
|
||
|
if gslbvserver_gslbservice_binding.count(client, name=module.params['name']) != 0:
|
||
|
# Get all service bindings associated with the named gslb vserver
|
||
|
fetched_bindings = gslbvserver_gslbservice_binding.get(client, name=module.params['name'])
|
||
|
# index by servicename
|
||
|
for binding in fetched_bindings:
|
||
|
complete_missing_attributes(binding, gslbvserver_gslbservice_binding_rw_attrs, fill_value=None)
|
||
|
actual_bindings[binding.servicename] = binding
|
||
|
|
||
|
return actual_bindings
|
||
|
|
||
|
|
||
|
def get_configured_service_bindings(client, module):
|
||
|
log('get_configured_service_bindings_proxys')
|
||
|
configured_proxys = {}
|
||
|
# Get configured domain bindings and index them by domainname
|
||
|
if module.params['service_bindings'] is not None:
|
||
|
for configured_binding in module.params['service_bindings']:
|
||
|
binding_values = copy.deepcopy(configured_binding)
|
||
|
binding_values['name'] = module.params['name']
|
||
|
gslbvserver_service_binding_proxy = ConfigProxy(
|
||
|
actual=gslbvserver_gslbservice_binding(),
|
||
|
client=client,
|
||
|
attribute_values_dict=binding_values,
|
||
|
readwrite_attrs=gslbvserver_gslbservice_binding_rw_attrs,
|
||
|
readonly_attrs=[],
|
||
|
)
|
||
|
configured_proxys[configured_binding['servicename']] = gslbvserver_service_binding_proxy
|
||
|
return configured_proxys
|
||
|
|
||
|
|
||
|
def sync_service_bindings(client, module):
|
||
|
actual = get_actual_service_bindings(client, module)
|
||
|
configured = get_configured_service_bindings(client, module)
|
||
|
|
||
|
# Delete extraneous
|
||
|
extraneous_service_bindings = list(set(actual.keys()) - set(configured.keys()))
|
||
|
for servicename in extraneous_service_bindings:
|
||
|
log('Deleting missing binding from service %s' % servicename)
|
||
|
binding = actual[servicename]
|
||
|
binding.name = module.params['name']
|
||
|
gslbvserver_gslbservice_binding.delete(client, binding)
|
||
|
|
||
|
# Recreate different
|
||
|
common_service_bindings = list(set(actual.keys()) & set(configured.keys()))
|
||
|
for servicename in common_service_bindings:
|
||
|
proxy = configured[servicename]
|
||
|
binding = actual[servicename]
|
||
|
if not proxy.has_equal_attributes(actual):
|
||
|
log('Recreating differing service binding %s' % servicename)
|
||
|
gslbvserver_gslbservice_binding.delete(client, binding)
|
||
|
proxy.add()
|
||
|
|
||
|
# Add missing
|
||
|
missing_service_bindings = list(set(configured.keys()) - set(actual.keys()))
|
||
|
for servicename in missing_service_bindings:
|
||
|
proxy = configured[servicename]
|
||
|
log('Adding missing service binding %s' % servicename)
|
||
|
proxy.add()
|
||
|
|
||
|
|
||
|
def service_bindings_identical(client, module):
|
||
|
actual_bindings = get_actual_service_bindings(client, module)
|
||
|
configured_proxys = get_configured_service_bindings(client, module)
|
||
|
|
||
|
actual_keyset = set(actual_bindings.keys())
|
||
|
configured_keyset = set(configured_proxys.keys())
|
||
|
|
||
|
symmetric_difference = actual_keyset ^ configured_keyset
|
||
|
if len(symmetric_difference) != 0:
|
||
|
return False
|
||
|
|
||
|
# Item for item equality test
|
||
|
for key, proxy in configured_proxys.items():
|
||
|
if key in actual_bindings.keys():
|
||
|
if not proxy.has_equal_attributes(actual_bindings[key]):
|
||
|
return False
|
||
|
|
||
|
# Fallthrough to True result
|
||
|
return True
|
||
|
|
||
|
|
||
|
def gslb_vserver_exists(client, module):
|
||
|
if gslbvserver.count_filtered(client, 'name:%s' % module.params['name']) > 0:
|
||
|
return True
|
||
|
else:
|
||
|
return False
|
||
|
|
||
|
|
||
|
def gslb_vserver_identical(client, module, gslb_vserver_proxy):
|
||
|
gslb_vserver_list = gslbvserver.get_filtered(client, 'name:%s' % module.params['name'])
|
||
|
diff_dict = gslb_vserver_proxy.diff_object(gslb_vserver_list[0])
|
||
|
if len(diff_dict) != 0:
|
||
|
return False
|
||
|
else:
|
||
|
return True
|
||
|
|
||
|
|
||
|
def all_identical(client, module, gslb_vserver_proxy):
|
||
|
return (
|
||
|
gslb_vserver_identical(client, module, gslb_vserver_proxy) and
|
||
|
domain_bindings_identical(client, module) and
|
||
|
service_bindings_identical(client, module)
|
||
|
)
|
||
|
|
||
|
|
||
|
def diff_list(client, module, gslb_vserver_proxy):
|
||
|
gslb_vserver_list = gslbvserver.get_filtered(client, 'name:%s' % module.params['name'])
|
||
|
return gslb_vserver_proxy.diff_object(gslb_vserver_list[0])
|
||
|
|
||
|
|
||
|
def do_state_change(client, module, gslb_vserver_proxy):
|
||
|
if module.params['disabled']:
|
||
|
log('Disabling glsb_vserver')
|
||
|
result = gslbvserver.disable(client, gslb_vserver_proxy.actual)
|
||
|
else:
|
||
|
log('Enabling gslbvserver')
|
||
|
result = gslbvserver.enable(client, gslb_vserver_proxy.actual)
|
||
|
return result
|
||
|
|
||
|
|
||
|
def main():
|
||
|
|
||
|
module_specific_arguments = dict(
|
||
|
name=dict(type='str'),
|
||
|
servicetype=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'HTTP',
|
||
|
'FTP',
|
||
|
'TCP',
|
||
|
'UDP',
|
||
|
'SSL',
|
||
|
'SSL_BRIDGE',
|
||
|
'SSL_TCP',
|
||
|
'NNTP',
|
||
|
'ANY',
|
||
|
'SIP_UDP',
|
||
|
'SIP_TCP',
|
||
|
'SIP_SSL',
|
||
|
'RADIUS',
|
||
|
'RDP',
|
||
|
'RTSP',
|
||
|
'MYSQL',
|
||
|
'MSSQL',
|
||
|
'ORACLE',
|
||
|
]
|
||
|
),
|
||
|
dnsrecordtype=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'A',
|
||
|
'AAAA',
|
||
|
'CNAME',
|
||
|
'NAPTR',
|
||
|
]
|
||
|
),
|
||
|
lbmethod=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'ROUNDROBIN',
|
||
|
'LEASTCONNECTION',
|
||
|
'LEASTRESPONSETIME',
|
||
|
'SOURCEIPHASH',
|
||
|
'LEASTBANDWIDTH',
|
||
|
'LEASTPACKETS',
|
||
|
'STATICPROXIMITY',
|
||
|
'RTT',
|
||
|
'CUSTOMLOAD',
|
||
|
]
|
||
|
),
|
||
|
backuplbmethod=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'ROUNDROBIN',
|
||
|
'LEASTCONNECTION',
|
||
|
'LEASTRESPONSETIME',
|
||
|
'SOURCEIPHASH',
|
||
|
'LEASTBANDWIDTH',
|
||
|
'LEASTPACKETS',
|
||
|
'STATICPROXIMITY',
|
||
|
'RTT',
|
||
|
'CUSTOMLOAD',
|
||
|
]
|
||
|
),
|
||
|
netmask=dict(type='str'),
|
||
|
v6netmasklen=dict(type='float'),
|
||
|
tolerance=dict(type='float'),
|
||
|
persistencetype=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'SOURCEIP',
|
||
|
'NONE',
|
||
|
]
|
||
|
),
|
||
|
persistenceid=dict(type='float'),
|
||
|
persistmask=dict(type='str'),
|
||
|
v6persistmasklen=dict(type='float'),
|
||
|
timeout=dict(type='float'),
|
||
|
mir=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'enabled',
|
||
|
'disabled',
|
||
|
]
|
||
|
),
|
||
|
disableprimaryondown=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'enabled',
|
||
|
'disabled',
|
||
|
]
|
||
|
),
|
||
|
dynamicweight=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'SERVICECOUNT',
|
||
|
'SERVICEWEIGHT',
|
||
|
'DISABLED',
|
||
|
]
|
||
|
),
|
||
|
considereffectivestate=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'NONE',
|
||
|
'STATE_ONLY',
|
||
|
]
|
||
|
),
|
||
|
comment=dict(type='str'),
|
||
|
somethod=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'CONNECTION',
|
||
|
'DYNAMICCONNECTION',
|
||
|
'BANDWIDTH',
|
||
|
'HEALTH',
|
||
|
'NONE',
|
||
|
]
|
||
|
),
|
||
|
sopersistence=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'enabled',
|
||
|
'disabled',
|
||
|
]
|
||
|
),
|
||
|
sopersistencetimeout=dict(type='float'),
|
||
|
sothreshold=dict(type='float'),
|
||
|
sobackupaction=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'DROP',
|
||
|
'ACCEPT',
|
||
|
'REDIRECT',
|
||
|
]
|
||
|
),
|
||
|
appflowlog=dict(
|
||
|
type='str',
|
||
|
choices=[
|
||
|
'enabled',
|
||
|
'disabled',
|
||
|
]
|
||
|
),
|
||
|
domainname=dict(type='str'),
|
||
|
cookie_domain=dict(type='str'),
|
||
|
)
|
||
|
|
||
|
hand_inserted_arguments = dict(
|
||
|
domain_bindings=dict(type='list'),
|
||
|
service_bindings=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))
|
||
|
|
||
|
readwrite_attrs = [
|
||
|
'name',
|
||
|
'servicetype',
|
||
|
'dnsrecordtype',
|
||
|
'lbmethod',
|
||
|
'backuplbmethod',
|
||
|
'netmask',
|
||
|
'v6netmasklen',
|
||
|
'tolerance',
|
||
|
'persistencetype',
|
||
|
'persistenceid',
|
||
|
'persistmask',
|
||
|
'v6persistmasklen',
|
||
|
'timeout',
|
||
|
'mir',
|
||
|
'disableprimaryondown',
|
||
|
'dynamicweight',
|
||
|
'considereffectivestate',
|
||
|
'comment',
|
||
|
'somethod',
|
||
|
'sopersistence',
|
||
|
'sopersistencetimeout',
|
||
|
'sothreshold',
|
||
|
'sobackupaction',
|
||
|
'appflowlog',
|
||
|
'cookie_domain',
|
||
|
]
|
||
|
|
||
|
readonly_attrs = [
|
||
|
'curstate',
|
||
|
'status',
|
||
|
'lbrrreason',
|
||
|
'iscname',
|
||
|
'sitepersistence',
|
||
|
'totalservices',
|
||
|
'activeservices',
|
||
|
'statechangetimesec',
|
||
|
'statechangetimemsec',
|
||
|
'tickssincelaststatechange',
|
||
|
'health',
|
||
|
'policyname',
|
||
|
'priority',
|
||
|
'gotopriorityexpression',
|
||
|
'type',
|
||
|
'vsvrbindsvcip',
|
||
|
'vsvrbindsvcport',
|
||
|
'__count',
|
||
|
]
|
||
|
|
||
|
immutable_attrs = [
|
||
|
'name',
|
||
|
'servicetype',
|
||
|
]
|
||
|
|
||
|
transforms = {
|
||
|
'mir': [lambda v: v.upper()],
|
||
|
'disableprimaryondown': [lambda v: v.upper()],
|
||
|
'sopersistence': [lambda v: v.upper()],
|
||
|
'appflowlog': [lambda v: v.upper()],
|
||
|
}
|
||
|
|
||
|
# Instantiate config proxy
|
||
|
gslb_vserver_proxy = ConfigProxy(
|
||
|
actual=gslbvserver(),
|
||
|
client=client,
|
||
|
attribute_values_dict=module.params,
|
||
|
readwrite_attrs=readwrite_attrs,
|
||
|
readonly_attrs=readonly_attrs,
|
||
|
immutable_attrs=immutable_attrs,
|
||
|
transforms=transforms,
|
||
|
)
|
||
|
|
||
|
try:
|
||
|
ensure_feature_is_enabled(client, 'GSLB')
|
||
|
# Apply appropriate state
|
||
|
if module.params['state'] == 'present':
|
||
|
log('Applying state present')
|
||
|
if not gslb_vserver_exists(client, module):
|
||
|
log('Creating object')
|
||
|
if not module.check_mode:
|
||
|
gslb_vserver_proxy.add()
|
||
|
sync_domain_bindings(client, module)
|
||
|
sync_service_bindings(client, module)
|
||
|
if module.params['save_config']:
|
||
|
client.save_config()
|
||
|
module_result['changed'] = True
|
||
|
elif not all_identical(client, module, gslb_vserver_proxy):
|
||
|
log('Entering update actions')
|
||
|
|
||
|
# Check if we try to change value of immutable attributes
|
||
|
if not gslb_vserver_identical(client, module, gslb_vserver_proxy):
|
||
|
log('Updating gslb vserver')
|
||
|
immutables_changed = get_immutables_intersection(gslb_vserver_proxy, diff_list(client, module, gslb_vserver_proxy).keys())
|
||
|
if immutables_changed != []:
|
||
|
module.fail_json(
|
||
|
msg='Cannot update immutable attributes %s' % (immutables_changed,),
|
||
|
diff=diff_list(client, module, gslb_vserver_proxy),
|
||
|
**module_result
|
||
|
)
|
||
|
if not module.check_mode:
|
||
|
gslb_vserver_proxy.update()
|
||
|
|
||
|
# Update domain bindings
|
||
|
if not domain_bindings_identical(client, module):
|
||
|
if not module.check_mode:
|
||
|
sync_domain_bindings(client, module)
|
||
|
|
||
|
# Update service bindings
|
||
|
if not service_bindings_identical(client, module):
|
||
|
if not module.check_mode:
|
||
|
sync_service_bindings(client, module)
|
||
|
|
||
|
module_result['changed'] = True
|
||
|
if not module.check_mode:
|
||
|
if module.params['save_config']:
|
||
|
client.save_config()
|
||
|
else:
|
||
|
module_result['changed'] = False
|
||
|
|
||
|
if not module.check_mode:
|
||
|
res = do_state_change(client, module, gslb_vserver_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:
|
||
|
if not gslb_vserver_exists(client, module):
|
||
|
module.fail_json(msg='GSLB Vserver does not exist', **module_result)
|
||
|
if not gslb_vserver_identical(client, module, gslb_vserver_proxy):
|
||
|
module.fail_json(msg='GSLB Vserver differs from configured', diff=diff_list(client, module, gslb_vserver_proxy), **module_result)
|
||
|
if not domain_bindings_identical(client, module):
|
||
|
module.fail_json(msg='Domain bindings differ from configured', diff=diff_list(client, module, gslb_vserver_proxy), **module_result)
|
||
|
if not service_bindings_identical(client, module):
|
||
|
module.fail_json(msg='Service bindings differ from configured', diff=diff_list(client, module, gslb_vserver_proxy), **module_result)
|
||
|
|
||
|
elif module.params['state'] == 'absent':
|
||
|
|
||
|
if gslb_vserver_exists(client, module):
|
||
|
if not module.check_mode:
|
||
|
gslb_vserver_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:
|
||
|
if gslb_vserver_exists(client, module):
|
||
|
module.fail_json(msg='GSLB Vserver still exists', **module_result)
|
||
|
|
||
|
except nitro_exception as e:
|
||
|
msg = "nitro exception errorcode=%s, message=%s" % (str(e.errorcode), e.message)
|
||
|
module.fail_json(msg=msg, **module_result)
|
||
|
|
||
|
client.logout()
|
||
|
module.exit_json(**module_result)
|
||
|
|
||
|
|
||
|
if __name__ == "__main__":
|
||
|
main()
|