diff --git a/lib/ansible/modules/network/netscaler/netscaler_lb_vserver.py b/lib/ansible/modules/network/netscaler/netscaler_lb_vserver.py
new file mode 100644
index 00000000000..a4663eeca97
--- /dev/null
+++ b/lib/ansible/modules/network/netscaler/netscaler_lb_vserver.py
@@ -0,0 +1,1940 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# 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 .
+#
+
+ANSIBLE_METADATA = {'metadata_version': '1.0',
+ 'status': ['preview'],
+ 'supported_by': 'community'}
+
+
+DOCUMENTATION = '''
+---
+module: netscaler_lb_vserver
+short_description: Manage load balancing vserver configuration
+description:
+ - Manage load balancing vserver configuration
+ - 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:
+
+ name:
+ description:
+ - >-
+ Name for the virtual server. Must begin with an ASCII alphanumeric or underscore C(_) character, and
+ must contain only ASCII alphanumeric, underscore, hash C(#), period C(.), space C( ), colon C(:), at sign
+ C(@), equal sign 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'
+ - 'DTLS'
+ - 'NNTP'
+ - 'DNS'
+ - 'DHCPRA'
+ - 'ANY'
+ - 'SIP_UDP'
+ - 'SIP_TCP'
+ - 'SIP_SSL'
+ - 'DNS_TCP'
+ - 'RTSP'
+ - 'PUSH'
+ - 'SSL_PUSH'
+ - 'RADIUS'
+ - 'RDP'
+ - 'MYSQL'
+ - 'MSSQL'
+ - 'DIAMETER'
+ - 'SSL_DIAMETER'
+ - 'TFTP'
+ - 'ORACLE'
+ - 'SMPP'
+ - 'SYSLOGTCP'
+ - 'SYSLOGUDP'
+ - 'FIX'
+ - 'SSL_FIX'
+ description:
+ - "Protocol used by the service (also called the service type)."
+
+ ipv46:
+ description:
+ - "IPv4 or IPv6 address to assign to the virtual server."
+
+ ippattern:
+ description:
+ - >-
+ IP address pattern, in dotted decimal notation, for identifying packets to be accepted by the virtual
+ server. The IP Mask parameter specifies which part of the destination IP address is matched against
+ the pattern. Mutually exclusive with the IP Address parameter.
+ - >-
+ For example, if the IP pattern assigned to the virtual server is C(198.51.100.0) and the IP mask is
+ C(255.255.240.0) (a forward mask), the first 20 bits in the destination IP addresses are matched with
+ the first 20 bits in the pattern. The virtual server accepts requests with IP addresses that range
+ from C(198.51.96.1) to C(198.51.111.254). You can also use a pattern such as C(0.0.2.2) and a mask such as
+ C(0.0.255.255) (a reverse mask).
+ - >-
+ If a destination IP address matches more than one IP pattern, the pattern with the longest match is
+ selected, and the associated virtual server processes the request. For example, if virtual servers
+ C(vs1) and C(vs2) have the same IP pattern, C(0.0.100.128), but different IP masks of C(0.0.255.255) and
+ C(0.0.224.255), a destination IP address of C(198.51.100.128) has the longest match with the IP pattern of
+ vs1. If a destination IP address matches two or more virtual servers to the same extent, the request
+ is processed by the virtual server whose port number matches the port number in the request.
+
+ ipmask:
+ description:
+ - >-
+ IP mask, in dotted decimal notation, for the IP Pattern parameter. Can have leading or trailing
+ non-zero octets (for example, C(255.255.240.0) or C(0.0.255.255)). Accordingly, the mask specifies whether
+ the first n bits or the last n bits of the destination IP address in a client request are to be
+ matched with the corresponding bits in the IP pattern. The former is called a forward mask. The
+ latter is called a reverse mask.
+
+ port:
+ description:
+ - "Port number for the virtual server."
+ - "Range C(1) - C(65535)"
+ - "* in CLI is represented as C(65535) in NITRO API"
+
+ range:
+ description:
+ - >-
+ Number of IP addresses that the appliance must generate and assign to the virtual server. The virtual
+ server then functions as a network virtual server, accepting traffic on any of the generated IP
+ addresses. The IP addresses are generated automatically, as follows:
+ - >-
+ * For a range of n, the last octet of the address specified by the IP Address parameter increments
+ n-1 times.
+ - "* If the last octet exceeds 255, it rolls over to 0 and the third octet increments by 1."
+ - >-
+ Note: The Range parameter assigns multiple IP addresses to one virtual server. To generate an array
+ of virtual servers, each of which owns only one IP address, use brackets in the IP Address and Name
+ parameters to specify the range. For example:
+ - "add lb vserver my_vserver[1-3] HTTP 192.0.2.[1-3] 80."
+ - "Minimum value = C(1)"
+ - "Maximum value = C(254)"
+
+ persistencetype:
+ choices:
+ - 'SOURCEIP'
+ - 'COOKIEINSERT'
+ - 'SSLSESSION'
+ - 'RULE'
+ - 'URLPASSIVE'
+ - 'CUSTOMSERVERID'
+ - 'DESTIP'
+ - 'SRCIPDESTIP'
+ - 'CALLID'
+ - 'RTSPSID'
+ - 'DIAMETER'
+ - 'FIXSESSION'
+ - 'NONE'
+ description:
+ - "Type of persistence for the virtual server. Available settings function as follows:"
+ - "* C(SOURCEIP) - Connections from the same client IP address belong to the same persistence session."
+ - >-
+ * C(COOKIEINSERT) - Connections that have the same HTTP Cookie, inserted by a Set-Cookie directive from
+ a server, belong to the same persistence session.
+ - "* C(SSLSESSION) - Connections that have the same SSL Session ID belong to the same persistence session."
+ - >-
+ * C(CUSTOMSERVERID) - Connections with the same server ID form part of the same session. For this
+ persistence type, set the Server ID (CustomServerID) parameter for each service and configure the
+ Rule parameter to identify the server ID in a request.
+ - "* C(RULE) - All connections that match a user defined rule belong to the same persistence session."
+ - >-
+ * C(URLPASSIVE) - Requests that have the same server ID in the URL query belong to the same persistence
+ session. The server ID is the hexadecimal representation of the IP address and port of the service to
+ which the request must be forwarded. This persistence type requires a rule to identify the server ID
+ in the request.
+ - "* C(DESTIP) - Connections to the same destination IP address belong to the same persistence session."
+ - >-
+ * C(SRCIPDESTIP) - Connections that have the same source IP address and destination IP address belong to
+ the same persistence session.
+ - "* C(CALLID) - Connections that have the same CALL-ID SIP header belong to the same persistence session."
+ - "* C(RTSPSID) - Connections that have the same RTSP Session ID belong to the same persistence session."
+ - >-
+ * FIXSESSION - Connections that have the same SenderCompID and TargetCompID values belong to the same
+ persistence session.
+
+ timeout:
+ description:
+ - "Time period for which a persistence session is in effect."
+ - "Minimum value = C(0)"
+ - "Maximum value = C(1440)"
+
+ persistencebackup:
+ choices:
+ - 'SOURCEIP'
+ - 'NONE'
+ description:
+ - >-
+ Backup persistence type for the virtual server. Becomes operational if the primary persistence
+ mechanism fails.
+
+ backuppersistencetimeout:
+ description:
+ - "Time period for which backup persistence is in effect."
+ - "Minimum value = C(2)"
+ - "Maximum value = C(1440)"
+
+ lbmethod:
+ choices:
+ - 'ROUNDROBIN'
+ - 'LEASTCONNECTION'
+ - 'LEASTRESPONSETIME'
+ - 'URLHASH'
+ - 'DOMAINHASH'
+ - 'DESTINATIONIPHASH'
+ - 'SOURCEIPHASH'
+ - 'SRCIPDESTIPHASH'
+ - 'LEASTBANDWIDTH'
+ - 'LEASTPACKETS'
+ - 'TOKEN'
+ - 'SRCIPSRCPORTHASH'
+ - 'LRTM'
+ - 'CALLIDHASH'
+ - 'CUSTOMLOAD'
+ - 'LEASTREQUEST'
+ - 'AUDITLOGHASH'
+ - 'STATICPROXIMITY'
+ description:
+ - "Load balancing method. The available settings function as follows:"
+ - >-
+ * C(ROUNDROBIN) - Distribute requests in rotation, regardless of the load. Weights can be assigned to
+ services to enforce weighted round robin distribution.
+ - "* C(LEASTCONNECTION) (default) - Select the service with the fewest connections."
+ - "* C(LEASTRESPONSETIME) - Select the service with the lowest average response time."
+ - "* C(LEASTBANDWIDTH) - Select the service currently handling the least traffic."
+ - "* C(LEASTPACKETS) - Select the service currently serving the lowest number of packets per second."
+ - "* C(CUSTOMLOAD) - Base service selection on the SNMP metrics obtained by custom load monitors."
+ - >-
+ * C(LRTM) - Select the service with the lowest response time. Response times are learned through
+ monitoring probes. This method also takes the number of active connections into account.
+ - >-
+ Also available are a number of hashing methods, in which the appliance extracts a predetermined
+ portion of the request, creates a hash of the portion, and then checks whether any previous requests
+ had the same hash value. If it finds a match, it forwards the request to the service that served
+ those previous requests. Following are the hashing methods:
+ - "* C(URLHASH) - Create a hash of the request URL (or part of the URL)."
+ - >-
+ * C(DOMAINHASH) - Create a hash of the domain name in the request (or part of the domain name). The
+ domain name is taken from either the URL or the Host header. If the domain name appears in both
+ locations, the URL is preferred. If the request does not contain a domain name, the load balancing
+ method defaults to C(LEASTCONNECTION).
+ - "* C(DESTINATIONIPHASH) - Create a hash of the destination IP address in the IP header."
+ - "* C(SOURCEIPHASH) - Create a hash of the source IP address in the IP header."
+ - >-
+ * C(TOKEN) - Extract a token from the request, create a hash of the token, and then select the service
+ to which any previous requests with the same token hash value were sent.
+ - >-
+ * C(SRCIPDESTIPHASH) - Create a hash of the string obtained by concatenating the source IP address and
+ destination IP address in the IP header.
+ - "* C(SRCIPSRCPORTHASH) - Create a hash of the source IP address and source port in the IP header."
+ - "* C(CALLIDHASH) - Create a hash of the SIP Call-ID header."
+
+ hashlength:
+ description:
+ - >-
+ Number of bytes to consider for the hash value used in the URLHASH and DOMAINHASH load balancing
+ methods.
+ - "Minimum value = C(1)"
+ - "Maximum value = C(4096)"
+
+ netmask:
+ description:
+ - >-
+ IPv4 subnet mask to apply to the destination IP address or source IP address when the load balancing
+ method is C(DESTINATIONIPHASH) or C(SOURCEIPHASH).
+ - "Minimum length = 1"
+
+ v6netmasklen:
+ description:
+ - >-
+ Number of bits to consider in an IPv6 destination or source IP address, for creating the hash that is
+ required by the C(DESTINATIONIPHASH) and C(SOURCEIPHASH) load balancing methods.
+ - "Minimum value = C(1)"
+ - "Maximum value = C(128)"
+
+ backuplbmethod:
+ choices:
+ - 'ROUNDROBIN'
+ - 'LEASTCONNECTION'
+ - 'LEASTRESPONSETIME'
+ - 'SOURCEIPHASH'
+ - 'LEASTBANDWIDTH'
+ - 'LEASTPACKETS'
+ - 'CUSTOMLOAD'
+ description:
+ - "Backup load balancing method. Becomes operational if the primary load balancing me"
+ - "thod fails or cannot be used."
+ - "Valid only if the primary method is based on static proximity."
+
+ cookiename:
+ description:
+ - >-
+ Use this parameter to specify the cookie name for C(COOKIE) peristence type. It specifies the name of
+ cookie with a maximum of 32 characters. If not specified, cookie name is internally generated.
+
+
+ listenpolicy:
+ description:
+ - >-
+ Default syntax expression identifying traffic accepted by the virtual server. Can be either an
+ expression (for example, C(CLIENT.IP.DST.IN_SUBNET(192.0.2.0/24)) or the name of a named expression. In
+ the above example, the virtual server accepts all requests whose destination IP address is in the
+ 192.0.2.0/24 subnet.
+
+ listenpriority:
+ description:
+ - >-
+ Integer specifying the priority of the listen policy. A higher number specifies a lower priority. If
+ a request matches the listen policies of more than one virtual server the virtual server whose listen
+ policy has the highest priority (the lowest priority number) accepts the request.
+ - "Minimum value = C(0)"
+ - "Maximum value = C(101)"
+
+ resrule:
+ description:
+ - >-
+ Default syntax expression specifying which part of a server's response to use for creating rule based
+ persistence sessions (persistence type RULE). Can be either an expression or the name of a named
+ expression.
+ - "Example:"
+ - "C(HTTP.RES.HEADER(\\"setcookie\\").VALUE(0).TYPECAST_NVLIST_T('=',';').VALUE(\\"server1\\"))."
+
+ persistmask:
+ description:
+ - "Persistence mask for IP based persistence types, for IPv4 virtual servers."
+ - "Minimum length = 1"
+
+ v6persistmasklen:
+ description:
+ - "Persistence mask for IP based persistence types, for IPv6 virtual servers."
+ - "Minimum value = C(1)"
+ - "Maximum value = C(128)"
+
+ rtspnat:
+ description:
+ - "Use network address translation (NAT) for RTSP data connections."
+ type: bool
+
+ m:
+ choices:
+ - 'IP'
+ - 'MAC'
+ - 'IPTUNNEL'
+ - 'TOS'
+ description:
+ - "Redirection mode for load balancing. Available settings function as follows:"
+ - >-
+ * C(IP) - Before forwarding a request to a server, change the destination IP address to the server's IP
+ address.
+ - >-
+ * C(MAC) - Before forwarding a request to a server, change the destination MAC address to the server's
+ MAC address. The destination IP address is not changed. MAC-based redirection mode is used mostly in
+ firewall load balancing deployments.
+ - >-
+ * C(IPTUNNEL) - Perform IP-in-IP encapsulation for client IP packets. In the outer IP headers, set the
+ destination IP address to the IP address of the server and the source IP address to the subnet IP
+ (SNIP). The client IP packets are not modified. Applicable to both IPv4 and IPv6 packets.
+ - "* C(TOS) - Encode the virtual server's TOS ID in the TOS field of the IP header."
+ - "You can use either the C(IPTUNNEL) or the C(TOS) option to implement Direct Server Return (DSR)."
+
+ tosid:
+ description:
+ - >-
+ TOS ID of the virtual server. Applicable only when the load balancing redirection mode is set to TOS.
+ - "Minimum value = C(1)"
+ - "Maximum value = C(63)"
+
+ datalength:
+ description:
+ - >-
+ Length of the token to be extracted from the data segment of an incoming packet, for use in the token
+ method of load balancing. The length of the token, specified in bytes, must not be greater than 24
+ KB. Applicable to virtual servers of type TCP.
+ - "Minimum value = C(1)"
+ - "Maximum value = C(100)"
+
+ dataoffset:
+ description:
+ - >-
+ Offset to be considered when extracting a token from the TCP payload. Applicable to virtual servers,
+ of type TCP, using the token method of load balancing. Must be within the first 24 KB of the TCP
+ payload.
+ - "Minimum value = C(0)"
+ - "Maximum value = C(25400)"
+
+ sessionless:
+ choices:
+ - 'ENABLED'
+ - 'DISABLED'
+ description:
+ - >-
+ Perform load balancing on a per-packet basis, without establishing sessions. Recommended for load
+ balancing of intrusion detection system (IDS) servers and scenarios involving direct server return
+ (DSR), where session information is unnecessary.
+
+ connfailover:
+ choices:
+ - 'DISABLED'
+ - 'STATEFUL'
+ - 'STATELESS'
+ description:
+ - >-
+ Mode in which the connection failover feature must operate for the virtual server. After a failover,
+ established TCP connections and UDP packet flows are kept active and resumed on the secondary
+ appliance. Clients remain connected to the same servers. Available settings function as follows:
+ - >-
+ * C(STATEFUL) - The primary appliance shares state information with the secondary appliance, in real
+ time, resulting in some runtime processing overhead.
+ - >-
+ * C(STATELESS) - State information is not shared, and the new primary appliance tries to re-create the
+ packet flow on the basis of the information contained in the packets it receives.
+ - "* C(DISABLED) - Connection failover does not occur."
+
+ redirurl:
+ description:
+ - "URL to which to redirect traffic if the virtual server becomes unavailable."
+ - >-
+ WARNING! Make sure that the domain in the URL does not match the domain specified for a content
+ switching policy. If it does, requests are continuously redirected to the unavailable virtual server.
+ - "Minimum length = 1"
+
+ cacheable:
+ description:
+ - >-
+ Route cacheable requests to a cache redirection virtual server. The load balancing virtual server can
+ forward requests only to a transparent cache redirection virtual server that has an IP address and
+ port combination of *:80, so such a cache redirection virtual server must be configured on the
+ appliance.
+ type: bool
+
+ clttimeout:
+ description:
+ - "Idle time, in seconds, after which a client connection is terminated."
+ - "Minimum value = C(0)"
+ - "Maximum value = C(31536000)"
+
+ 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."
+ - >-
+ * DYNAMICCONNECTION - Spillover occurs when the number of client connections at the virtual server
+ exceeds the sum of the maximum client (Max Clients) settings for bound services. Do not specify a
+ spillover threshold for this setting, because the threshold is implied by the Max Clients settings of
+ bound services.
+ - >-
+ * C(BANDWIDTH) - Spillover occurs when the bandwidth consumed by the virtual server's incoming and
+ outgoing traffic exceeds the threshold.
+ - >-
+ * C(HEALTH) - Spillover occurs when the percentage of weights of the services that are UP drops below
+ the threshold. For example, if services svc1, svc2, and svc3 are bound to a virtual server, with
+ weights 1, 2, and 3, and the spillover threshold is 50%, spillover occurs if svc1 and svc3 or svc2
+ and svc3 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 virtual
+ servers.
+
+ sopersistencetimeout:
+ description:
+ - "Timeout for spillover persistence, in minutes."
+ - "Minimum value = C(2)"
+ - "Maximum value = C(1440)"
+
+ healththreshold:
+ description:
+ - >-
+ Threshold in percent of active services below which vserver state is made down. If this threshold is
+ 0, vserver state will be up even if one bound service is up.
+ - "Minimum value = C(0)"
+ - "Maximum value = C(100)"
+
+ sothreshold:
+ description:
+ - >-
+ Threshold at which spillover occurs. Specify an integer for the C(CONNECTION) spillover method, a
+ bandwidth value in kilobits per second for the C(BANDWIDTH) method (do not enter the units), or a
+ percentage for the C(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.
+
+ redirectportrewrite:
+ choices:
+ - 'ENABLED'
+ - 'DISABLED'
+ description:
+ - "Rewrite the port and change the protocol to ensure successful HTTP redirects from services."
+
+ downstateflush:
+ choices:
+ - 'ENABLED'
+ - 'DISABLED'
+ description:
+ - >-
+ Flush all active transactions associated with a virtual server whose state transitions from UP to
+ DOWN. Do not enable this option for applications that must complete their transactions.
+
+ disableprimaryondown:
+ choices:
+ - 'ENABLED'
+ - 'DISABLED'
+ description:
+ - >-
+ If the primary virtual server goes down, do not allow it to return to primary status until manually
+ enabled.
+
+ insertvserveripport:
+ choices:
+ - 'OFF'
+ - 'VIPADDR'
+ - 'V6TOV4MAPPING'
+ description:
+ - >-
+ Insert an HTTP header, whose value is the IP address and port number of the virtual server, before
+ forwarding a request to the server. The format of the header is : _, where vipHeader is the name that you specify for the header. If the virtual
+ server has an IPv6 address, the address in the header is enclosed in brackets ([ and ]) to separate
+ it from the port number. If you have mapped an IPv4 address to a virtual server's IPv6 address, the
+ value of this parameter determines which IP address is inserted in the header, as follows:
+ - >-
+ * C(VIPADDR) - Insert the IP address of the virtual server in the HTTP header regardless of whether the
+ virtual server has an IPv4 address or an IPv6 address. A mapped IPv4 address, if configured, is
+ ignored.
+ - >-
+ * C(V6TOV4MAPPING) - Insert the IPv4 address that is mapped to the virtual server's IPv6 address. If a
+ mapped IPv4 address is not configured, insert the IPv6 address.
+ - "* C(OFF) - Disable header insertion."
+
+ vipheader:
+ description:
+ - "Name for the inserted header. The default name is vip-header."
+ - "Minimum length = 1"
+
+ authenticationhost:
+ description:
+ - >-
+ Fully qualified domain name (FQDN) of the authentication virtual server to which the user must be
+ redirected for authentication. Make sure that the Authentication parameter is set to ENABLED.
+ - "Minimum length = 3"
+ - "Maximum length = 252"
+
+ authentication:
+ description:
+ - "Enable or disable user authentication."
+ type: bool
+
+ authn401:
+ description:
+ - "Enable or disable user authentication with HTTP 401 responses."
+ type: bool
+
+ authnvsname:
+ description:
+ - "Name of an authentication virtual server with which to authenticate users."
+ - "Minimum length = 1"
+ - "Maximum length = 252"
+
+ push:
+ choices:
+ - 'ENABLED'
+ - 'DISABLED'
+ description:
+ - "Process traffic with the push virtual server that is bound to this load balancing virtual server."
+
+ pushvserver:
+ description:
+ - >-
+ Name of the load balancing virtual server, of type PUSH or SSL_PUSH, to which the server pushes
+ updates received on the load balancing virtual server that you are configuring.
+ - "Minimum length = 1"
+
+ pushlabel:
+ description:
+ - >-
+ Expression for extracting a label from the server's response. Can be either an expression or the name
+ of a named expression.
+
+ pushmulticlients:
+ description:
+ - >-
+ Allow multiple Web 2.0 connections from the same client to connect to the virtual server and expect
+ updates.
+ type: bool
+
+ tcpprofilename:
+ description:
+ - "Name of the TCP profile whose settings are to be applied to the virtual server."
+ - "Minimum length = 1"
+ - "Maximum length = 127"
+
+ httpprofilename:
+ description:
+ - "Name of the HTTP profile whose settings are to be applied to the virtual server."
+ - "Minimum length = 1"
+ - "Maximum length = 127"
+
+ dbprofilename:
+ description:
+ - "Name of the DB profile whose settings are to be applied to the virtual server."
+ - "Minimum length = 1"
+ - "Maximum length = 127"
+
+ comment:
+ description:
+ - "Any comments that you might want to associate with the virtual server."
+
+ l2conn:
+ description:
+ - >-
+ Use Layer 2 parameters (channel number, MAC address, and VLAN ID) in addition to the 4-tuple (::::) that is used to identify a connection. Allows
+ multiple TCP and non-TCP connections with the same 4-tuple to co-exist on the NetScaler appliance.
+ type: bool
+
+ oracleserverversion:
+ choices:
+ - '10G'
+ - '11G'
+ description:
+ - "Oracle server version."
+
+ mssqlserverversion:
+ choices:
+ - '70'
+ - '2000'
+ - '2000SP1'
+ - '2005'
+ - '2008'
+ - '2008R2'
+ - '2012'
+ - '2014'
+ description:
+ - >-
+ For a load balancing virtual server of type C(MSSQL), the Microsoft SQL Server version. Set this
+ parameter if you expect some clients to run a version different from the version of the database.
+ This setting provides compatibility between the client-side and server-side connections by ensuring
+ that all communication conforms to the server's version.
+
+ mysqlprotocolversion:
+ description:
+ - "MySQL protocol version that the virtual server advertises to clients."
+
+ mysqlserverversion:
+ description:
+ - "MySQL server version string that the virtual server advertises to clients."
+ - "Minimum length = 1"
+ - "Maximum length = 31"
+
+ mysqlcharacterset:
+ description:
+ - "Character set that the virtual server advertises to clients."
+
+ mysqlservercapabilities:
+ description:
+ - "Server capabilities that the virtual server advertises to clients."
+
+ appflowlog:
+ choices:
+ - 'ENABLED'
+ - 'DISABLED'
+ description:
+ - "Apply AppFlow logging to the virtual server."
+
+ netprofile:
+ description:
+ - >-
+ Name of the network profile to associate with the virtual server. If you set this parameter, the
+ virtual server uses only the IP addresses in the network profile as source IP addresses when
+ initiating connections with servers.
+ - "Minimum length = 1"
+ - "Maximum length = 127"
+
+ icmpvsrresponse:
+ choices:
+ - 'PASSIVE'
+ - 'ACTIVE'
+ description:
+ - >-
+ How the NetScaler appliance responds to ping requests received for an IP address that is common to
+ one or more virtual servers. Available settings function as follows:
+ - >-
+ * If set to C(PASSIVE) on all the virtual servers that share the IP address, the appliance always
+ responds to the ping requests.
+ - >-
+ * If set to C(ACTIVE) on all the virtual servers that share the IP address, the appliance responds to
+ the ping requests if at least one of the virtual servers is UP. Otherwise, the appliance does not
+ respond.
+ - >-
+ * If set to C(ACTIVE) on some virtual servers and PASSIVE on the others, the appliance responds if at
+ least one virtual server with the ACTIVE setting is UP. Otherwise, the appliance does not respond.
+ - >-
+ Note: This parameter is available at the virtual server level. A similar parameter, ICMP Response, is
+ available at the IP address level, for IPv4 addresses of type VIP. To set that parameter, use the add
+ ip command in the CLI or the Create IP dialog box in the GUI.
+
+ rhistate:
+ choices:
+ - 'PASSIVE'
+ - 'ACTIVE'
+ description:
+ - >-
+ Route Health Injection (RHI) functionality of the NetSaler appliance for advertising the route of the
+ VIP address associated with the virtual server. When Vserver RHI Level (RHI) parameter is set to
+ VSVR_CNTRLD, the following are different RHI behaviors for the VIP address on the basis of RHIstate
+ (RHI STATE) settings on the virtual servers associated with the VIP address:
+ - >-
+ * If you set C(rhistate) to C(PASSIVE) on all virtual servers, the NetScaler ADC always advertises the
+ route for the VIP address.
+ - >-
+ * If you set C(rhistate) to C(ACTIVE) on all virtual servers, the NetScaler ADC advertises the route for
+ the VIP address if at least one of the associated virtual servers is in UP state.
+ - >-
+ * If you set C(rhistate) to C(ACTIVE) on some and PASSIVE on others, the NetScaler ADC advertises the
+ route for the VIP address if at least one of the associated virtual servers, whose C(rhistate) set to
+ C(ACTIVE), is in UP state.
+
+ newservicerequest:
+ description:
+ - >-
+ Number of requests, or percentage of the load on existing services, by which to increase the load on
+ a new service at each interval in slow-start mode. A non-zero value indicates that slow-start is
+ applicable. A zero value indicates that the global RR startup parameter is applied. Changing the
+ value to zero will cause services currently in slow start to take the full traffic as determined by
+ the LB method. Subsequently, any new services added will use the global RR factor.
+
+ newservicerequestunit:
+ choices:
+ - 'PER_SECOND'
+ - 'PERCENT'
+ description:
+ - "Units in which to increment load at each interval in slow-start mode."
+
+ newservicerequestincrementinterval:
+ description:
+ - >-
+ Interval, in seconds, between successive increments in the load on a new service or a service whose
+ state has just changed from DOWN to UP. A value of 0 (zero) specifies manual slow start.
+ - "Minimum value = C(0)"
+ - "Maximum value = C(3600)"
+
+ minautoscalemembers:
+ description:
+ - "Minimum number of members expected to be present when vserver is used in Autoscale."
+ - "Minimum value = C(0)"
+ - "Maximum value = C(5000)"
+
+ maxautoscalemembers:
+ description:
+ - "Maximum number of members expected to be present when vserver is used in Autoscale."
+ - "Minimum value = C(0)"
+ - "Maximum value = C(5000)"
+
+ persistavpno:
+ description:
+ - "Persist AVP number for Diameter Persistency."
+ - "In case this AVP is not defined in Base RFC 3588 and it is nested inside a Grouped AVP,"
+ - "define a sequence of AVP numbers (max 3) in order of parent to child. So say persist AVP number X"
+ - "is nested inside AVP Y which is nested in Z, then define the list as Z Y X."
+ - "Minimum value = C(1)"
+
+ skippersistency:
+ choices:
+ - 'Bypass'
+ - 'ReLb'
+ - 'None'
+ description:
+ - >-
+ This argument decides the behavior incase the service which is selected from an existing persistence
+ session has reached threshold.
+
+ td:
+ description:
+ - >-
+ Integer value that uniquely identifies the traffic domain in which you want to configure the entity.
+ If you do not specify an ID, the entity becomes part of the default traffic domain, which has an ID
+ of 0.
+ - "Minimum value = C(0)"
+ - "Maximum value = C(4094)"
+
+ authnprofile:
+ description:
+ - "Name of the authentication profile to be used when authentication is turned on."
+
+ macmoderetainvlan:
+ choices:
+ - 'ENABLED'
+ - 'DISABLED'
+ description:
+ - "This option is used to retain vlan information of incoming packet when macmode is enabled."
+
+ dbslb:
+ choices:
+ - 'ENABLED'
+ - 'DISABLED'
+ description:
+ - "Enable database specific load balancing for MySQL and MSSQL service types."
+
+ dns64:
+ choices:
+ - 'ENABLED'
+ - 'DISABLED'
+ description:
+ - "This argument is for enabling/disabling the C(dns64) on lbvserver."
+
+ bypassaaaa:
+ description:
+ - >-
+ If this option is enabled while resolving DNS64 query AAAA queries are not sent to back end dns
+ server.
+ type: bool
+
+ recursionavailable:
+ description:
+ - >-
+ When set to YES, this option causes the DNS replies from this vserver to have the RA bit turned on.
+ Typically one would set this option to YES, when the vserver is load balancing a set of DNS servers
+ thatsupport recursive queries.
+ type: bool
+
+ processlocal:
+ choices:
+ - 'ENABLED'
+ - 'DISABLED'
+ description:
+ - >-
+ By turning on this option packets destined to a vserver in a cluster will not under go any steering.
+ Turn this option for single packet request response mode or when the upstream device is performing a
+ proper RSS for connection based distribution.
+
+ dnsprofilename:
+ description:
+ - >-
+ Name of the DNS profile to be associated with the VServer. DNS profile properties will be applied to
+ the transactions processed by a VServer. This parameter is valid only for DNS and DNS-TCP VServers.
+ - "Minimum length = 1"
+ - "Maximum length = 127"
+
+ servicebindings:
+ description:
+ - List of services along with the weights that are load balanced.
+ - The following suboptions are available.
+ suboptions:
+ servicename:
+ description:
+ - "Service to bind to the virtual server."
+ - "Minimum length = 1"
+ weight:
+ description:
+ - "Weight to assign to the specified service."
+ - "Minimum value = C(1)"
+ - "Maximum value = C(100)"
+
+ servicegroupbindings:
+ description:
+ - List of service groups along with the weights that are load balanced.
+ - The following suboptions are available.
+ suboptions:
+ servicegroupname:
+ description:
+ - "The service group name bound to the selected load balancing virtual server."
+ weight:
+ description:
+ - >-
+ Integer specifying the weight of the service. A larger number specifies a greater weight. Defines the
+ capacity of the service relative to the other services in the load balancing configuration.
+ Determines the priority given to the service in load balancing decisions.
+ - "Minimum value = C(1)"
+ - "Maximum value = C(100)"
+
+ ssl_certkey:
+ description:
+ - The name of the ssl certificate that is bound to this service.
+ - The ssl certificate must already exist.
+ - Creating the certificate can be done with the M(netscaler_ssl_certkey) module.
+ - This option is only applicable only when C(servicetype) is C(SSL).
+
+ disabled:
+ description:
+ - When set to C(yes) the lb vserver will be disabled.
+ - When set to C(no) the lb vserver will be 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: 'no'
+
+extends_documentation_fragment: netscaler
+requirements:
+ - nitro python sdk
+'''
+
+EXAMPLES = '''
+# Netscaler services service-http-1, service-http-2 must have been already created with the netscaler_service module
+
+- name: Create a load balancing vserver bound to services
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nsip: 172.18.0.2
+ nitro_user: nsroot
+ nitro_pass: nsroot
+ validate_certs: no
+
+ state: present
+
+ name: lb_vserver_1
+ servicetype: HTTP
+ timeout: 12
+ ipv46: 6.93.3.3
+ port: 80
+ servicebindings:
+ - servicename: service-http-1
+ weight: 80
+ - servicename: service-http-2
+ weight: 20
+
+# Service group service-group-1 must have been already created with the netscaler_servicegroup module
+
+- name: Create load balancing vserver bound to servicegroup
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nsip: 172.18.0.2
+ nitro_user: nsroot
+ nitro_pass: nsroot
+ validate_certs: no
+ state: present
+
+ name: lb_vserver_2
+ servicetype: HTTP
+ ipv46: 6.92.2.2
+ port: 80
+ timeout: 10
+ servicegroupbindings:
+ - servicegroupname: service-group-1
+'''
+
+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
+from ansible.module_utils.netscaler import (
+ ConfigProxy,
+ get_nitro_client,
+ netscaler_common_arguments,
+ log,
+ loglines,
+ get_immutables_intersection,
+ ensure_feature_is_enabled
+)
+import copy
+
+try:
+ from nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver import lbvserver
+ from nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver_servicegroup_binding import lbvserver_servicegroup_binding
+ from nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver_service_binding import lbvserver_service_binding
+ from nssrc.com.citrix.netscaler.nitro.resource.config.ssl.sslvserver_sslcertkey_binding import sslvserver_sslcertkey_binding
+ from nssrc.com.citrix.netscaler.nitro.exception.nitro_exception import nitro_exception
+
+ PYTHON_SDK_IMPORTED = True
+except ImportError as e:
+ IMPORT_ERROR = str(e)
+ PYTHON_SDK_IMPORTED = False
+
+
+def lb_vserver_exists(client, module):
+ log('Checking if lb vserver exists')
+ if lbvserver.count_filtered(client, 'name:%s' % module.params['name']) > 0:
+ return True
+ else:
+ return False
+
+
+def lb_vserver_identical(client, module, lbvserver_proxy):
+ log('Checking if configured lb vserver is identical')
+ lbvserver_list = lbvserver.get_filtered(client, 'name:%s' % module.params['name'])
+ if lbvserver_proxy.has_equal_attributes(lbvserver_list[0]):
+ return True
+ else:
+ return False
+
+
+def lb_vserver_diff(client, module, lbvserver_proxy):
+ lbvserver_list = lbvserver.get_filtered(client, 'name:%s' % module.params['name'])
+ return lbvserver_proxy.diff_object(lbvserver_list[0])
+
+
+def get_configured_service_bindings(client, module):
+ log('Getting configured service bindings')
+
+ readwrite_attrs = [
+ 'weight',
+ 'name',
+ 'servicename',
+ 'servicegroupname'
+ ]
+ readonly_attrs = [
+ 'preferredlocation',
+ 'vserverid',
+ 'vsvrbindsvcip',
+ 'servicetype',
+ 'cookieipport',
+ 'port',
+ 'vsvrbindsvcport',
+ 'curstate',
+ 'ipv46',
+ 'dynamicweight',
+ ]
+
+ configured_bindings = {}
+ if 'servicebindings' in module.params and module.params['servicebindings'] is not None:
+ for binding in module.params['servicebindings']:
+ attribute_values_dict = copy.deepcopy(binding)
+ attribute_values_dict['name'] = module.params['name']
+ key = binding['servicename'].strip()
+ configured_bindings[key] = ConfigProxy(
+ actual=lbvserver_service_binding(),
+ client=client,
+ attribute_values_dict=attribute_values_dict,
+ readwrite_attrs=readwrite_attrs,
+ readonly_attrs=readonly_attrs,
+ )
+ return configured_bindings
+
+
+def get_configured_servicegroup_bindings(client, module):
+ log('Getting configured service group bindings')
+ readwrite_attrs = [
+ 'weight',
+ 'name',
+ 'servicename',
+ 'servicegroupname',
+ ]
+ readonly_attrs = []
+
+ configured_bindings = {}
+
+ if 'servicegroupbindings' in module.params and module.params['servicegroupbindings'] is not None:
+ for binding in module.params['servicegroupbindings']:
+ attribute_values_dict = copy.deepcopy(binding)
+ attribute_values_dict['name'] = module.params['name']
+ key = binding['servicegroupname'].strip()
+ configured_bindings[key] = ConfigProxy(
+ actual=lbvserver_servicegroup_binding(),
+ client=client,
+ attribute_values_dict=attribute_values_dict,
+ readwrite_attrs=readwrite_attrs,
+ readonly_attrs=readonly_attrs,
+ )
+
+ return configured_bindings
+
+
+def get_actual_service_bindings(client, module):
+ log('Getting actual service bindings')
+ bindings = {}
+ try:
+ if lbvserver_service_binding.count(client, module.params['name']) == 0:
+ return bindings
+ except nitro_exception as e:
+ if e.errorcode == 258:
+ return bindings
+ else:
+ raise
+
+ bindigs_list = lbvserver_service_binding.get(client, module.params['name'])
+
+ for item in bindigs_list:
+ key = item.servicename
+ bindings[key] = item
+
+ return bindings
+
+
+def get_actual_servicegroup_bindings(client, module):
+ log('Getting actual service group bindings')
+ bindings = {}
+
+ try:
+ if lbvserver_servicegroup_binding.count(client, module.params['name']) == 0:
+ return bindings
+ except nitro_exception as e:
+ if e.errorcode == 258:
+ return bindings
+ else:
+ raise
+
+ bindigs_list = lbvserver_servicegroup_binding.get(client, module.params['name'])
+
+ for item in bindigs_list:
+ key = item.servicegroupname
+ bindings[key] = item
+
+ return bindings
+
+
+def service_bindings_identical(client, module):
+ log('service_bindings_identical')
+
+ # Compare service keysets
+ configured_service_bindings = get_configured_service_bindings(client, module)
+ service_bindings = get_actual_service_bindings(client, module)
+ configured_keyset = set(configured_service_bindings.keys())
+ service_keyset = set(service_bindings.keys())
+ if len(configured_keyset ^ service_keyset) > 0:
+ return False
+
+ # Compare service item to item
+ for key in configured_service_bindings.keys():
+ conf = configured_service_bindings[key]
+ serv = service_bindings[key]
+ log('s diff %s' % conf.diff_object(serv))
+ if not conf.has_equal_attributes(serv):
+ return False
+
+ # Fallthrough to success
+ return True
+
+
+def servicegroup_bindings_identical(client, module):
+ log('servicegroup_bindings_identical')
+
+ # Compare servicegroup keysets
+ configured_servicegroup_bindings = get_configured_servicegroup_bindings(client, module)
+ servicegroup_bindings = get_actual_servicegroup_bindings(client, module)
+ configured_keyset = set(configured_servicegroup_bindings.keys())
+ service_keyset = set(servicegroup_bindings.keys())
+ log('len %s' % len(configured_keyset ^ service_keyset))
+ if len(configured_keyset ^ service_keyset) > 0:
+ return False
+
+ # Compare servicegroup item to item
+ for key in configured_servicegroup_bindings.keys():
+ conf = configured_servicegroup_bindings[key]
+ serv = servicegroup_bindings[key]
+ log('sg diff %s' % conf.diff_object(serv))
+ if not conf.has_equal_attributes(serv):
+ return False
+
+ # Fallthrough to success
+ return True
+
+
+def sync_service_bindings(client, module):
+ log('sync_service_bindings')
+
+ actual_bindings = get_actual_service_bindings(client, module)
+ configured_bindigns = get_configured_service_bindings(client, module)
+
+ # Delete actual but not configured
+ delete_keys = list(set(actual_bindings.keys()) - set(configured_bindigns.keys()))
+ for key in delete_keys:
+ log('Deleting service binding %s' % key)
+ actual_bindings[key].servicegroupname = ''
+ actual_bindings[key].delete(client, actual_bindings[key])
+
+ # Add configured but not in actual
+ add_keys = list(set(configured_bindigns.keys()) - set(actual_bindings.keys()))
+ for key in add_keys:
+ log('Adding service binding %s' % key)
+ configured_bindigns[key].add()
+
+ # Update existing if changed
+ modify_keys = list(set(configured_bindigns.keys()) & set(actual_bindings.keys()))
+ for key in modify_keys:
+ if not configured_bindigns[key].has_equal_attributes(actual_bindings[key]):
+ log('Updating service binding %s' % key)
+ actual_bindings[key].servicegroupname = ''
+ actual_bindings[key].delete(client, actual_bindings[key])
+ configured_bindigns[key].add()
+
+
+def sync_servicegroup_bindings(client, module):
+ log('sync_servicegroup_bindings')
+
+ actual_bindings = get_actual_servicegroup_bindings(client, module)
+ configured_bindigns = get_configured_servicegroup_bindings(client, module)
+
+ # Delete actual but not configured
+ delete_keys = list(set(actual_bindings.keys()) - set(configured_bindigns.keys()))
+ for key in delete_keys:
+ log('Deleting servicegroup binding %s' % key)
+ actual_bindings[key].servicename = None
+ actual_bindings[key].delete(client, actual_bindings[key])
+
+ # Add configured but not in actual
+ add_keys = list(set(configured_bindigns.keys()) - set(actual_bindings.keys()))
+ for key in add_keys:
+ log('Adding servicegroup binding %s' % key)
+ configured_bindigns[key].add()
+
+ # Update existing if changed
+ modify_keys = list(set(configured_bindigns.keys()) & set(actual_bindings.keys()))
+ for key in modify_keys:
+ if not configured_bindigns[key].has_equal_attributes(actual_bindings[key]):
+ log('Updating servicegroup binding %s' % key)
+ actual_bindings[key].servicename = None
+ actual_bindings[key].delete(client, actual_bindings[key])
+ configured_bindigns[key].add()
+
+
+def ssl_certkey_bindings_identical(client, module):
+ log('Entering ssl_certkey_bindings_identical')
+ vservername = module.params['name']
+
+ if sslvserver_sslcertkey_binding.count(client, vservername) == 0:
+ bindings = []
+ else:
+ bindings = sslvserver_sslcertkey_binding.get(client, vservername)
+
+ log('Existing certs %s' % bindings)
+
+ if module.params['ssl_certkey'] is None:
+ if len(bindings) == 0:
+ return True
+ else:
+ return False
+ else:
+ certificate_list = [item.certkeyname for item in bindings]
+ log('certificate_list %s' % certificate_list)
+ if certificate_list == [module.params['ssl_certkey']]:
+ return True
+ else:
+ return False
+
+
+def ssl_certkey_bindings_sync(client, module):
+ log('Syncing ssl certificates')
+ vservername = module.params['name']
+ if sslvserver_sslcertkey_binding.count(client, vservername) == 0:
+ bindings = []
+ else:
+ bindings = sslvserver_sslcertkey_binding.get(client, vservername)
+ log('bindings len is %s' % len(bindings))
+
+ # Delete existing bindings
+ for binding in bindings:
+ sslvserver_sslcertkey_binding.delete(client, binding)
+
+ # Add binding if appropriate
+ if module.params['ssl_certkey'] is not None:
+ binding = sslvserver_sslcertkey_binding()
+ binding.vservername = module.params['name']
+ binding.certkeyname = module.params['ssl_certkey']
+ sslvserver_sslcertkey_binding.add(client, binding)
+
+
+def do_state_change(client, module, lbvserver_proxy):
+ if module.params['disabled']:
+ log('Disabling lb server')
+ result = lbvserver.disable(client, lbvserver_proxy.actual)
+ else:
+ log('Enabling lb server')
+ result = lbvserver.enable(client, lbvserver_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',
+ 'DTLS',
+ 'NNTP',
+ 'DNS',
+ 'DHCPRA',
+ 'ANY',
+ 'SIP_UDP',
+ 'SIP_TCP',
+ 'SIP_SSL',
+ 'DNS_TCP',
+ 'RTSP',
+ 'PUSH',
+ 'SSL_PUSH',
+ 'RADIUS',
+ 'RDP',
+ 'MYSQL',
+ 'MSSQL',
+ 'DIAMETER',
+ 'SSL_DIAMETER',
+ 'TFTP',
+ 'ORACLE',
+ 'SMPP',
+ 'SYSLOGTCP',
+ 'SYSLOGUDP',
+ 'FIX',
+ 'SSL_FIX',
+ ]
+ ),
+ ipv46=dict(type='str'),
+ ippattern=dict(type='str'),
+ ipmask=dict(type='str'),
+ port=dict(type='int'),
+ range=dict(type='float'),
+ persistencetype=dict(
+ type='str',
+ choices=[
+ 'SOURCEIP',
+ 'COOKIEINSERT',
+ 'SSLSESSION',
+ 'RULE',
+ 'URLPASSIVE',
+ 'CUSTOMSERVERID',
+ 'DESTIP',
+ 'SRCIPDESTIP',
+ 'CALLID',
+ 'RTSPSID',
+ 'DIAMETER',
+ 'FIXSESSION',
+ 'NONE',
+ ]
+ ),
+ timeout=dict(type='float'),
+ persistencebackup=dict(
+ type='str',
+ choices=[
+ 'SOURCEIP',
+ 'NONE',
+ ]
+ ),
+ backuppersistencetimeout=dict(type='float'),
+ lbmethod=dict(
+ type='str',
+ choices=[
+ 'ROUNDROBIN',
+ 'LEASTCONNECTION',
+ 'LEASTRESPONSETIME',
+ 'URLHASH',
+ 'DOMAINHASH',
+ 'DESTINATIONIPHASH',
+ 'SOURCEIPHASH',
+ 'SRCIPDESTIPHASH',
+ 'LEASTBANDWIDTH',
+ 'LEASTPACKETS',
+ 'TOKEN',
+ 'SRCIPSRCPORTHASH',
+ 'LRTM',
+ 'CALLIDHASH',
+ 'CUSTOMLOAD',
+ 'LEASTREQUEST',
+ 'AUDITLOGHASH',
+ 'STATICPROXIMITY',
+ ]
+ ),
+ hashlength=dict(type='float'),
+ netmask=dict(type='str'),
+ v6netmasklen=dict(type='float'),
+ backuplbmethod=dict(
+ type='str',
+ choices=[
+ 'ROUNDROBIN',
+ 'LEASTCONNECTION',
+ 'LEASTRESPONSETIME',
+ 'SOURCEIPHASH',
+ 'LEASTBANDWIDTH',
+ 'LEASTPACKETS',
+ 'CUSTOMLOAD',
+ ]
+ ),
+ cookiename=dict(type='str'),
+ listenpolicy=dict(type='str'),
+ listenpriority=dict(type='float'),
+ persistmask=dict(type='str'),
+ v6persistmasklen=dict(type='float'),
+ rtspnat=dict(type='bool'),
+ m=dict(
+ type='str',
+ choices=[
+ 'IP',
+ 'MAC',
+ 'IPTUNNEL',
+ 'TOS',
+ ]
+ ),
+ tosid=dict(type='float'),
+ datalength=dict(type='float'),
+ dataoffset=dict(type='float'),
+ sessionless=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ connfailover=dict(
+ type='str',
+ choices=[
+ 'DISABLED',
+ 'STATEFUL',
+ 'STATELESS',
+ ]
+ ),
+ redirurl=dict(type='str'),
+ cacheable=dict(type='bool'),
+ clttimeout=dict(type='float'),
+ somethod=dict(
+ type='str',
+ choices=[
+ 'CONNECTION',
+ 'DYNAMICCONNECTION',
+ 'BANDWIDTH',
+ 'HEALTH',
+ 'NONE',
+ ]
+ ),
+ sopersistence=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ sopersistencetimeout=dict(type='float'),
+ healththreshold=dict(type='float'),
+ sothreshold=dict(type='float'),
+ sobackupaction=dict(
+ type='str',
+ choices=[
+ 'DROP',
+ 'ACCEPT',
+ 'REDIRECT',
+ ]
+ ),
+ redirectportrewrite=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ downstateflush=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ disableprimaryondown=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ insertvserveripport=dict(
+ type='str',
+ choices=[
+ 'OFF',
+ 'VIPADDR',
+ 'V6TOV4MAPPING',
+ ]
+ ),
+ vipheader=dict(type='str'),
+ authenticationhost=dict(type='str'),
+ authentication=dict(type='bool'),
+ authn401=dict(type='bool'),
+ authnvsname=dict(type='str'),
+ push=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ pushvserver=dict(type='str'),
+ pushlabel=dict(type='str'),
+ pushmulticlients=dict(type='bool'),
+ tcpprofilename=dict(type='str'),
+ httpprofilename=dict(type='str'),
+ dbprofilename=dict(type='str'),
+ comment=dict(type='str'),
+ l2conn=dict(type='bool'),
+ oracleserverversion=dict(
+ type='str',
+ choices=[
+ '10G',
+ '11G',
+ ]
+ ),
+ mssqlserverversion=dict(
+ type='str',
+ choices=[
+ '70',
+ '2000',
+ '2000SP1',
+ '2005',
+ '2008',
+ '2008R2',
+ '2012',
+ '2014',
+ ]
+ ),
+ mysqlprotocolversion=dict(type='float'),
+ mysqlserverversion=dict(type='str'),
+ mysqlcharacterset=dict(type='float'),
+ mysqlservercapabilities=dict(type='float'),
+ appflowlog=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ netprofile=dict(type='str'),
+ icmpvsrresponse=dict(
+ type='str',
+ choices=[
+ 'PASSIVE',
+ 'ACTIVE',
+ ]
+ ),
+ rhistate=dict(
+ type='str',
+ choices=[
+ 'PASSIVE',
+ 'ACTIVE',
+ ]
+ ),
+ newservicerequest=dict(type='float'),
+ newservicerequestunit=dict(
+ type='str',
+ choices=[
+ 'PER_SECOND',
+ 'PERCENT',
+ ]
+ ),
+ newservicerequestincrementinterval=dict(type='float'),
+ minautoscalemembers=dict(type='float'),
+ maxautoscalemembers=dict(type='float'),
+ skippersistency=dict(
+ type='str',
+ choices=[
+ 'Bypass',
+ 'ReLb',
+ 'None',
+ ]
+ ),
+ authnprofile=dict(type='str'),
+ macmoderetainvlan=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ dbslb=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ dns64=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ bypassaaaa=dict(type='bool'),
+ recursionavailable=dict(type='bool'),
+ processlocal=dict(
+ type='str',
+ choices=[
+ 'ENABLED',
+ 'DISABLED',
+ ]
+ ),
+ dnsprofilename=dict(type='str'),
+ )
+
+ hand_inserted_arguments = dict(
+ servicebindings=dict(type='list'),
+ servicegroupbindings=dict(type='list'),
+ ssl_certkey=dict(type='str'),
+ 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)) == "":
+ module.fail_json(msg='Connection error %s' % str(e))
+ elif str(type(e)) == "":
+ 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',
+ 'ipv46',
+ 'ippattern',
+ 'ipmask',
+ 'port',
+ 'range',
+ 'persistencetype',
+ 'timeout',
+ 'persistencebackup',
+ 'backuppersistencetimeout',
+ 'lbmethod',
+ 'hashlength',
+ 'netmask',
+ 'v6netmasklen',
+ 'backuplbmethod',
+ 'cookiename',
+ 'listenpolicy',
+ 'listenpriority',
+ 'persistmask',
+ 'v6persistmasklen',
+ 'rtspnat',
+ 'm',
+ 'tosid',
+ 'datalength',
+ 'dataoffset',
+ 'sessionless',
+ 'connfailover',
+ 'redirurl',
+ 'cacheable',
+ 'clttimeout',
+ 'somethod',
+ 'sopersistence',
+ 'sopersistencetimeout',
+ 'healththreshold',
+ 'sothreshold',
+ 'sobackupaction',
+ 'redirectportrewrite',
+ 'downstateflush',
+ 'disableprimaryondown',
+ 'insertvserveripport',
+ 'vipheader',
+ 'authenticationhost',
+ 'authentication',
+ 'authn401',
+ 'authnvsname',
+ 'push',
+ 'pushvserver',
+ 'pushlabel',
+ 'pushmulticlients',
+ 'tcpprofilename',
+ 'httpprofilename',
+ 'dbprofilename',
+ 'comment',
+ 'l2conn',
+ 'oracleserverversion',
+ 'mssqlserverversion',
+ 'mysqlprotocolversion',
+ 'mysqlserverversion',
+ 'mysqlcharacterset',
+ 'mysqlservercapabilities',
+ 'appflowlog',
+ 'netprofile',
+ 'icmpvsrresponse',
+ 'rhistate',
+ 'newservicerequest',
+ 'newservicerequestunit',
+ 'newservicerequestincrementinterval',
+ 'minautoscalemembers',
+ 'maxautoscalemembers',
+ 'skippersistency',
+ 'authnprofile',
+ 'macmoderetainvlan',
+ 'dbslb',
+ 'dns64',
+ 'bypassaaaa',
+ 'recursionavailable',
+ 'processlocal',
+ 'dnsprofilename',
+ ]
+
+ readonly_attrs = [
+ 'value',
+ 'ipmapping',
+ 'ngname',
+ 'type',
+ 'curstate',
+ 'effectivestate',
+ 'status',
+ 'lbrrreason',
+ 'redirect',
+ 'precedence',
+ 'homepage',
+ 'dnsvservername',
+ 'domain',
+ 'policyname',
+ 'cachevserver',
+ 'health',
+ 'gotopriorityexpression',
+ 'ruletype',
+ 'groupname',
+ 'cookiedomain',
+ 'map',
+ 'gt2gb',
+ 'consolidatedlconn',
+ 'consolidatedlconngbl',
+ 'thresholdvalue',
+ 'bindpoint',
+ 'invoke',
+ 'labeltype',
+ 'labelname',
+ 'version',
+ 'totalservices',
+ 'activeservices',
+ 'statechangetimesec',
+ 'statechangetimeseconds',
+ 'statechangetimemsec',
+ 'tickssincelaststatechange',
+ 'isgslb',
+ 'vsvrdynconnsothreshold',
+ 'backupvserverstatus',
+ '__count',
+ ]
+
+ immutable_attrs = [
+ 'name',
+ 'servicetype',
+ 'ipv46',
+ 'port',
+ 'range',
+ 'state',
+ 'redirurl',
+ 'vipheader',
+ 'newservicerequestunit',
+ 'td',
+ ]
+
+ transforms = {
+ 'rtspnat': ['bool_on_off'],
+ 'authn401': ['bool_on_off'],
+ 'bypassaaaa': ['bool_yes_no'],
+ 'authentication': ['bool_on_off'],
+ 'cacheable': ['bool_yes_no'],
+ 'l2conn': ['bool_on_off'],
+ 'pushmulticlients': ['bool_yes_no'],
+ 'recursionavailable': ['bool_yes_no'],
+ }
+
+ lbvserver_proxy = ConfigProxy(
+ actual=lbvserver(),
+ 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, 'LB')
+ if module.params['state'] == 'present':
+ log('Applying actions for state present')
+
+ if not lb_vserver_exists(client, module):
+ log('Add lb vserver')
+ if not module.check_mode:
+ lbvserver_proxy.add()
+ if module.params['save_config']:
+ client.save_config()
+ module_result['changed'] = True
+ elif not lb_vserver_identical(client, module, lbvserver_proxy):
+
+ # Check if we try to change value of immutable attributes
+ diff_dict = lb_vserver_diff(client, module, lbvserver_proxy)
+ immutables_changed = get_immutables_intersection(lbvserver_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)
+
+ log('Update lb vserver')
+ if not module.check_mode:
+ lbvserver_proxy.update()
+ if module.params['save_config']:
+ client.save_config()
+ module_result['changed'] = True
+ else:
+ log('Present noop')
+
+ if not service_bindings_identical(client, module):
+ if not module.check_mode:
+ sync_service_bindings(client, module)
+ if module.params['save_config']:
+ client.save_config()
+ module_result['changed'] = True
+
+ if not servicegroup_bindings_identical(client, module):
+ if not module.check_mode:
+ sync_servicegroup_bindings(client, module)
+ if module.params['save_config']:
+ client.save_config()
+ module_result['changed'] = True
+
+ if module.params['servicetype'] != 'SSL' and module.params['ssl_certkey'] is not None:
+ module.fail_json(msg='ssl_certkey is applicable only to SSL vservers', **module_result)
+
+ # Check if SSL certkey is sane
+ if module.params['servicetype'] == 'SSL':
+ if not ssl_certkey_bindings_identical(client, module):
+ if not module.check_mode:
+ ssl_certkey_bindings_sync(client, module)
+
+ module_result['changed'] = True
+
+ if not module.check_mode:
+ res = do_state_change(client, module, lbvserver_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
+ log('Sanity checks for state present')
+ if not module.check_mode:
+ if not lb_vserver_exists(client, module):
+ module.fail_json(msg='Did not create lb vserver', **module_result)
+
+ if not lb_vserver_identical(client, module, lbvserver_proxy):
+ msg = 'lb vserver is not configured correctly'
+ module.fail_json(msg=msg, diff=lb_vserver_diff(client, module, lbvserver_proxy), **module_result)
+
+ if not service_bindings_identical(client, module):
+ module.fail_json(msg='service bindings are not identical', **module_result)
+
+ if not servicegroup_bindings_identical(client, module):
+ module.fail_json(msg='servicegroup bindings are not identical', **module_result)
+
+ if module.params['servicetype'] == 'SSL':
+ if not ssl_certkey_bindings_identical(client, module):
+ module.fail_json(msg='sll certkey bindings not identical', **module_result)
+
+ elif module.params['state'] == 'absent':
+ log('Applying actions for state absent')
+ if lb_vserver_exists(client, module):
+ if not module.check_mode:
+ log('Delete lb vserver')
+ lbvserver_proxy.delete()
+ if module.params['save_config']:
+ client.save_config()
+ module_result['changed'] = True
+ else:
+ log('Absent noop')
+ module_result['changed'] = False
+
+ # Sanity check
+ log('Sanity checks for state absent')
+ if not module.check_mode:
+ if lb_vserver_exists(client, module):
+ module.fail_json(msg='lb 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()
diff --git a/test/integration/roles/netscaler_lb_vserver/defaults/main.yaml b/test/integration/roles/netscaler_lb_vserver/defaults/main.yaml
new file mode 100644
index 00000000000..641801f6600
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/defaults/main.yaml
@@ -0,0 +1,6 @@
+---
+testcase: "*"
+test_cases: []
+
+nitro_user: nsroot
+nitro_pass: nsroot
diff --git a/test/integration/roles/netscaler_lb_vserver/sample_inventory b/test/integration/roles/netscaler_lb_vserver/sample_inventory
new file mode 100644
index 00000000000..42635796914
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/sample_inventory
@@ -0,0 +1,5 @@
+
+
+[netscaler]
+
+netscaler01 nsip=172.18.0.2 nitro_user=nsroot nitro_pass=nsroot
diff --git a/test/integration/roles/netscaler_lb_vserver/tasks/main.yaml b/test/integration/roles/netscaler_lb_vserver/tasks/main.yaml
new file mode 100644
index 00000000000..8e14bcc3843
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tasks/main.yaml
@@ -0,0 +1,7 @@
+---
+
+- include: testbed_setup.yaml state=present
+
+- { include: nitro.yaml, tags: ['nitro'] }
+
+- include: testbed_setup.yaml state=absent
diff --git a/test/integration/roles/netscaler_lb_vserver/tasks/nitro.yaml b/test/integration/roles/netscaler_lb_vserver/tasks/nitro.yaml
new file mode 100644
index 00000000000..00ab502dda9
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tasks/nitro.yaml
@@ -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
diff --git a/test/integration/roles/netscaler_lb_vserver/tasks/testbed_setup.yaml b/test/integration/roles/netscaler_lb_vserver/tasks/testbed_setup.yaml
new file mode 100644
index 00000000000..c7f92333c44
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tasks/testbed_setup.yaml
@@ -0,0 +1,78 @@
+---
+
+- name: Setup server
+ delegate_to: localhost
+ netscaler_server:
+ nsip: "{{ nsip }}"
+ nitro_user: "{{ nitro_user }}"
+ nitro_pass: "{{ nitro_pass }}"
+
+ state: "{{ state }}"
+ name: "server-{{ item }}"
+ ipaddress: "192.168.1.{{ item }}"
+ with_sequence: count=6
+
+
+- name: Setup http service
+ delegate_to: localhost
+ netscaler_service:
+ nsip: "{{ nsip }}"
+ nitro_user: "{{ nitro_user }}"
+ nitro_pass: "{{ nitro_pass }}"
+
+ state: "{{ state }}"
+ name: "service-http-{{ item }}"
+ servername: "server-{{ item }}"
+ servicetype: HTTP
+ port: 80
+ with_sequence: count=2
+
+- name: Setup service group
+ delegate_to: localhost
+ netscaler_servicegroup:
+ nsip: "{{ nsip }}"
+ nitro_user: "{{ nitro_user }}"
+ nitro_pass: "{{ nitro_pass }}"
+
+ state: "{{ state }}"
+ servicegroupname: service-group-1
+ servicetype: HTTP
+ servicemembers:
+ - servername: server-3
+ port: 80
+ weight: 50
+ - servername: server-4
+ port: 80
+ weight: 50
+
+- name: Setup service group
+ delegate_to: localhost
+ netscaler_servicegroup:
+ nsip: "{{ nsip }}"
+ nitro_user: "{{ nitro_user }}"
+ nitro_pass: "{{ nitro_pass }}"
+
+ state: "{{ state }}"
+ servicegroupname: service-group-2
+ servicetype: HTTP
+ servicemembers:
+ - servername: server-5
+ port: 80
+ weight: 50
+ - servername: server-6
+ port: 80
+ weight: 50
+
+- name: Setup push vserver
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nsip: "{{ nsip }}"
+ nitro_user: "{{ nitro_user }}"
+ nitro_pass: "{{ nitro_pass }}"
+
+ state: "{{ state }}"
+
+ name: lb-vserver-push
+ port: 80
+ servicetype: PUSH
+ ipv46: 193.1.1.1
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_any.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_any.yaml
new file mode 100644
index 00000000000..397ac466d2d
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_any.yaml
@@ -0,0 +1,57 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_any/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_any/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_any/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_any/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_any/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_any/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_any/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_any/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_any/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_any/remove.yaml
new file mode 100644
index 00000000000..86bb460fb0b
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_any/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-4
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_any/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_any/setup.yaml
new file mode 100644
index 00000000000..99d3a37693c
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_any/setup.yaml
@@ -0,0 +1,17 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-4
+ ipv46: 10.79.1.4
+ port: 80
+ servicetype: ANY
+ connfailover: STATELESS
+ skippersistency: None
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_dns.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_dns.yaml
new file mode 100644
index 00000000000..8c815487207
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_dns.yaml
@@ -0,0 +1,57 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_dns/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_dns/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_dns/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_dns/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_dns/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_dns/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_dns/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_dns/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_dns/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_dns/remove.yaml
new file mode 100644
index 00000000000..19e1ea84560
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_dns/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-8
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_dns/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_dns/setup.yaml
new file mode 100644
index 00000000000..2dbc06e4ba6
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_dns/setup.yaml
@@ -0,0 +1,17 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-8
+ ipv46: 10.79.1.8
+ port: 80
+ servicetype: DNS
+
+ recursionavailable: no
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_flap_disabled.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_flap_disabled.yaml
new file mode 100644
index 00000000000..822c07a02df
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_flap_disabled.yaml
@@ -0,0 +1,9 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_flap_disabled/setup.yaml"
+ vars:
+ check_mode: no
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_flap_disabled/remove.yaml"
+ vars:
+ check_mode: no
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_flap_disabled/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_flap_disabled/remove.yaml
new file mode 100644
index 00000000000..89cd1cd1ca3
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_flap_disabled/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-flap
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_flap_disabled/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_flap_disabled/setup.yaml
new file mode 100644
index 00000000000..3a63ce6627f
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_flap_disabled/setup.yaml
@@ -0,0 +1,47 @@
+---
+
+- name: flap http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-flap
+ ipv46: 10.79.1.2
+ port: 80
+ servicetype: HTTP
+ servicebindings:
+ - servicename: service-http-1
+ weight: 50
+ - servicename: service-http-2
+ weight: 50
+
+ disabled: "{{ item|int % 2 }}"
+ with_sequence: count=20
+ delay: 1
+
+- name: flap http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-flap
+ ipv46: 10.79.1.2
+ port: 80
+ servicetype: HTTP
+ servicebindings:
+ - servicename: service-http-1
+ weight: 50
+ - servicename: service-http-2
+ weight: 50
+
+ disabled: "{{ item|int % 2 }}"
+ with_sequence: count=20
+ delay: 5
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http.yaml
new file mode 100644
index 00000000000..b63df58d131
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http.yaml
@@ -0,0 +1,85 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/update.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/update.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/update.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/update.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_http/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http/remove.yaml
new file mode 100644
index 00000000000..3d525e97e15
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-1
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http/setup.yaml
new file mode 100644
index 00000000000..f52e735be1a
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http/setup.yaml
@@ -0,0 +1,71 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+
+ name: lb-vserver-1
+ ipv46: 10.79.1.1
+ port: 80
+ range: 2
+ servicetype: HTTP
+ persistencetype: COOKIEINSERT
+ timeout: 100
+ persistencebackup: SOURCEIP
+ backuppersistencetimeout: 110
+ lbmethod: URLHASH
+ cookiename: COOKIE
+ listenpolicy: "CLIENT.IP.DST.IN_SUBNET(192.0.2.0/24)"
+ listenpriority: 66
+ persistmask: 255.255.0.0
+ v6persistmasklen: 64
+ m: IP
+ tosid: 6
+ sessionless: DISABLED
+ redirurl: http://somewhere.com
+ cacheable: no
+ clttimeout: 111
+ somethod: CONNECTION
+ sopersistence: DISABLED
+ sopersistencetimeout: 222
+ sothreshold: 4096
+ healththreshold: 55
+ sobackupaction: DROP
+ redirectportrewrite: DISABLED
+ downstateflush: DISABLED
+ disableprimaryondown: DISABLED
+ insertvserveripport: VIPADDR
+ vipheader: vip
+ authenticationhost: authenticate.me
+ authentication: off
+ authn401: off
+ authnvsname: somename
+ push: DISABLED
+ pushmulticlients: no
+ comment: Vserver comment
+ l2conn: "OFF"
+ appflowlog: DISABLED
+ icmpvsrresponse: PASSIVE
+ rhistate: PASSIVE
+ newservicerequest: 11
+ newservicerequestunit: PER_SECOND
+ newservicerequestincrementinterval: 5
+ minautoscalemembers: 8
+ maxautoscalemembers: 10
+ macmoderetainvlan: DISABLED
+ dns64: DISABLED
+ bypassaaaa: no
+ processlocal: DISABLED
+ backuplbmethod: LEASTCONNECTION
+ hashlength: 100
+ servicebindings:
+ - servicename: service-http-1
+ weight: 50
+ - servicename: service-http-2
+ weight: 50
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http/update.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http/update.yaml
new file mode 100644
index 00000000000..e8621c27b5a
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_http/update.yaml
@@ -0,0 +1,71 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+
+ name: lb-vserver-1
+ ipv46: 10.79.1.1
+ port: 80
+ range: 2
+ servicetype: HTTP
+ persistencetype: COOKIEINSERT
+ timeout: 100
+ persistencebackup: SOURCEIP
+ backuppersistencetimeout: 110
+ lbmethod: URLHASH
+ cookiename: COOKIE
+ listenpolicy: "CLIENT.IP.DST.IN_SUBNET(192.0.2.0/24)"
+ listenpriority: 66
+ persistmask: 255.255.0.0
+ v6persistmasklen: 64
+ m: IP
+ tosid: 6
+ sessionless: DISABLED
+ redirurl: http://somewhere.com
+ cacheable: no
+ clttimeout: 222
+ somethod: CONNECTION
+ sopersistence: DISABLED
+ sopersistencetimeout: 222
+ sothreshold: 4096
+ healththreshold: 55
+ sobackupaction: DROP
+ redirectportrewrite: DISABLED
+ downstateflush: DISABLED
+ disableprimaryondown: DISABLED
+ insertvserveripport: VIPADDR
+ vipheader: vip
+ authenticationhost: authenticate.me
+ authentication: off
+ authn401: off
+ authnvsname: somename
+ push: DISABLED
+ pushmulticlients: no
+ comment: Vserver comment
+ l2conn: "OFF"
+ appflowlog: DISABLED
+ icmpvsrresponse: PASSIVE
+ rhistate: PASSIVE
+ newservicerequest: 11
+ newservicerequestunit: PER_SECOND
+ newservicerequestincrementinterval: 5
+ minautoscalemembers: 8
+ maxautoscalemembers: 10
+ macmoderetainvlan: DISABLED
+ dns64: DISABLED
+ bypassaaaa: no
+ processlocal: DISABLED
+ backuplbmethod: LEASTCONNECTION
+ hashlength: 100
+ servicebindings:
+ - servicename: service-http-1
+ weight: 60
+ - servicename: service-http-2
+ weight: 40
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_iphash.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_iphash.yaml
new file mode 100644
index 00000000000..64e24fd4baa
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_iphash.yaml
@@ -0,0 +1,57 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_iphash/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_iphash/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_iphash/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_iphash/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_iphash/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_iphash/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_iphash/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_iphash/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_iphash/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_iphash/remove.yaml
new file mode 100644
index 00000000000..5c9a48394f0
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_iphash/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-10
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_iphash/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_iphash/setup.yaml
new file mode 100644
index 00000000000..4f56d7510d4
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_iphash/setup.yaml
@@ -0,0 +1,19 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-10
+ port: 80
+ servicetype: HTTP
+ lbmethod: DESTINATIONIPHASH
+ netmask: 255.255.255.0
+ ippattern: 10.68.0.0
+ ipmask: 255.255.0.0
+ v6netmasklen: 24
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_ippattern.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_ippattern.yaml
new file mode 100644
index 00000000000..e44507262b4
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_ippattern.yaml
@@ -0,0 +1,57 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_ippattern/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_ippattern/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_ippattern/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_ippattern/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_ippattern/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_ippattern/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_ippattern/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_ippattern/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_ippattern/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_ippattern/remove.yaml
new file mode 100644
index 00000000000..19bfd4f7389
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_ippattern/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-9
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_ippattern/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_ippattern/setup.yaml
new file mode 100644
index 00000000000..8ca894f945b
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_ippattern/setup.yaml
@@ -0,0 +1,16 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-9
+ port: 80
+ servicetype: HTTP
+ ippattern: 10.67.0.0
+ ipmask: 255.255.0.0
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mssql.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mssql.yaml
new file mode 100644
index 00000000000..cee26f896d1
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mssql.yaml
@@ -0,0 +1,57 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mssql/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mssql/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mssql/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mssql/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mssql/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mssql/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mssql/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mssql/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mssql/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mssql/remove.yaml
new file mode 100644
index 00000000000..1c5d0cd6dd5
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mssql/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-6
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mssql/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mssql/setup.yaml
new file mode 100644
index 00000000000..0451a097317
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mssql/setup.yaml
@@ -0,0 +1,16 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-6
+ ipv46: 10.79.1.6
+ port: 80
+ servicetype: MSSQL
+ mssqlserverversion: 2000
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mysql.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mysql.yaml
new file mode 100644
index 00000000000..40e8cbf43b1
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mysql.yaml
@@ -0,0 +1,57 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mysql/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mysql/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mysql/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mysql/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mysql/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mysql/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mysql/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_mysql/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mysql/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mysql/remove.yaml
new file mode 100644
index 00000000000..6db6dbbdf42
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mysql/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-7
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mysql/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mysql/setup.yaml
new file mode 100644
index 00000000000..9aef8b44624
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_mysql/setup.yaml
@@ -0,0 +1,22 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-7
+ ipv46: 10.79.1.7
+ port: 80
+ servicetype: MYSQL
+
+ mysqlprotocolversion: 2
+ mysqlserverversion: 10
+ mysqlcharacterset: 8
+ mysqlservercapabilities: 244
+
+ dbslb: DISABLED
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_oracle.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_oracle.yaml
new file mode 100644
index 00000000000..353aeaf6f69
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_oracle.yaml
@@ -0,0 +1,57 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_oracle/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_oracle/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_oracle/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_oracle/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_oracle/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_oracle/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_oracle/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_oracle/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_oracle/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_oracle/remove.yaml
new file mode 100644
index 00000000000..770d61dfa2e
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_oracle/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-5
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_oracle/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_oracle/setup.yaml
new file mode 100644
index 00000000000..7f1b945f937
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_oracle/setup.yaml
@@ -0,0 +1,16 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-5
+ ipv46: 10.79.1.5
+ port: 80
+ servicetype: ORACLE
+ oracleserverversion: 10G
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_push.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_push.yaml
new file mode 100644
index 00000000000..04d6c54c02b
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_push.yaml
@@ -0,0 +1,57 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_push/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_push/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_push/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_push/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_push/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_push/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_push/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_push/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_push/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_push/remove.yaml
new file mode 100644
index 00000000000..914551067a3
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_push/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-11
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_push/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_push/setup.yaml
new file mode 100644
index 00000000000..03c22f890f7
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_push/setup.yaml
@@ -0,0 +1,21 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-11
+ port: 80
+ servicetype: HTTP
+ lbmethod: DESTINATIONIPHASH
+ netmask: 255.255.255.0
+ ippattern: 10.69.0.0
+ ipmask: 255.255.0.0
+ v6netmasklen: 24
+ pushvserver: lb-vserver-push
+ pushlabel: none
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_rtspnat.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_rtspnat.yaml
new file mode 100644
index 00000000000..de272174c4c
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_rtspnat.yaml
@@ -0,0 +1,57 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_rtspnat/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_rtspnat/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_rtspnat/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_rtspnat/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_rtspnat/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_rtspnat/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_rtspnat/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_rtspnat/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_rtspnat/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_rtspnat/remove.yaml
new file mode 100644
index 00000000000..50b056a33b3
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_rtspnat/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-3
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_rtspnat/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_rtspnat/setup.yaml
new file mode 100644
index 00000000000..a25db36c049
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_rtspnat/setup.yaml
@@ -0,0 +1,20 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+
+ name: lb-vserver-3
+ ipv46: 10.79.1.3
+ port: 80
+ servicetype: TCP
+
+ lbmethod: TOKEN
+ datalength: 20
+ dataoffset: 5
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup.yaml
new file mode 100644
index 00000000000..403162cea03
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup.yaml
@@ -0,0 +1,132 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/setup.yaml"
+ vars:
+ check_mode: no
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update.yaml"
+ vars:
+ check_mode: no
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update_service.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update_service.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update_service.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update_service.yaml"
+ vars:
+ check_mode: no
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/update.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_servicegroup/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/remove.yaml
new file mode 100644
index 00000000000..d4a88c43bc9
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-20
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/setup.yaml
new file mode 100644
index 00000000000..cf4157763e6
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/setup.yaml
@@ -0,0 +1,17 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-20
+ ipv46: 10.79.1.8
+ port: 80
+ servicetype: HTTP
+ servicegroupbindings:
+ - servicegroupname: service-group-1
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/update.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/update.yaml
new file mode 100644
index 00000000000..454565dc8e8
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/update.yaml
@@ -0,0 +1,17 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-20
+ ipv46: 10.79.1.8
+ port: 80
+ servicetype: HTTP
+ servicegroupbindings:
+ - servicegroupname: service-group-2
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/update_service.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/update_service.yaml
new file mode 100644
index 00000000000..a168f1c742a
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_servicegroup/update_service.yaml
@@ -0,0 +1,20 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-20
+ ipv46: 10.79.1.8
+ port: 80
+ servicetype: HTTP
+ servicebindings:
+ - servicename: service-http-1
+ weight: 50
+ - servicename: service-http-2
+ weight: 50
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_tcp.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_tcp.yaml
new file mode 100644
index 00000000000..5b1b7acd527
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_tcp.yaml
@@ -0,0 +1,57 @@
+---
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_tcp/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_tcp/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_tcp/setup.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_tcp/setup.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_tcp/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_tcp/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_tcp/remove.yaml"
+ vars:
+ check_mode: yes
+
+- assert:
+ that: not result|changed
+
+- include: "{{ role_path }}/tests/nitro/lb_vserver_tcp/remove.yaml"
+ vars:
+ check_mode: no
+
+- assert:
+ that: not result|changed
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_tcp/remove.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_tcp/remove.yaml
new file mode 100644
index 00000000000..c2a99a7f7a3
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_tcp/remove.yaml
@@ -0,0 +1,12 @@
+---
+
+- name: remove http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: absent
+ name: lb-vserver-2
diff --git a/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_tcp/setup.yaml b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_tcp/setup.yaml
new file mode 100644
index 00000000000..8d1b48284dc
--- /dev/null
+++ b/test/integration/roles/netscaler_lb_vserver/tests/nitro/lb_vserver_tcp/setup.yaml
@@ -0,0 +1,16 @@
+---
+
+- name: setup http lb vserver
+ register: result
+ check_mode: "{{ check_mode }}"
+ delegate_to: localhost
+ netscaler_lb_vserver:
+ nitro_user: "{{nitro_user}}"
+ nitro_pass: "{{nitro_pass}}"
+ nsip: "{{nsip}}"
+ state: present
+ name: lb-vserver-2
+ ipv46: 10.79.1.2
+ port: 80
+ servicetype: RTSP
+ rtspnat: on
diff --git a/test/units/modules/network/netscaler/test_netscaler_lb_vserver.py b/test/units/modules/network/netscaler/test_netscaler_lb_vserver.py
new file mode 100644
index 00000000000..7f3e2d25a0b
--- /dev/null
+++ b/test/units/modules/network/netscaler/test_netscaler_lb_vserver.py
@@ -0,0 +1,835 @@
+
+# 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 .
+#
+
+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 TestNetscalerLBVServerModule(TestModule):
+
+ @classmethod
+ def setUpClass(cls):
+ class MockException(Exception):
+ pass
+
+ cls.MockException = MockException
+
+ m = MagicMock()
+ cls.server_mock = MagicMock()
+ cls.server_mock.__class__ = MagicMock(add=Mock())
+ nssrc_modules_mock = {
+ 'nssrc.com.citrix.netscaler.nitro.resource.config.lb': m,
+ 'nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver': m,
+ 'nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver.lbvserver': m,
+ 'nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver_service_binding': m,
+ 'nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver_service_binding.lbvserver_service_binding': m,
+ 'nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver_servicegroup_binding': m,
+ 'nssrc.com.citrix.netscaler.nitro.resource.config.lb.lbvserver_servicegroup_binding.lbvserver_servicegroup_binding': m,
+ 'nssrc.com.citrix.netscaler.nitro.resource.config.ssl': m,
+ 'nssrc.com.citrix.netscaler.nitro.resource.config.ssl.sslvserver_sslcertkey_binding': m,
+ 'nssrc.com.citrix.netscaler.nitro.resource.config.ssl.sslvserver_sslcertkey_binding.sslvserver_sslcertkey_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_lb_vserver
+ self.module = netscaler_lb_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_lb_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_lb_vserver.get_nitro_client', m):
+ with patch('ansible.modules.network.netscaler.netscaler_lb_vserver.nitro_exception', MockException):
+ self.module = netscaler_lb_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_lb_vserver
+
+ 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_lb_vserver',
+ get_nitro_client=m,
+ nitro_exception=self.MockException,
+ ):
+ self.module = netscaler_lb_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_lb_vserver
+
+ if sys.version_info[:2] == (2, 6):
+ self.skipTest('requests library not available under python2.6')
+
+ 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_lb_vserver',
+ get_nitro_client=m,
+ nitro_exception=self.MockException,
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.failed()
+ self.assertTrue(result['msg'].startswith('SSL Error'), msg='SSL Error was not handled gracefully')
+
+ 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_lb_vserver
+
+ client_mock = Mock()
+
+ m = Mock(return_value=client_mock)
+
+ lb_vserver_proxy_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=m,
+ lb_vserver_exists=Mock(side_effect=[False, True]),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ ):
+ self.module = netscaler_lb_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_lb_vserver
+
+ client_mock = Mock()
+
+ m = Mock(return_value=client_mock)
+
+ lb_vserver_proxy_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=m,
+ lb_vserver_exists=Mock(side_effect=[True, False]),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ ):
+ self.module = netscaler_lb_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_lb_vserver
+
+ client_mock = Mock()
+
+ m = Mock(return_value=client_mock)
+
+ lb_vserver_proxy_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=m,
+ lb_vserver_exists=Mock(side_effect=[False, True]),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ ):
+ self.module = netscaler_lb_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_lb_vserver
+
+ client_mock = Mock()
+
+ m = Mock(return_value=client_mock)
+
+ lb_vserver_proxy_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=m,
+ lb_vserver_exists=Mock(side_effect=[True, False]),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ ):
+ self.module = netscaler_lb_vserver
+ self.exited()
+ self.assertNotIn(call.save_config(), client_mock.mock_calls)
+
+ 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',
+ save_config=False,
+ ))
+ from ansible.modules.network.netscaler import netscaler_lb_vserver
+
+ client_mock = Mock()
+
+ lb_vserver_proxy_mock = Mock()
+ feature_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[True, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[True, True]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=feature_mock,
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ ):
+ self.module = netscaler_lb_vserver
+ self.exited()
+ feature_mock.assert_called_with(client_mock, 'LB')
+
+ def test_ensure_feature_is_enabled_nitro_exception_caught(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_lb_vserver
+
+ client_mock = Mock()
+
+ lb_vserver_proxy_mock = Mock()
+ errorcode = 10
+ message = 'mock error'
+
+ class MockException(Exception):
+ def __init__(self):
+ self.errorcode = errorcode
+ self.message = message
+
+ feature_mock = Mock(side_effect=MockException)
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[True, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[True, True]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=feature_mock,
+ nitro_exception=MockException,
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.failed()
+ expected_msg = 'nitro exception errorcode=%s, message=%s' % (errorcode, message)
+ self.assertEqual(result['msg'], expected_msg, 'Failed to handle nitro exception')
+
+ def test_create_new_lb_vserver_workflow(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=Mock()),
+ lb_vserver_exists=Mock(side_effect=[False, True]),
+ lb_vserver_identical=Mock(side_effect=[True]),
+ servicegroup_bindings_identical=Mock(side_effect=[True, True]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.exited()
+ lb_vserver_proxy_mock.assert_has_calls([call.add()])
+ self.assertTrue(result['changed'])
+
+ def test_update_lb_vserver_workflow(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=Mock()),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[False, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[True, True]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ get_immutables_intersection=Mock(return_value=[]),
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.exited()
+ lb_vserver_proxy_mock.assert_has_calls([call.update()])
+ self.assertTrue(result['changed'])
+
+ def test_service_bindings_handling(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+ configured_dict = {
+ 'first': Mock(),
+ 'second': Mock(has_equal_attributes=Mock(return_value=False)),
+ }
+
+ actual_dict = {
+ 'second': Mock(),
+ 'third': Mock(),
+ }
+
+ client_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[False, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[True, True]),
+ service_bindings_identical=Mock(side_effect=[False, True]),
+ get_configured_service_bindings=Mock(return_value=configured_dict),
+ get_actual_service_bindings=Mock(return_value=actual_dict),
+
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ get_immutables_intersection=(Mock(return_value=[])),
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.exited()
+ configured_dict['first'].assert_has_calls([call.add()])
+
+ configured_dict['second'].assert_has_calls([call.has_equal_attributes(actual_dict['second']), call.add()])
+
+ actual_dict['second'].assert_has_calls([call.delete(client_mock, actual_dict['second'])])
+
+ actual_dict['third'].assert_has_calls([call.delete(client_mock, actual_dict['third'])])
+
+ self.assertTrue(result['changed'])
+
+ def test_servicegroup_bindings_handling(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+ configured_dict = {
+ 'first': Mock(),
+ 'second': Mock(has_equal_attributes=Mock(return_value=False)),
+ }
+
+ actual_dict = {
+ 'second': Mock(),
+ 'third': Mock(),
+ }
+
+ client_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[False, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[False, True]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+ get_configured_servicegroup_bindings=Mock(return_value=configured_dict),
+ get_actual_servicegroup_bindings=Mock(return_value=actual_dict),
+
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ get_immutables_intersection=(Mock(return_value=[])),
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.exited()
+ configured_dict['first'].assert_has_calls([call.add()])
+
+ configured_dict['second'].assert_has_calls([call.has_equal_attributes(actual_dict['second']), call.add()])
+
+ actual_dict['second'].assert_has_calls([call.delete(client_mock, actual_dict['second'])])
+
+ actual_dict['third'].assert_has_calls([call.delete(client_mock, actual_dict['third'])])
+
+ self.assertTrue(result['changed'])
+
+ def test_ssl_bindings_handling(self):
+ set_module_args(dict(
+ nitro_user='user',
+ nitro_pass='pass',
+ nsip='1.1.1.1',
+ state='present',
+ save_config=False,
+ servicetype='SSL',
+ ))
+ from ansible.modules.network.netscaler import netscaler_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+ ssl_sync_mock = Mock()
+
+ client_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[False, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[True, True]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+ ssl_certkey_bindings_identical=Mock(side_effect=[False, True]),
+ ssl_certkey_bindings_sync=ssl_sync_mock,
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ get_immutables_intersection=(Mock(return_value=[])),
+ nitro_exception=self.MockException,
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.exited()
+ self.assertTrue(len(ssl_sync_mock.mock_calls) > 0, msg='ssl cert_key bindings not called')
+ self.assertTrue(result['changed'])
+
+ def test_ssl_bindings_not_called_for_non_ssl_service(self):
+ set_module_args(dict(
+ nitro_user='user',
+ nitro_pass='pass',
+ nsip='1.1.1.1',
+ state='present',
+ save_config=False,
+ servicetype='HTTP',
+ ))
+ from ansible.modules.network.netscaler import netscaler_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+ ssl_sync_mock = Mock()
+
+ client_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[False, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[True, True]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+ ssl_certkey_bindings_identical=Mock(side_effect=[False, True]),
+ ssl_certkey_bindings_sync=ssl_sync_mock,
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ get_immutables_intersection=(Mock(return_value=[])),
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.exited()
+ ssl_sync_mock.assert_not_called()
+ self.assertTrue(result['changed'])
+
+ def test_server_exists_sanity_check(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+ ssl_sync_mock = Mock()
+
+ client_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[False, False]),
+ lb_vserver_identical=Mock(side_effect=[False, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[True, True]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+ ssl_certkey_bindings_identical=Mock(side_effect=[False, True]),
+ ssl_certkey_bindings_sync=ssl_sync_mock,
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ nitro_exception=self.MockException,
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.failed()
+ self.assertEqual(result['msg'], 'Did not create lb vserver')
+
+ def test_server_identical_sanity_check(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+ ssl_sync_mock = Mock()
+
+ client_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[False, False]),
+ servicegroup_bindings_identical=Mock(side_effect=[True, True]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+ ssl_certkey_bindings_identical=Mock(side_effect=[False, True]),
+ ssl_certkey_bindings_sync=ssl_sync_mock,
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ get_immutables_intersection=(Mock(return_value=[])),
+ nitro_exception=self.MockException,
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.failed()
+ self.assertEqual(result['msg'], 'lb vserver is not configured correctly')
+
+ def test_service_bindings_sanity_check(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+
+ client_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[False, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[True, True]),
+ service_bindings_identical=Mock(side_effect=[False, False]),
+ ssl_certkey_bindings_identical=Mock(side_effect=[False, False]),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ get_immutables_intersection=(Mock(return_value=[])),
+ nitro_exception=self.MockException,
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.failed()
+ self.assertEqual(result['msg'], 'service bindings are not identical')
+
+ def test_servicegroup_bindings_sanity_check(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+
+ client_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[False, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[False, False]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+ ssl_certkey_bindings_identical=Mock(side_effect=[False, False]),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ get_immutables_intersection=(Mock(return_value=[])),
+ nitro_exception=self.MockException,
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.failed()
+ self.assertEqual(result['msg'], 'servicegroup bindings are not identical')
+
+ def test_server_servicegroup_bindings_sanity_check(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+
+ client_mock = Mock()
+
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[False, True]),
+ servicegroup_bindings_identical=Mock(side_effect=[False, False]),
+ service_bindings_identical=Mock(side_effect=[True, True]),
+ ssl_certkey_bindings_identical=Mock(side_effect=[False, False]),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ get_immutables_intersection=(Mock(return_value=[])),
+ nitro_exception=self.MockException,
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.failed()
+ self.assertEqual(result['msg'], 'servicegroup bindings are not identical')
+
+ def test_absent_state_workflow(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+
+ client_mock = Mock()
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ lb_vserver_exists=Mock(side_effect=[True, False]),
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.exited()
+ lb_vserver_proxy_mock.assert_has_calls([call.delete()])
+ self.assertTrue(result['changed'])
+
+ def test_absent_state_sanity_check(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+
+ client_mock = Mock()
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ nitro_exception=self.MockException,
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.failed()
+ lb_vserver_proxy_mock.assert_has_calls([call.delete()])
+ self.assertEqual(result['msg'], 'lb vserver still exists')
+
+ def test_disabled_state_change_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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+
+ do_state_change_mock = Mock(return_value=Mock(errorcode=0))
+ client_mock = Mock()
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(return_value=True),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ do_state_change=do_state_change_mock,
+ ):
+ self.module = netscaler_lb_vserver
+ self.exited()
+ self.assertTrue(len(do_state_change_mock.mock_calls) > 0, msg='Did not call state change')
+
+ def test_get_immutables_failure(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_lb_vserver
+
+ lb_vserver_proxy_mock = Mock()
+
+ client_mock = Mock()
+ m = Mock(return_value=['some'])
+ with patch.multiple(
+ 'ansible.modules.network.netscaler.netscaler_lb_vserver',
+ get_nitro_client=Mock(return_value=client_mock),
+ ConfigProxy=Mock(return_value=lb_vserver_proxy_mock),
+ ensure_feature_is_enabled=Mock(),
+ lb_vserver_exists=Mock(side_effect=[True, True]),
+ lb_vserver_identical=Mock(side_effect=[False]),
+ do_state_change=Mock(return_value=Mock(errorcode=0)),
+ get_immutables_intersection=m,
+ nitro_exception=self.MockException,
+ ):
+ self.module = netscaler_lb_vserver
+ result = self.failed()
+ self.assertTrue(
+ result['msg'].startswith('Cannot update immutable attributes'),
+ msg='Did not handle immutables error correctly',
+ )