Add netscaler_gslb_vserver (#27640)

This commit is contained in:
George Nikolopoulos 2017-08-03 14:32:10 +03:00 committed by John R Barker
parent 60b3f80c6c
commit e2f907ae3e
18 changed files with 2256 additions and 0 deletions

View file

@ -0,0 +1,946 @@
#!/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 = {'status': ['preview'],
'supported_by': 'community',
'metadata_version': '1.0'}
DOCUMENTATION = '''
---
module: netscaler_gslb_vserver
short_description: Configure gslb vserver entities in Netscaler.
description:
- Configure gslb vserver entities in Netscaler.
version_added: "2.4.0"
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 DISABLED.
- When set to C(no) the GSLB Vserver 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 = '''
'''
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.module_utils.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',
]
# 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,
)
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()

View file

@ -0,0 +1,6 @@
---
testcase: "*"
test_cases: []
nitro_user: nsroot
nitro_pass: nsroot

View file

@ -0,0 +1,7 @@
---
- { include: testbed.yaml, state: present }
- { include: nitro.yaml, tags: ['nitro'] }
- { include: testbed.yaml, state: absent }

View file

@ -0,0 +1,14 @@
- name: collect all nitro test cases
find:
paths: "{{ role_path }}/tests/nitro"
patterns: "{{ testcase }}.yaml"
register: test_cases
- name: set test_items
set_fact: test_items="{{ test_cases.files | map(attribute='path') | list }}"
- name: run test case
include: "{{ test_case_to_run }}"
with_items: "{{ test_items }}"
loop_control:
loop_var: test_case_to_run

View file

@ -0,0 +1,39 @@
---
- name: Setup gslb site
netscaler_gslb_site:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
state: "{{ state }}"
sitename: gslb-site-1
siteipaddress: 192.168.1.1
sitetype: LOCAL
publicip: 192.168.1.1
metricexchange: ENABLED
nwmetricexchange: ENABLED
sessionexchange: ENABLED
triggermonitor: ALWAYS
delegate_to: localhost
register: result
- name: Setup gslb service
delegate_to: localhost
register: result
netscaler_gslb_service:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
state: "{{ state }}"
servicename: gslb-service-1
servicetype: HTTP
sitename: gslb-site-1
ipaddress: 10.10.10.11
port: 80

View file

@ -0,0 +1,9 @@
---
- include: "{{ role_path }}/tests/nitro/flap_disabled/setup.yaml"
vars:
check_mode: no
- include: "{{ role_path }}/tests/nitro/flap_disabled/remove.yaml"
vars:
check_mode: no

View file

@ -0,0 +1,12 @@
---
- name: remove http lb vserver
register: result
check_mode: "{{ check_mode }}"
delegate_to: localhost
netscaler_gslb_vserver:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
state: absent
name: gslb-vserver-2

View file

@ -0,0 +1,39 @@
---
- name: flap gslb vserver
register: result
check_mode: "{{ check_mode }}"
delegate_to: localhost
netscaler_gslb_vserver:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
name: gslb-vserver-2
servicetype: HTTP
lbmethod: SOURCEIPHASH
netmask: 255.255.255.0
v6persistmasklen: 128
disabled: "{{ item|int % 2 }}"
with_sequence: count=20
delay: 1
- name: flap gslb vserver
register: result
check_mode: "{{ check_mode }}"
delegate_to: localhost
netscaler_gslb_vserver:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
name: gslb-vserver-2
servicetype: HTTP
lbmethod: SOURCEIPHASH
netmask: 255.255.255.0
v6persistmasklen: 128
disabled: "{{ item|int % 2 }}"
with_sequence: count=20
delay: 5

View file

@ -0,0 +1,141 @@
---
- include: "{{ role_path }}/tests/nitro/http/setup.yaml"
vars:
check_mode: yes
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/http/setup.yaml"
vars:
check_mode: no
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/http/setup.yaml"
vars:
check_mode: yes
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/http/setup.yaml"
vars:
check_mode: no
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/http/update.yaml"
vars:
check_mode: yes
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/http/update.yaml"
vars:
check_mode: no
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/http/update.yaml"
vars:
check_mode: yes
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/http/update.yaml"
vars:
check_mode: no
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/http/update_domainbinding.yaml"
vars:
check_mode: yes
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/http/update_domainbinding.yaml"
vars:
check_mode: no
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/http/update_domainbinding.yaml"
vars:
check_mode: yes
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/http/update_domainbinding.yaml"
vars:
check_mode: no
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/http/update_gslbservice_binding.yaml"
vars:
check_mode: yes
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/http/update_gslbservice_binding.yaml"
vars:
check_mode: no
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/http/update_gslbservice_binding.yaml"
vars:
check_mode: yes
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/http/update_gslbservice_binding.yaml"
vars:
check_mode: no
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/http/remove.yaml"
vars:
check_mode: yes
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/http/remove.yaml"
vars:
check_mode: no
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/http/remove.yaml"
vars:
check_mode: yes
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/http/remove.yaml"
vars:
check_mode: no
- assert:
that: not result|changed

View file

@ -0,0 +1,15 @@
---
- name: Remove gslb vserver
delegate_to: localhost
register: result
check_mode: "{{ check_mode }}"
netscaler_gslb_vserver:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
state: absent
name: gslb-vserver-1

View file

@ -0,0 +1,46 @@
---
- name: Setup gslb vserver
delegate_to: localhost
register: result
check_mode: "{{ check_mode }}"
netscaler_gslb_vserver:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
name: gslb-vserver-1
servicetype: HTTP
dnsrecordtype: A
lbmethod: ROUNDROBIN
backuplbmethod: RTT
tolerance: 50
persistencetype: NONE
persistenceid: 500
persistmask: 255.255.255.0
v6persistmasklen: 128
timeout: 1000
mir: DISABLED
disableprimaryondown: DISABLED
dynamicweight: DISABLED
considereffectivestate: NONE
comment: some comment
somethod: CONNECTION
sopersistence: DISABLED
sopersistencetimeout: 100
sothreshold: 5000
sobackupaction: DROP
appflowlog: DISABLED
domain_bindings:
- domainname: example.com
cookietimeout: 100
backupip: 10.10.10.10
ttl: 100
sitedomainttl: 200
service_bindings:
- weight: 100
servicename: gslb-service-1

View file

@ -0,0 +1,46 @@
---
- name: Setup gslb vserver
delegate_to: localhost
register: result
check_mode: "{{ check_mode }}"
netscaler_gslb_vserver:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
name: gslb-vserver-1
servicetype: HTTP
dnsrecordtype: A
lbmethod: ROUNDROBIN
backuplbmethod: RTT
tolerance: 50
persistencetype: NONE
persistenceid: 500
persistmask: 255.255.255.0
v6persistmasklen: 128
timeout: 500
mir: DISABLED
disableprimaryondown: DISABLED
dynamicweight: DISABLED
considereffectivestate: NONE
comment: some comment
somethod: CONNECTION
sopersistence: DISABLED
sopersistencetimeout: 100
sothreshold: 5000
sobackupaction: DROP
appflowlog: DISABLED
domain_bindings:
- domainname: example.com
cookietimeout: 100
backupip: 10.10.10.10
ttl: 100
sitedomainttl: 200
service_bindings:
- weight: 100
servicename: gslb-service-1

View file

@ -0,0 +1,46 @@
---
- name: Setup gslb vserver
delegate_to: localhost
register: result
check_mode: "{{ check_mode }}"
netscaler_gslb_vserver:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
name: gslb-vserver-1
servicetype: HTTP
dnsrecordtype: A
lbmethod: ROUNDROBIN
backuplbmethod: RTT
tolerance: 50
persistencetype: NONE
persistenceid: 500
persistmask: 255.255.255.0
v6persistmasklen: 128
timeout: 500
mir: DISABLED
disableprimaryondown: DISABLED
dynamicweight: DISABLED
considereffectivestate: NONE
comment: some comment
somethod: CONNECTION
sopersistence: DISABLED
sopersistencetimeout: 100
sothreshold: 5000
sobackupaction: DROP
appflowlog: DISABLED
domain_bindings:
- domainname: anotherexample.com
cookietimeout: 100
backupip: 10.10.10.10
ttl: 100
sitedomainttl: 200
service_bindings:
- weight: 100
servicename: gslb-service-1

View file

@ -0,0 +1,46 @@
---
- name: Setup gslb vserver
delegate_to: localhost
register: result
check_mode: "{{ check_mode }}"
netscaler_gslb_vserver:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
name: gslb-vserver-1
servicetype: HTTP
dnsrecordtype: A
lbmethod: ROUNDROBIN
backuplbmethod: RTT
tolerance: 50
persistencetype: NONE
persistenceid: 500
persistmask: 255.255.255.0
v6persistmasklen: 128
timeout: 500
mir: DISABLED
disableprimaryondown: DISABLED
dynamicweight: DISABLED
considereffectivestate: NONE
comment: some comment
somethod: CONNECTION
sopersistence: DISABLED
sopersistencetimeout: 100
sothreshold: 5000
sobackupaction: DROP
appflowlog: DISABLED
domain_bindings:
- domainname: example.com
cookietimeout: 100
backupip: 10.10.10.10
ttl: 200
sitedomainttl: 200
service_bindings:
- weight: 50
servicename: gslb-service-1

View file

@ -0,0 +1,57 @@
---
- include: "{{ role_path }}/tests/nitro/sourceiphash/setup.yaml"
vars:
check_mode: yes
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/sourceiphash/setup.yaml"
vars:
check_mode: no
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/sourceiphash/setup.yaml"
vars:
check_mode: yes
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/sourceiphash/setup.yaml"
vars:
check_mode: no
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/sourceiphash/remove.yaml"
vars:
check_mode: yes
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/sourceiphash/remove.yaml"
vars:
check_mode: no
- assert:
that: result|changed
- include: "{{ role_path }}/tests/nitro/sourceiphash/remove.yaml"
vars:
check_mode: yes
- assert:
that: not result|changed
- include: "{{ role_path }}/tests/nitro/sourceiphash/remove.yaml"
vars:
check_mode: no
- assert:
that: not result|changed

View file

@ -0,0 +1,16 @@
---
- name: Remove sourceiphash glsb vserver
delegate_to: localhost
register: result
check_mode: "{{ check_mode }}"
netscaler_gslb_vserver:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
state: absent
name: gslb-vserver-2

View file

@ -0,0 +1,18 @@
---
- name: Setup sourceiphash gslb vserver
delegate_to: localhost
register: result
check_mode: "{{ check_mode }}"
netscaler_gslb_vserver:
nitro_user: "{{nitro_user}}"
nitro_pass: "{{nitro_pass}}"
nsip: "{{nsip}}"
name: gslb-vserver-2
servicetype: HTTP
lbmethod: SOURCEIPHASH
netmask: 255.255.255.0
v6persistmasklen: 128

View file

@ -0,0 +1,753 @@
# Copyright (c) 2017 Citrix Systems
#
# 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/>.
#
from ansible.compat.tests.mock import patch, Mock, MagicMock, call
from .netscaler_module import TestModule, nitro_base_patcher, set_module_args
import sys
if sys.version_info[:2] != (2, 6):
import requests
class TestNetscalerGSLBVserverModule(TestModule):
@classmethod
def setUpClass(cls):
class MockException(Exception):
pass
cls.MockException = MockException
m = MagicMock()
nssrc_modules_mock = {
'nssrc.com.citrix.netscaler.nitro.resource.config.gslb': m,
'nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver': m,
'nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver.gslbvserver': m,
'nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver_gslbservice_binding': m,
'nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver_gslbservice_binding.gslbvserver_gslbservice_binding': m,
'nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver_domain_binding': m,
'nssrc.com.citrix.netscaler.nitro.resource.config.gslb.gslbvserver_domain_binding.gslbvserver_domain_binding': m,
}
cls.nitro_specific_patcher = patch.dict(sys.modules, nssrc_modules_mock)
cls.nitro_base_patcher = nitro_base_patcher
@classmethod
def tearDownClass(cls):
cls.nitro_base_patcher.stop()
cls.nitro_specific_patcher.stop()
def setUp(self):
self.nitro_base_patcher.start()
self.nitro_specific_patcher.start()
# Setup minimal required arguments to pass AnsibleModule argument parsing
def tearDown(self):
self.nitro_base_patcher.stop()
self.nitro_specific_patcher.stop()
def test_graceful_nitro_api_import_error(self):
# Stop nitro api patching to cause ImportError
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
self.nitro_base_patcher.stop()
self.nitro_specific_patcher.stop()
from ansible.modules.network.netscaler import netscaler_gslb_vserver
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertEqual(result['msg'], 'Could not load nitro python sdk')
def test_graceful_nitro_error_on_login(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
class MockException(Exception):
def __init__(self, *args, **kwargs):
self.errorcode = 0
self.message = ''
client_mock = Mock()
client_mock.login = Mock(side_effect=MockException)
m = Mock(return_value=client_mock)
with patch('ansible.modules.network.netscaler.netscaler_gslb_vserver.get_nitro_client', m):
with patch('ansible.modules.network.netscaler.netscaler_gslb_vserver.nitro_exception', MockException):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertTrue(result['msg'].startswith('nitro exception'), msg='nitro exception during login not handled properly')
def test_graceful_no_connection_error(self):
if sys.version_info[:2] == (2, 6):
self.skipTest('requests library not available under python2.6')
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
class MockException(Exception):
pass
client_mock = Mock()
attrs = {'login.side_effect': requests.exceptions.ConnectionError}
client_mock.configure_mock(**attrs)
m = Mock(return_value=client_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
nitro_exception=MockException,
):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertTrue(result['msg'].startswith('Connection error'), msg='Connection error was not handled gracefully')
def test_graceful_login_error(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
if sys.version_info[:2] == (2, 6):
self.skipTest('requests library not available under python2.6')
class MockException(Exception):
pass
client_mock = Mock()
attrs = {'login.side_effect': requests.exceptions.SSLError}
client_mock.configure_mock(**attrs)
m = Mock(return_value=client_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
nitro_exception=MockException,
):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertTrue(result['msg'].startswith('SSL Error'), msg='SSL Error was not handled gracefully')
def test_ensure_feature_is_enabled_called(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
gslb_service_proxy_mock = Mock()
ensure_feature_is_enabled_mock = Mock()
client_mock = Mock()
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=Mock(return_value=client_mock),
gslb_vserver_exists=Mock(side_effect=[False, True]),
gslb_vserver_identical=Mock(side_effect=[True]),
nitro_exception=self.MockException,
ensure_feature_is_enabled=ensure_feature_is_enabled_mock,
do_state_change=Mock(return_value=Mock(errorcode=0)),
ConfigProxy=Mock(return_value=gslb_service_proxy_mock),
):
self.module = netscaler_gslb_vserver
self.exited()
ensure_feature_is_enabled_mock.assert_called_with(client_mock, 'GSLB')
def test_save_config_called_on_state_present(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
gslb_service_proxy_mock = Mock()
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
gslb_vserver_exists=Mock(side_effect=[False, True]),
gslb_vserver_identical=Mock(side_effect=[True]),
do_state_change=Mock(return_value=Mock(errorcode=0)),
nitro_exception=self.MockException,
ensure_feature_is_enabled=Mock(),
ConfigProxy=Mock(return_value=gslb_service_proxy_mock),
):
self.module = netscaler_gslb_vserver
self.exited()
self.assertIn(call.save_config(), client_mock.mock_calls)
def test_save_config_called_on_state_absent(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='absent',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
gslb_service_proxy_mock = Mock()
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
gslb_vserver_exists=Mock(side_effect=[True, False]),
nitro_exception=self.MockException,
ensure_feature_is_enabled=Mock(),
ConfigProxy=Mock(return_value=gslb_service_proxy_mock),
):
self.module = netscaler_gslb_vserver
self.exited()
self.assertIn(call.save_config(), client_mock.mock_calls)
def test_save_config_not_called_on_state_present(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
save_config=False,
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
gslb_service_proxy_mock = Mock()
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
gslb_vserver_exists=Mock(side_effect=[False, True]),
gslb_vserver_identical=Mock(side_effect=[True]),
nitro_exception=self.MockException,
do_state_change=Mock(return_value=Mock(errorcode=0)),
ensure_feature_is_enabled=Mock(),
ConfigProxy=Mock(return_value=gslb_service_proxy_mock),
):
self.module = netscaler_gslb_vserver
self.exited()
self.assertNotIn(call.save_config(), client_mock.mock_calls)
def test_save_config_not_called_on_state_absent(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='absent',
save_config=False,
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
gslb_service_proxy_mock = Mock()
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
gslb_vserver_exists=Mock(side_effect=[True, False]),
nitro_exception=self.MockException,
ensure_feature_is_enabled=Mock(),
ConfigProxy=Mock(return_value=gslb_service_proxy_mock),
):
self.module = netscaler_gslb_vserver
self.exited()
self.assertNotIn(call.save_config(), client_mock.mock_calls)
def test_new_gslb_vserver_execution_flow(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
gslb_vserver_exists=Mock(side_effect=[False, True]),
gslb_vserver_identical=Mock(side_effect=[True]),
nitro_exception=self.MockException,
do_state_change=Mock(return_value=Mock(errorcode=0)),
ensure_feature_is_enabled=Mock(),
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
self.exited()
gslb_service_proxy_mock.assert_has_calls([call.add()])
def test_modified_gslb_vserver_execution_flow(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
diff_list=Mock(return_value={}),
get_immutables_intersection=Mock(return_value=[]),
gslb_vserver_exists=Mock(side_effect=[True, True]),
gslb_vserver_identical=Mock(side_effect=[False, False, True]),
ensure_feature_is_enabled=Mock(),
domain_bindings_identical=Mock(side_effect=[True, True, True]),
service_bindings_identical=Mock(side_effect=[True, True, True]),
do_state_change=Mock(return_value=Mock(errorcode=0)),
nitro_exception=self.MockException,
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
self.exited()
gslb_service_proxy_mock.assert_has_calls([call.update()])
def test_absent_gslb_vserver_execution_flow(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='absent',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
diff_list=Mock(return_value={}),
get_immutables_intersection=Mock(return_value=[]),
gslb_vserver_exists=Mock(side_effect=[True, False]),
gslb_vserver_identical=Mock(side_effect=[False, True]),
ensure_feature_is_enabled=Mock(),
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
self.exited()
gslb_service_proxy_mock.assert_has_calls([call.delete()])
def test_present_gslb_vserver_identical_flow(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
diff_list=Mock(return_value={}),
get_immutables_intersection=Mock(return_value=[]),
gslb_vserver_exists=Mock(side_effect=[True, True]),
gslb_vserver_identical=Mock(side_effect=[True, True]),
do_state_change=Mock(return_value=Mock(errorcode=0)),
nitro_exception=self.MockException,
ensure_feature_is_enabled=Mock(),
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
self.exited()
gslb_service_proxy_mock.assert_not_called()
def test_present_gslb_vserver_domain_bindings_error_flow(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
diff_list=Mock(return_value={}),
get_immutables_intersection=Mock(return_value=[]),
gslb_vserver_exists=Mock(side_effect=[True, True]),
gslb_vserver_identical=Mock(side_effect=[True, True, True]),
domain_bindings_identical=Mock(side_effect=[False, False, False]),
do_state_change=Mock(return_value=Mock(errorcode=0)),
nitro_exception=self.MockException,
ensure_feature_is_enabled=Mock(),
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertEqual(result['msg'], 'Domain bindings differ from configured')
self.assertTrue(result['failed'])
def test_present_gslb_vserver_service_bindings_error_flow(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
diff_list=Mock(return_value={}),
get_immutables_intersection=Mock(return_value=[]),
gslb_vserver_exists=Mock(side_effect=[True, True]),
gslb_vserver_identical=Mock(side_effect=[True, True, True]),
service_bindings_identical=Mock(side_effect=[False, False, False]),
do_state_change=Mock(return_value=Mock(errorcode=0)),
nitro_exception=self.MockException,
ensure_feature_is_enabled=Mock(),
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertEqual(result['msg'], 'Service bindings differ from configured')
self.assertTrue(result['failed'])
def test_absent_gslb_vserver_noop_flow(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='absent',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
get_nitro_client=m,
diff_list=Mock(return_value={}),
get_immutables_intersection=Mock(return_value=[]),
gslb_vserver_exists=Mock(side_effect=[False, False]),
gslb_vserver_identical=Mock(side_effect=[False, False]),
nitro_exception=self.MockException,
ensure_feature_is_enabled=Mock(),
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
self.exited()
gslb_service_proxy_mock.assert_not_called()
def test_present_gslb_vserver_failed_update(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
nitro_exception=self.MockException,
get_nitro_client=m,
diff_list=Mock(return_value={}),
get_immutables_intersection=Mock(return_value=[]),
gslb_vserver_exists=Mock(side_effect=[True, True]),
gslb_vserver_identical=Mock(side_effect=[False, False, False]),
do_state_change=Mock(return_value=Mock(errorcode=0)),
ensure_feature_is_enabled=Mock(),
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertEqual(result['msg'], 'GSLB Vserver differs from configured')
self.assertTrue(result['failed'])
def test_present_gslb_vserver_failed_create(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
nitro_exception=self.MockException,
get_nitro_client=m,
diff_list=Mock(return_value={}),
get_immutables_intersection=Mock(return_value=[]),
do_state_change=Mock(return_value=Mock(errorcode=0)),
gslb_vserver_exists=Mock(side_effect=[False, False]),
gslb_vserver_identical=Mock(side_effect=[False, False]),
ensure_feature_is_enabled=Mock(),
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertEqual(result['msg'], 'GSLB Vserver does not exist')
self.assertTrue(result['failed'])
def test_present_gslb_vserver_update_immutable_attribute(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
nitro_exception=self.MockException,
get_nitro_client=m,
diff_list=Mock(return_value={}),
get_immutables_intersection=Mock(return_value=['domain']),
gslb_vserver_exists=Mock(side_effect=[True, True]),
gslb_vserver_identical=Mock(side_effect=[False, False]),
ensure_feature_is_enabled=Mock(),
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertEqual(result['msg'], 'Cannot update immutable attributes [\'domain\']')
self.assertTrue(result['failed'])
def test_absent_gslb_vserver_failed_delete(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='absent',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
client_mock = Mock()
m = Mock(return_value=client_mock)
glsb_service_proxy_attrs = {
'diff_object.return_value': {},
}
gslb_service_proxy_mock = Mock()
gslb_service_proxy_mock.configure_mock(**glsb_service_proxy_attrs)
config_proxy_mock = Mock(return_value=gslb_service_proxy_mock)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
nitro_exception=self.MockException,
get_nitro_client=m,
diff_list=Mock(return_value={}),
get_immutables_intersection=Mock(return_value=[]),
gslb_vserver_exists=Mock(side_effect=[True, True]),
gslb_vserver_identical=Mock(side_effect=[False, False]),
ensure_feature_is_enabled=Mock(),
ConfigProxy=config_proxy_mock,
):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertEqual(result['msg'], 'GSLB Vserver still exists')
self.assertTrue(result['failed'])
def test_graceful_nitro_exception_state_present(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='present',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
class MockException(Exception):
def __init__(self, *args, **kwargs):
self.errorcode = 0
self.message = ''
m = Mock(side_effect=MockException)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
gslb_vserver_exists=m,
ensure_feature_is_enabled=Mock(),
nitro_exception=MockException
):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertTrue(
result['msg'].startswith('nitro exception'),
msg='Nitro exception not caught on operation absent'
)
def test_graceful_nitro_exception_state_absent(self):
set_module_args(dict(
nitro_user='user',
nitro_pass='pass',
nsip='1.1.1.1',
state='absent',
))
from ansible.modules.network.netscaler import netscaler_gslb_vserver
class MockException(Exception):
def __init__(self, *args, **kwargs):
self.errorcode = 0
self.message = ''
m = Mock(side_effect=MockException)
with patch.multiple(
'ansible.modules.network.netscaler.netscaler_gslb_vserver',
gslb_vserver_exists=m,
ensure_feature_is_enabled=Mock(),
nitro_exception=MockException
):
self.module = netscaler_gslb_vserver
result = self.failed()
self.assertTrue(
result['msg'].startswith('nitro exception'),
msg='Nitro exception not caught on operation absent'
)