Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] netscaler.adc.servicegroup_servicegroupmember_binding does not properly handle *Server Based* members #297

Closed
fa-elepape opened this issue Nov 3, 2023 · 7 comments
Assignees
Labels

Comments

@fa-elepape
Copy link

Describe the bug
When using Server Based members, usage of servicegroup_servicegroupmember_binding breaks because it attempts to remove members from the Service Group providing both the ip and servername to the API.
This also makes ip a mandatory parameter even for Server Based members where it has no meaning.

To Reproduce
Steps to reproduce the behaviour:

  1. My ansible-playbook is...
---
- name: netscaler.adc.servicegroup_servicegroupmember_binding bug report
  hosts: localhost
  gather_facts: false

  tasks:
  - netscaler.adc.server:
      domain: example.com
      name: example.com
  - netscaler.adc.servicegroup:
      servicegroup_servicegroupmember_binding:
        binding_members:
        - ip:
          port: 443
          servername: example.com
          servicegroupname: example.com
        mode: desired
      servicegroupname: example.com
      servicetype: SSL
  - netscaler.adc.servicegroup:
      servicegroup_servicegroupmember_binding:
        binding_members:
        - ip:
          port: 443
          servername: example.com
          servicegroupname: example.com
        mode: desired
      servicegroupname: example.com
      servicetype: SSL
  1. The ansible-playbook command I executed is...
$ ansible-playbook --diff bug-report.yml
PLAY [netscaler.adc.servicegroup_servicegroupmember_binding bug report] *****************************************************

TASK [netscaler.adc.server] *************************************************************************************************
--- before
+++ after
@@ -1 +1,4 @@
-{}
+{
+    "domain": "example.com",
+    "name": "example.com"
+}

changed: [localhost]

TASK [netscaler.adc.servicegroup] *******************************************************************************************
--- before
+++ after
@@ -1 +1,4 @@
-{}
+{
+    "servicegroupname": "example.com",
+    "servicetype": "SSL"
+}


+   servicegroup_servicegroupmember_binding--example.com:None ADDED
changed: [localhost]

TASK [netscaler.adc.servicegroup] *******************************************************************************************
fatal: [localhost]: FAILED! => {
  "changed": false,
  "loglines": [
    "DEBUG: Initializing ModuleExecutor for resource servicegroup",
    "TRACE: ENTRY: get_valid_desired_states() called with ('servicegroup',), {}",
    "TRACE: EXIT: get_valid_desired_states() returned {'absent', 'enabled', 'disabled', 'present'}",
    "TRACE: ENTRY: get_netscaler_version() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>,), {}",
    "TRACE: ENTRY: get_resource() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>, 'nsversion'), {}",
    "TRACE: ENTRY: send() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>, 'GET', 'https://netscaler.example.com/nitro/v1/config/nsversion'), {}",
    "DEBUG: self={'_module': <ansible.module_utils.basic.AnsibleModule object at 0x7f7c8def63d0>, 'check_mode': False, 'api_path': 'nitro/v1/config', '_headers': {'Content-Type': 'application/json', 'User-Agent': 'ansible-ctxadc', 'X-NITRO-USER': '********', 'X-NITRO-PASS': '********'}}",
    "DEBUG: fetch_url()-resonse-info={'url': 'https://netscaler.example.com/nitro/v1/config/nsversion', 'status': 200, 'date': 'Fri, 03 Nov 2023 18:22:03 GMT', 'server': 'Apache', 'x-frame-options': 'SAMEORIGIN', 'expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'cache-control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'pragma': 'no-cache', 'vary': 'Accept-Encoding', 'feature-policy': \"camera 'none'; microphone 'none'; geolocation 'none'\", 'referrer-policy': 'no-referrer', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'content-length': '201', 'content-type': 'application/json; charset=utf-8', 'connection': 'close', 'cookies_string': '', 'cookies': {}, 'msg': 'OK (201 bytes)'}",
    "TRACE: EXIT: send() returned (200, {'errorcode': 0, 'message': 'Done', 'severity': 'NONE', 'nsversion': {'installedversion': False, 'version': 'NetScaler NS13.0: Build 90.12.nc, Date: May 15 2023, 03:58:09   (64-bit)', 'mode': '1'}})",
    "TRACE: EXIT: get_resource() returned [{'installedversion': False, 'version': 'NetScaler NS13.0: Build 90.12.nc, Date: May 15 2023, 03:58:09   (64-bit)', 'mode': '1'}]",
    "TRACE: EXIT: get_netscaler_version() returned (13.0, 90.12)",
    "INFO: NetScaler version: 13.0-90.12",
    "DEBUG: All params (including non module-specific params) are: {'servicegroup_servicegroupmember_binding': {'binding_members': [{'ip': None, 'port': 443, 'servername': 'example.com', 'servicegroupname': 'example.com'}], 'mode': 'desired'}, 'servicegroupname': 'example.com', 'servicetype': 'SSL', 'nsip': 'netscaler.example.com', 'nitro_user': '********', 'nitro_pass': '********', 'nitro_protocol': 'https', 'validate_certs': False, 'save_config': True, 'api_path': 'nitro/v1/config', 'state': 'present', 'nitro_auth_token': None, 'appflowlog': None, 'autodisabledelay': None, 'autodisablegraceful': None, 'autoscale': None, 'cacheable': None, 'cachetype': None, 'cip': None, 'cipheader': None, 'cka': None, 'clttimeout': None, 'cmp': None, 'comment': None, 'customserverid': None, 'dbsttl': None, 'delay': None, 'downstateflush': None, 'dup_weight': None, 'graceful': None, 'hashid': None, 'healthmonitor': None, 'httpprofilename': None, 'includemembers': None, 'maxbandwidth': None, 'maxclient': None, 'maxreq': None, 'memberport': None, 'monconnectionclose': None, 'monitor_name_svc': None, 'monthreshold': None, 'nameserver': None, 'netprofile': None, 'newname': None, 'order': None, 'pathmonitor': None, 'pathmonitorindv': None, 'port': None, 'rtspsessionidremap': None, 'serverid': None, 'servername': None, 'servicegroup_lbmonitor_binding': None, 'sp': None, 'svrtimeout': None, 'tcpb': None, 'tcpprofilename': None, 'td': None, 'useproxyport': None, 'usip': None, 'weight': None}",
    "TRACE: ENTRY: _filter_resource_module_params() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>,), {}",
    "DEBUG: Desired `servicegroup` module specific params are: {'servicegroupname': 'example.com', 'servicetype': 'SSL'}",
    "TRACE: EXIT: _filter_resource_module_params() returned None",
    "TRACE: ENTRY: _filter_desired_bindings() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>,), {}",
    "DEBUG: Desired `servicegroup` module specific bindings are: ['servicegroup_servicegroupmember_binding']",
    "TRACE: EXIT: _filter_desired_bindings() returned None",
    "TRACE: ENTRY: get_existing_resource() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>,), {}",
    "TRACE: ENTRY: get_resource() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>,), {'resource_name': 'servicegroup', 'resource_id': 'example.com', 'args': {}, 'filter': {}}",
    "TRACE: ENTRY: send() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>, 'GET', 'https://netscaler.example.com/nitro/v1/config/servicegroup/example.com'), {}",
    "DEBUG: self={'_module': <ansible.module_utils.basic.AnsibleModule object at 0x7f7c8def63d0>, 'check_mode': False, 'api_path': 'nitro/v1/config', '_headers': {'Content-Type': 'application/json', 'User-Agent': 'ansible-ctxadc', 'X-NITRO-USER': '********', 'X-NITRO-PASS': '********'}}",
    "DEBUG: fetch_url()-resonse-info={'url': 'https://netscaler.example.com/nitro/v1/config/servicegroup/example.com', 'status': 200, 'date': 'Fri, 03 Nov 2023 18:22:04 GMT', 'server': 'Apache', 'x-frame-options': 'SAMEORIGIN', 'expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'cache-control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'pragma': 'no-cache', 'vary': 'Accept-Encoding', 'feature-policy': \"camera 'none'; microphone 'none'; geolocation 'none'\", 'referrer-policy': 'no-referrer', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'content-length': '1446', 'content-type': 'application/json; charset=utf-8', 'connection': 'close', 'cookies_string': '', 'cookies': {}, 'msg': 'OK (1446 bytes)'}",
    "TRACE: EXIT: send() returned (200, {'errorcode': 0, 'message': 'Done', 'severity': 'NONE', 'servicegroup': [{'servicegroupname': 'example.com', 'numofconnections': 0, 'servicetype': 'SSL', 'td': '0', 'serviceconftype': True, 'cachetype': 'SERVER', 'maxclient': '0', 'maxreq': '0', 'cacheable': 'NO', 'cip': 'DISABLED', 'usip': 'NO', 'pathmonitor': 'NO', 'pathmonitorindv': 'NO', 'useproxyport': 'YES', 'monweight': '0', 'sp': 'OFF', 'rtspsessionidremap': 'OFF', 'clttimeout': 180, 'svrtimeout': 360, 'cka': 'YES', 'tcpb': 'YES', 'cmp': 'YES', 'maxbandwidth': '0', 'state': 'ENABLED', 'svrstate': 'DOWN', 'delay': 0, 'ip': '0.0.0.0', 'monthreshold': '0', 'monstate': 'ENABLED', 'monitor_state': 'Unknown', 'monstatcode': 0, 'monstatparam1': 0, 'monstatparam2': 0, 'monstatparam3': 0, 'monitortotalprobes': '0', 'monitortotalfailedprobes': '0', 'monitorcurrentfailedprobes': '0', 'downstateflush': 'ENABLED', 'statechangetimesec': 'Fri Nov  3 18:21:55 2023', 'statechangetimemsec': '846', 'tickssincelaststatechange': '854', 'stateupdatereason': '0', 'clmonowner': '0', 'clmonview': '0', 'groupcount': '0', 'hashid': '0', 'graceful': 'NO', 'healthmonitor': 'YES', 'appflowlog': 'ENABLED', 'autoscale': 'DISABLED', 'memberport': 0, 'serviceipstr': '0.0.0.0', 'passive': False, 'servicegroupeffectivestate': 'DOWN', 'monconnectionclose': 'NONE', 'nodefaultbindings': 'NO', 'nameserver': '0.0.0.0', 'dbsttl': 0, 'svcitmactsvcs': '0', 'svcitmboundsvcs': '0', 'responsetime': '0'}]})",
    "TRACE: EXIT: get_resource() returned [{'servicegroupname': 'example.com', 'numofconnections': 0, 'servicetype': 'SSL', 'td': '0', 'serviceconftype': True, 'cachetype': 'SERVER', 'maxclient': '0', 'maxreq': '0', 'cacheable': 'NO', 'cip': 'DISABLED', 'usip': 'NO', 'pathmonitor': 'NO', 'pathmonitorindv': 'NO', 'useproxyport': 'YES', 'monweight': '0', 'sp': 'OFF', 'rtspsessionidremap': 'OFF', 'clttimeout': 180, 'svrtimeout': 360, 'cka': 'YES', 'tcpb': 'YES', 'cmp': 'YES', 'maxbandwidth': '0', 'state': 'ENABLED', 'svrstate': 'DOWN', 'delay': 0, 'ip': '0.0.0.0', 'monthreshold': '0', 'monstate': 'ENABLED', 'monitor_state': 'Unknown', 'monstatcode': 0, 'monstatparam1': 0, 'monstatparam2': 0, 'monstatparam3': 0, 'monitortotalprobes': '0', 'monitortotalfailedprobes': '0', 'monitorcurrentfailedprobes': '0', 'downstateflush': 'ENABLED', 'statechangetimesec': 'Fri Nov  3 18:21:55 2023', 'statechangetimemsec': '846', 'tickssincelaststatechange': '854', 'stateupdatereason': '0', 'clmonowner': '0', 'clmonview': '0', 'groupcount': '0', 'hashid': '0', 'graceful': 'NO', 'healthmonitor': 'YES', 'appflowlog': 'ENABLED', 'autoscale': 'DISABLED', 'memberport': 0, 'serviceipstr': '0.0.0.0', 'passive': False, 'servicegroupeffectivestate': 'DOWN', 'monconnectionclose': 'NONE', 'nodefaultbindings': 'NO', 'nameserver': '0.0.0.0', 'dbsttl': 0, 'svcitmactsvcs': '0', 'svcitmboundsvcs': '0', 'responsetime': '0'}]",
    "TRACE: EXIT: get_existing_resource() returned {'servicegroupname': 'example.com', 'numofconnections': 0, 'servicetype': 'SSL', 'td': '0', 'serviceconftype': True, 'cachetype': 'SERVER', 'maxclient': '0', 'maxreq': '0', 'cacheable': 'NO', 'cip': 'DISABLED', 'usip': 'NO', 'pathmonitor': 'NO', 'pathmonitorindv': 'NO', 'useproxyport': 'YES', 'monweight': '0', 'sp': 'OFF', 'rtspsessionidremap': 'OFF', 'clttimeout': 180, 'svrtimeout': 360, 'cka': 'YES', 'tcpb': 'YES', 'cmp': 'YES', 'maxbandwidth': '0', 'state': 'ENABLED', 'svrstate': 'DOWN', 'delay': 0, 'ip': '0.0.0.0', 'monthreshold': '0', 'monstate': 'ENABLED', 'monitor_state': 'Unknown', 'monstatcode': 0, 'monstatparam1': 0, 'monstatparam2': 0, 'monstatparam3': 0, 'monitortotalprobes': '0', 'monitortotalfailedprobes': '0', 'monitorcurrentfailedprobes': '0', 'downstateflush': 'ENABLED', 'statechangetimesec': 'Fri Nov  3 18:21:55 2023', 'statechangetimemsec': '846', 'tickssincelaststatechange': '854', 'stateupdatereason': '0', 'clmonowner': '0', 'clmonview': '0', 'groupcount': '0', 'hashid': '0', 'graceful': 'NO', 'healthmonitor': 'YES', 'appflowlog': 'ENABLED', 'autoscale': 'DISABLED', 'memberport': 0, 'serviceipstr': '0.0.0.0', 'passive': False, 'servicegroupeffectivestate': 'DOWN', 'monconnectionclose': 'NONE', 'nodefaultbindings': 'NO', 'nameserver': '0.0.0.0', 'dbsttl': 0, 'svcitmactsvcs': '0', 'svcitmboundsvcs': '0', 'responsetime': '0'}",
    "TRACE: ENTRY: main() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>,), {}",
    "TRACE: ENTRY: create_or_update() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>,), {}",
    "TRACE: ENTRY: update_diff_list() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>,), {'existing': {'servicegroupname': 'example.com', 'numofconnections': 0, 'servicetype': 'SSL', 'td': '0', 'serviceconftype': True, 'cachetype': 'SERVER', 'maxclient': '0', 'maxreq': '0', 'cacheable': 'NO', 'cip': 'DISABLED', 'usip': 'NO', 'pathmonitor': 'NO', 'pathmonitorindv': 'NO', 'useproxyport': 'YES', 'monweight': '0', 'sp': 'OFF', 'rtspsessionidremap': 'OFF', 'clttimeout': 180, 'svrtimeout': 360, 'cka': 'YES', 'tcpb': 'YES', 'cmp': 'YES', 'maxbandwidth': '0', 'state': 'ENABLED', 'svrstate': 'DOWN', 'delay': 0, 'ip': '0.0.0.0', 'monthreshold': '0', 'monstate': 'ENABLED', 'monitor_state': 'Unknown', 'monstatcode': 0, 'monstatparam1': 0, 'monstatparam2': 0, 'monstatparam3': 0, 'monitortotalprobes': '0', 'monitortotalfailedprobes': '0', 'monitorcurrentfailedprobes': '0', 'downstateflush': 'ENABLED', 'statechangetimesec': 'Fri Nov  3 18:21:55 2023', 'statechangetimemsec': '846', 'tickssincelaststatechange': '854', 'stateupdatereason': '0', 'clmonowner': '0', 'clmonview': '0', 'groupcount': '0', 'hashid': '0', 'graceful': 'NO', 'healthmonitor': 'YES', 'appflowlog': 'ENABLED', 'autoscale': 'DISABLED', 'memberport': 0, 'serviceipstr': '0.0.0.0', 'passive': False, 'servicegroupeffectivestate': 'DOWN', 'monconnectionclose': 'NONE', 'nodefaultbindings': 'NO', 'nameserver': '0.0.0.0', 'dbsttl': 0, 'svcitmactsvcs': '0', 'svcitmboundsvcs': '0', 'responsetime': '0'}, 'desired': {'servicegroupname': 'example.com', 'servicetype': 'SSL'}}",
    "TRACE: EXIT: update_diff_list() returned None",
    "TRACE: ENTRY: is_resource_identical() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>,), {}",
    "TRACE: ENTRY: is_attribute_equal() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>, 'servicegroupname', 'example.com', 'example.com'), {}",
    "TRACE: EXIT: is_attribute_equal() returned True",
    "TRACE: ENTRY: is_attribute_equal() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>, 'servicetype', 'SSL', 'SSL'), {}",
    "TRACE: EXIT: is_attribute_equal() returned True",
    "TRACE: EXIT: is_resource_identical() returned True",
    "INFO: Resource `servicegroup:example.com` exists and is identical. No change required.",
    "TRACE: EXIT: create_or_update() returned None",
    "TRACE: ENTRY: sync_all_bindings() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>,), {}",
    "TRACE: ENTRY: sync_single_binding() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>, 'servicegroup_servicegroupmember_binding'), {}",
    "INFO: Binding mode is `desired`",
    "DEBUG: Desired binding members: [{'ip': None, 'port': 443, 'servername': 'example.com', 'servicegroupname': 'example.com'}]",
    "TRACE: ENTRY: get_bindings() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>,), {'binding_name': 'servicegroup_servicegroupmember_binding', 'binding_id': 'example.com'}",
    "TRACE: ENTRY: get_resource() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>,), {'resource_name': 'servicegroup_servicegroupmember_binding', 'resource_id': 'example.com'}",
    "TRACE: ENTRY: send() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>, 'GET', 'https://netscaler.example.com/nitro/v1/config/servicegroup_servicegroupmember_binding/example.com'), {}",
    "DEBUG: self={'_module': <ansible.module_utils.basic.AnsibleModule object at 0x7f7c8def63d0>, 'check_mode': False, 'api_path': 'nitro/v1/config', '_headers': {'Content-Type': 'application/json', 'User-Agent': 'ansible-ctxadc', 'X-NITRO-USER': '********', 'X-NITRO-PASS': '********'}}",
    "DEBUG: fetch_url()-resonse-info={'url': 'https://netscaler.example.com/nitro/v1/config/servicegroup_servicegroupmember_binding/example.com', 'status': 200, 'date': 'Fri, 03 Nov 2023 18:22:04 GMT', 'server': 'Apache', 'x-frame-options': 'SAMEORIGIN', 'expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'cache-control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'pragma': 'no-cache', 'vary': 'Accept-Encoding', 'feature-policy': \"camera 'none'; microphone 'none'; geolocation 'none'\", 'referrer-policy': 'no-referrer', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'content-length': '482', 'content-type': 'application/json; charset=utf-8', 'connection': 'close', 'cookies_string': '', 'cookies': {}, 'msg': 'OK (482 bytes)'}",
    "TRACE: EXIT: send() returned (200, {'errorcode': 0, 'message': 'Done', 'severity': 'NONE', 'servicegroup_servicegroupmember_binding': [{'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Fri Nov  3 18:21:56 2023', 'tickssincelaststatechange': '819', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}]})",
    "TRACE: EXIT: get_resource() returned [{'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Fri Nov  3 18:21:56 2023', 'tickssincelaststatechange': '819', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}]",
    "TRACE: EXIT: get_bindings() returned [{'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Fri Nov  3 18:21:56 2023', 'tickssincelaststatechange': '819', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}]",
    "DEBUG: Existing `servicegroup_servicegroupmember_binding` bindings: [{'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Fri Nov  3 18:21:56 2023', 'tickssincelaststatechange': '819', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}]",
    "DEBUG: To be deleted bindings: {'0.0.0.0'}",
    "DEBUG: To be added bindings: {None}",
    "DEBUG: To be updated bindings: set()",
    "TRACE: ENTRY: delete_bindings() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>,), {'binding_name': 'servicegroup_servicegroupmember_binding', 'bindprimary_key': 'ip', 'to_be_deleted_bindings': {'0.0.0.0'}, 'existing_bindings': [{'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Fri Nov  3 18:21:56 2023', 'tickssincelaststatechange': '819', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}]}",
    "TRACE: ENTRY: unbind_resource() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>,), {'binding_name': 'servicegroup_servicegroupmember_binding', 'binding_module_params': {'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Fri Nov  3 18:21:56 2023', 'tickssincelaststatechange': '819', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}}",
    "TRACE: ENTRY: delete_resource() called with (), {'client': <ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>, 'resource_name': 'servicegroup_servicegroupmember_binding', 'resource_module_params': {'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Fri Nov  3 18:21:56 2023', 'tickssincelaststatechange': '819', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}}",
    "TRACE: ENTRY: _check_delete_resource_params() called with ('servicegroup_servicegroupmember_binding', {'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Fri Nov  3 18:21:56 2023', 'tickssincelaststatechange': '819', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}), {}",
    "TRACE: EXIT: _check_delete_resource_params() returned (True, None)",
    "TRACE: ENTRY: is_resource_exists() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>, 'servicegroup_servicegroupmember_binding', 'example.com'), {'filter': {'ip': '0.0.0.0', 'port': 443, 'servername': 'example.com'}}",
    "TRACE: ENTRY: send() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>, 'GET', 'https://netscaler.example.com/nitro/v1/config/servicegroup_servicegroupmember_binding/example.com?filter=ip:0.0.0.0,port:443,servername:example.com'), {}",
    "DEBUG: self={'_module': <ansible.module_utils.basic.AnsibleModule object at 0x7f7c8def63d0>, 'check_mode': False, 'api_path': 'nitro/v1/config', '_headers': {'Content-Type': 'application/json', 'User-Agent': 'ansible-ctxadc', 'X-NITRO-USER': '********', 'X-NITRO-PASS': '********'}}",
    "DEBUG: fetch_url()-resonse-info={'url': 'https://netscaler.example.com/nitro/v1/config/servicegroup_servicegroupmember_binding/example.com?filter=ip:0.0.0.0,port:443,servername:example.com', 'status': 200, 'date': 'Fri, 03 Nov 2023 18:22:04 GMT', 'server': 'Apache', 'x-frame-options': 'SAMEORIGIN', 'expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'cache-control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'pragma': 'no-cache', 'vary': 'Accept-Encoding', 'feature-policy': \"camera 'none'; microphone 'none'; geolocation 'none'\", 'referrer-policy': 'no-referrer', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'content-length': '482', 'content-type': 'application/json; charset=utf-8', 'connection': 'close', 'cookies_string': '', 'cookies': {}, 'msg': 'OK (482 bytes)'}",
    "TRACE: EXIT: send() returned (200, {'errorcode': 0, 'message': 'Done', 'severity': 'NONE', 'servicegroup_servicegroupmember_binding': [{'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Fri Nov  3 18:21:56 2023', 'tickssincelaststatechange': '844', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}]})",
    "TRACE: EXIT: is_resource_exists() returned True",
    "TRACE: ENTRY: send() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7f7c8cd0cfa0>, 'DELETE', 'https://netscaler.example.com/nitro/v1/config/servicegroup_servicegroupmember_binding/example.com?args=ip:0.0.0.0,port:443,servername:example.com'), {}",
    "DEBUG: self={'_module': <ansible.module_utils.basic.AnsibleModule object at 0x7f7c8def63d0>, 'check_mode': False, 'api_path': 'nitro/v1/config', '_headers': {'Content-Type': 'application/json', 'User-Agent': 'ansible-ctxadc', 'X-NITRO-USER': '********', 'X-NITRO-PASS': '********'}}",
    "DEBUG: fetch_url()-resonse-info={'url': 'https://netscaler.example.com/nitro/v1/config/servicegroup_servicegroupmember_binding/example.com?args=ip:0.0.0.0,port:443,servername:example.com', 'status': 400, 'date': 'Fri, 03 Nov 2023 18:22:04 GMT', 'server': 'Apache', 'x-frame-options': 'SAMEORIGIN', 'expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'cache-control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'pragma': 'no-cache', 'feature-policy': \"camera 'none'; microphone 'none'; geolocation 'none'\", 'referrer-policy': 'no-referrer', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'content-length': '108', 'connection': 'close', 'content-type': 'application/json; charset=utf-8', 'msg': 'HTTP Error 400: Bad Request', 'body': b'{ \"errorcode\": 1092, \"message\": \"Arguments cannot both be specified [serverName, IP]\", \"severity\": \"ERROR\" }'}",
    "TRACE: EXIT: send() returned (400, {'errorcode': 1092, 'message': 'Arguments cannot both be specified [serverName, IP]', 'severity': 'ERROR'})",
    "TRACE: ENTRY: return_response() called with (), {'status_code': 400, 'response_body': {'errorcode': 1092, 'message': 'Arguments cannot both be specified [serverName, IP]', 'severity': 'ERROR'}, 'operation': 'delete_resource', 'resource_name': 'servicegroup_servicegroupmember_binding', 'resource_id': 'example.com'}",
    "ERROR: delete_resource FAILED; status_code: 400; Reason:{'errorcode': 1092, 'message': 'Arguments cannot both be specified [serverName, IP]', 'severity': 'ERROR'}",
    "TRACE: EXIT: return_response() returned (False, \"ERROR: delete_resource FAILED; status_code: 400; Reason:{'errorcode': 1092, 'message': 'Arguments cannot both be specified [serverName, IP]', 'severity': 'ERROR'}\")",
    "TRACE: EXIT: delete_resource() returned (False, \"ERROR: delete_resource FAILED; status_code: 400; Reason:{'errorcode': 1092, 'message': 'Arguments cannot both be specified [serverName, IP]', 'severity': 'ERROR'}\")",
    "TRACE: EXIT: unbind_resource() returned (False, \"ERROR: delete_resource FAILED; status_code: 400; Reason:{'errorcode': 1092, 'message': 'Arguments cannot both be specified [serverName, IP]', 'severity': 'ERROR'}\")",
    "TRACE: EXIT: delete_bindings() returned (False, \"ERROR: delete_resource FAILED; status_code: 400; Reason:{'errorcode': 1092, 'message': 'Arguments cannot both be specified [serverName, IP]', 'severity': 'ERROR'}\")",
    "TRACE: ENTRY: return_failure() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7f7c8def6460>, \"ERROR: delete_resource FAILED; status_code: 400; Reason:{'errorcode': 1092, 'message': 'Arguments cannot both be specified [serverName, IP]', 'severity': 'ERROR'}\"), {}"
  ],
  "msg": "ERROR: delete_resource FAILED; status_code: 400; Reason:{'errorcode': 1092, 'message': 'Arguments cannot both be specified [serverName, IP]', 'severity': 'ERROR'}"
}

PLAY RECAP ******************************************************************************************************************
localhost                  : ok=4    changed=4    unreachable=0    failed=1    skipped=0    rescued=0    ignored=0

Expected behaviour
When the Server is already part of the Service Group, I expect it to be left alone.
When a Server is not longer part of the Service Group, I expect it to be removed.

Environment (please complete the following information):

$ ansible-galaxy collection list netscaler.adc
Collection    Version
------------- -------
netscaler.adc 2.0.1

$ python3 --version
Python 3.9.16

$ ansible --version
ansible [core 2.13.4]
  config file = /etc/ansible/ansible.cfg
  configured module search path = ['~/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/local/share/ansible/lib64/python3.9/site-packages/ansible
  ansible collection location = /etc/ansible/collections:/usr/share/ansible/collections
  executable location = /usr/local/bin/ansible
  python version = 3.9.16 (main, May 29 2023, 00:00:00) [GCC 11.3.1 20221121 (Red Hat 11.3.1-4)]
  jinja version = 3.1.2
  libyaml = True

Additional context
I assume it is tied to the usage of ip as the primary key for the member binding instead of either one of servername for Server Based objects or ip for IP based ones.

@sumanth-lingappa
Copy link
Collaborator

sumanth-lingappa commented Nov 15, 2023

@fa-elepape, thank you for raising the issue here.
I am very happy to see the --diff views

Can you please help me with the equivalent netscaler commands you wish to apply?
This helps to reproduce the issue faster and resolve the issue sooner.

@fa-elepape
Copy link
Author

@sumanth-lingappa, the equivalent commands to actually bind or unbind the member are the following:

> bind serviceGroup example.com example.com 443
 Done
> unbind serviceGroup example.com example.com 443
 Done

The CLI doesn't distinguish IP from serverName which end up in the same positional parameter.
The API has two separate fields ip and servername which depend on whether the referenced server is IP based or domain based.

This appears to cause both the issue with the module attempting to unbind a member which should be left alone, as well as failing to unbind members because ip and servername are mutually exclusive in the NITRO API, yet both are passed by the module.

sumanth-lingappa added a commit that referenced this issue Dec 17, 2023
### Added

- introduced new states -- `created` and `imported` ([#295])

### Fixed

- removed default values in `nitro_resource_map.py` ([#313], [#314], [#311])
- default monitor can now be unbound from service ([#312])
- `ip` is now not mandatory for server based resources ([#297])
@sumanth-lingappa
Copy link
Collaborator

This issue is fixed in the latest release 2.1.0. Please check and update the issue accordingly.

@fa-elepape
Copy link
Author

@sumanth-lingappa, the currently implemented fix is partial since any mixing of both servername and ip members leads to the primary binding key being modifier to servername for all the bindings and KeyErrors being raised for ip based members. As a sample repoducer, see the following:

---
- name: netscaler.adc.servicegroup_servicegroupmember_binding bug report
  hosts: localhost
  gather_facts: false

  tasks:
  - netscaler.adc.server:
      domain: example.com
      name: example.com
  - netscaler.adc.servicegroup:
      servicegroup_servicegroupmember_binding:
        binding_members:
        - port: 443
          servername: example.com
          servicegroupname: example.com
        - ip: 127.0.0.1
          port: 443
          servicegroupname: example.com
        mode: desired
      servicegroupname: example.com
      servicetype: SSL

I never mix them so the current fix is good enough for my use case so I'm closing this issue, let me know if you'd like another one to track the aforementioned scenario.

As a side note, given the current implementation, you could break after setting bindprimary_key = "servername" to avoid needlessly iterating over all members 😉

@sumanth-lingappa
Copy link
Collaborator

@fa-elepape, thank you for updating the issue. I will keep this re-open till I test the scenario you had mentioned. No need for another issue.
If you have any more details on the scenario you mentioned, please do update here. This helps in faster reproduce the scenario.

@fa-elepape
Copy link
Author

@sumanth-lingappa, here's all the details I've got concerning the edge-case of the current workaround.

Using the above playbook yields the following error:

{
  "changed": false,
  "loglines": [
    "DEBUG: Initializing ModuleExecutor for resource servicegroup",
    "TRACE: ENTRY: get_valid_desired_states() called with ('servicegroup',), {}",
    "TRACE: EXIT: get_valid_desired_states() returned {'absent', 'present', 'disabled', 'enabled'}",
    "TRACE: ENTRY: get_netscaler_version() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>,), {}",
    "TRACE: ENTRY: get_resource() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>, 'nsversion'), {}",
    "TRACE: ENTRY: get() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>,), {'resource': 'nsversion', 'id': None, 'args': None, 'attrs': None, 'filter': None}",
    "TRACE: ENTRY: url_builder() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>, 'nsversion'), {'id': None, 'args': None, 'attrs': None, 'filter': None}",
    "TRACE: EXIT: url_builder() returned https://netscaler.example.com/nitro/v1/config/nsversion",
    "TRACE: ENTRY: send() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>, 'GET', 'https://netscaler.example.com/nitro/v1/config/nsversion'), {}",
    "DEBUG: self={'_module': <ansible.module_utils.basic.AnsibleModule object at 0x7ff0dc2a0e50>, 'check_mode': False, 'api_path': 'nitro/v1/config', '_headers': {'Content-Type': 'application/json', 'User-Agent': 'ansible-ctxadc', 'X-NITRO-USER': '********', 'X-NITRO-PASS': '********'}}",
    "DEBUG: fetch_url()-resonse-info={'url': 'https://netscaler.example.com/nitro/v1/config/nsversion', 'status': 200, 'date': 'Mon, 18 Dec 2023 15:58:46 GMT', 'server': 'Apache', 'x-frame-options': 'SAMEORIGIN', 'expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'cache-control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'pragma': 'no-cache', 'vary': 'Accept-Encoding', 'feature-policy': \"camera 'none'; microphone 'none'; geolocation 'none'\", 'referrer-policy': 'no-referrer', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'content-length': '201', 'content-type': 'application/json; charset=utf-8', 'connection': 'close', 'cookies_string': '', 'cookies': {}, 'msg': 'OK (201 bytes)'}",
    "TRACE: EXIT: send() returned (200, {'errorcode': 0, 'message': 'Done', 'severity': 'NONE', 'nsversion': {'installedversion': False, 'version': 'NetScaler NS13.0: Build 90.12.nc, Date: May 15 2023, 03:58:09   (64-bit)', 'mode': '1'}})",
    "TRACE: EXIT: get() returned (200, {'errorcode': 0, 'message': 'Done', 'severity': 'NONE', 'nsversion': {'installedversion': False, 'version': 'NetScaler NS13.0: Build 90.12.nc, Date: May 15 2023, 03:58:09   (64-bit)', 'mode': '1'}})",
    "TRACE: EXIT: get_resource() returned[{'installedversion': False, 'version': 'NetScaler NS13.0: Build 90.12.nc, Date: May 15 2023, 03:58:09   (64-bit)', 'mode': '1'}]",
    "TRACE: EXIT: get_netscaler_version() returned (13.0, 90.12)",
    "INFO: NetScaler version: 13.0-90.12",
    "DEBUG: All params (including non module-specific params) are: {'servicegroup_servicegroupmember_binding': {'binding_members': [{'port': 443, 'servername': 'example.com', 'servicegroupname': 'example.com'}, {'ip': '127.0.0.1', 'port': 443, 'servicegroupname': 'example.com'}], 'mode': 'desired'}, 'servicegroupname': 'example.com', 'servicetype': 'SSL','nsip': 'netscaler.example.com', 'nitro_user': '********', 'nitro_pass': '********', 'nitro_protocol': 'https', 'validate_certs': True, 'save_config': True, 'api_path': 'nitro/v1/config','state': 'present', 'nitro_auth_token': None, 'appflowlog': None, 'autodelayedtrofs': None, 'autodisabledelay': None, 'autodisablegraceful': None, 'autoscale': None, 'cacheable': None, 'cachetype': None, 'cip': None, 'cipheader': None, 'cka': None, 'clttimeout': None, 'cmp': None, 'comment': None, 'customserverid': None, 'dbsttl': None, 'delay': None, 'downstateflush': None, 'dup_weight':None, 'graceful': None, 'hashid': None, 'healthmonitor': None, 'httpprofilename': None, 'includemembers': None, 'maxbandwidth': None, 'maxclient': None, 'maxreq': None, 'memberport': None, 'monconnectionclose': None, 'monitor_name_svc': None, 'monthreshold': None, 'nameserver': None, 'netprofile': None, 'newname': None, 'order': None, 'pathmonitor': None, 'pathmonitorindv': None, 'port': None, 'rtspsessionidremap': None, 'serverid': None, 'servername': None, 'servicegroup_lbmonitor_binding': None, 'sp': None, 'svrtimeout': None, 'tcpb': None, 'tcpprofilename': None, 'td': None, 'useproxyport': None, 'usip': None, 'weight': None}",
    "TRACE: ENTRY: _filter_resource_module_params() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>,), {}",
    "DEBUG: self.module.params: {'servicegroup_servicegroupmember_binding': {'binding_members': [{'port': 443, 'servername': 'example.com', 'servicegroupname': 'example.com'}, {'ip': '127.0.0.1', 'port': 443, 'servicegroupname': 'example.com'}], 'mode': 'desired'}, 'servicegroupname': 'example.com', 'servicetype': 'SSL', 'nsip': 'netscaler.example.com', 'nitro_user': '********', 'nitro_pass': '********', 'nitro_protocol': 'https', 'validate_certs': True, 'save_config': True, 'api_path': 'nitro/v1/config', 'state': 'present', 'nitro_auth_token': None, 'appflowlog': None, 'autodelayedtrofs': None, 'autodisabledelay': None, 'autodisablegraceful': None, 'autoscale': None, 'cacheable': None, 'cachetype': None, 'cip': None, 'cipheader': None, 'cka': None, 'clttimeout': None, 'cmp': None, 'comment': None, 'customserverid': None, 'dbsttl': None, 'delay': None, 'downstateflush': None, 'dup_weight': None, 'graceful': None, 'hashid': None, 'healthmonitor': None, 'httpprofilename': None, 'includemembers': None, 'maxbandwidth': None, 'maxclient': None, 'maxreq': None, 'memberport': None, 'monconnectionclose': None, 'monitor_name_svc': None, 'monthreshold': None, 'nameserver': None, 'netprofile': None, 'newname': None, 'order': None, 'pathmonitor': None, 'pathmonitorindv': None, 'port': None, 'rtspsessionidremap': None, 'serverid': None, 'servername': None, 'servicegroup_lbmonitor_binding': None, 'sp': None, 'svrtimeout': None, 'tcpb': None, 'tcpprofilename': None, 'td': None, 'useproxyport': None, 'usip': None, 'weight': None}", "DEBUG: k: servicegroup_servicegroupmember_binding, v: {'binding_members': [{'port': 443, 'servername': 'example.com', 'servicegroupname': 'example.com'}, {'ip': '127.0.0.1', 'port': 443, 'servicegroupname': 'example.com'}], 'mode': 'desired'}",
    "DEBUG: k: servicegroupname, v: example.com",
    "DEBUG: k: servicetype, v: SSL",
    "DEBUG: k: nsip, v: netscaler.example.com",
    "DEBUG: k: nitro_user, v: ********",
    "DEBUG: k: nitro_pass, v: ********",
    "DEBUG: k: nitro_protocol, v: https",
    "DEBUG: k: validate_certs, v: True",
    "DEBUG: k: save_config, v: True",
    "DEBUG: k: api_path, v: nitro/v1/config",
    "DEBUG: k: state, v: present",
    "DEBUG: k: nitro_auth_token, v: None",
    "DEBUG: k: appflowlog, v: None",
    "DEBUG: k: autodelayedtrofs, v: None",
    "DEBUG: k: autodisabledelay, v: None",
    "DEBUG: k: autodisablegraceful, v: None",
    "DEBUG: k: autoscale, v: None",
    "DEBUG: k: cacheable, v: None",
    "DEBUG: k: cachetype, v: None",
    "DEBUG: k: cip, v: None",
    "DEBUG: k: cipheader, v: None",
    "DEBUG: k: cka, v: None",
    "DEBUG: k: clttimeout, v: None",
    "DEBUG: k: cmp, v: None",
    "DEBUG: k: comment, v: None",
    "DEBUG: k: customserverid, v: None",
    "DEBUG: k: dbsttl, v: None",
    "DEBUG: k: delay, v: None",
    "DEBUG: k: downstateflush, v: None",
    "DEBUG: k: dup_weight, v: None",
    "DEBUG: k: graceful, v: None",
    "DEBUG: k: hashid, v: None",
    "DEBUG: k: healthmonitor, v: None",
    "DEBUG: k: httpprofilename, v: None",
    "DEBUG: k: includemembers, v: None",
    "DEBUG: k: maxbandwidth, v: None",
    "DEBUG: k: maxclient, v: None",
    "DEBUG: k: maxreq, v: None",
    "DEBUG: k: memberport, v: None",
    "DEBUG: k: monconnectionclose, v: None",
    "DEBUG: k: monitor_name_svc, v: None",
    "DEBUG: k: monthreshold, v: None",
    "DEBUG: k: nameserver, v: None",
    "DEBUG: k: netprofile, v: None",
    "DEBUG: k: newname, v: None",
    "DEBUG: k: order, v: None",
    "DEBUG: k: pathmonitor, v: None",
    "DEBUG: k: pathmonitorindv, v: None",
    "DEBUG: k: port, v: None",
    "DEBUG: k: rtspsessionidremap, v: None",
    "DEBUG:k: serverid, v: None",
    "DEBUG: k: servername, v: None",
    "DEBUG: k: servicegroup_lbmonitor_binding, v: None",
    "DEBUG: k: sp, v: None",
    "DEBUG: k: svrtimeout, v: None",
    "DEBUG: k: tcpb, v: None",
    "DEBUG: k: tcpprofilename, v: None",
    "DEBUG: k: td, v: None",
    "DEBUG: k: useproxyport, v: None",
    "DEBUG: k: usip, v: None",
    "DEBUG: k: weight, v: None",
    "DEBUG: Desired `servicegroup` module specific params are: {'servicegroupname': 'example.com', 'servicetype': 'SSL'}",
    "TRACE: EXIT: _filter_resource_module_params() returned None",
    "TRACE: ENTRY: _filter_desired_bindings() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>,), {}",
    "DEBUG: Desired `servicegroup` module specific bindings are: ['servicegroup_servicegroupmember_binding']",
    "TRACE: EXIT: _filter_desired_bindings() returned None",
    "TRACE: ENTRY: get_existing_resource() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>,), {}",
    "TRACE: ENTRY: get_resource() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>,), {'resource_name': 'servicegroup', 'resource_id': 'example.com', 'args': {}, 'filter': {}}",
    "TRACE: ENTRY: get() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>,), {'resource': 'servicegroup', 'id': 'example.com', 'args': {}, 'attrs': None, 'filter': {}}",
    "TRACE: ENTRY: url_builder() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>, 'servicegroup'), {'id': 'example.com', 'args': {}, 'attrs': None, 'filter': {}}",
    "TRACE: EXIT: url_builder() returned https://netscaler.example.com/nitro/v1/config/servicegroup/example.com",
    "TRACE: ENTRY: send() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>, 'GET', 'https://netscaler.example.com/nitro/v1/config/servicegroup/example.com'), {}",
    "DEBUG: self={'_module': <ansible.module_utils.basic.AnsibleModule object at 0x7ff0dc2a0e50>, 'check_mode': False, 'api_path': 'nitro/v1/config', '_headers': {'Content-Type': 'application/json', 'User-Agent': 'ansible-ctxadc', 'X-NITRO-USER': '********', 'X-NITRO-PASS': '********'}}",
    "DEBUG: fetch_url()-resonse-info={'url': 'https://netscaler.example.com/nitro/v1/config/servicegroup/example.com', 'status': 200, 'date': 'Mon, 18 Dec 2023 15:58:46 GMT', 'server': 'Apache', 'x-frame-options': 'SAMEORIGIN', 'expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'cache-control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'pragma': 'no-cache', 'vary': 'Accept-Encoding', 'feature-policy': \"camera 'none'; microphone 'none'; geolocation 'none'\", 'referrer-policy': 'no-referrer', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'content-length': '1448', 'content-type': 'application/json; charset=utf-8', 'connection': 'close', 'cookies_string': '', 'cookies': {}, 'msg': 'OK (1448 bytes)'}",
    "TRACE: EXIT: send() returned (200, {'errorcode': 0, 'message': 'Done', 'severity': 'NONE', 'servicegroup': [{'servicegroupname': 'example.com', 'numofconnections': 0, 'servicetype': 'SSL', 'td': '0', 'serviceconftype': True, 'cachetype': 'SERVER', 'maxclient': '0', 'maxreq': '0', 'cacheable': 'NO', 'cip': 'DISABLED', 'usip': 'NO', 'pathmonitor': 'NO', 'pathmonitorindv': 'NO', 'useproxyport': 'YES', 'monweight': '0', 'sp': 'OFF', 'rtspsessionidremap': 'OFF', 'clttimeout': 180, 'svrtimeout': 360, 'cka': 'YES', 'tcpb': 'YES', 'cmp': 'YES', 'maxbandwidth': '0', 'state': 'ENABLED', 'svrstate': 'DOWN', 'delay': 0, 'ip': '0.0.0.0', 'monthreshold': '0', 'monstate': 'ENABLED', 'monitor_state': 'Unknown', 'monstatcode': 0, 'monstatparam1': 0, 'monstatparam2': 0, 'monstatparam3': 0, 'monitortotalprobes': '0', 'monitortotalfailedprobes': '0', 'monitorcurrentfailedprobes': '0', 'downstateflush': 'ENABLED', 'statechangetimesec': 'Mon Dec 18 15:52:38 2023', 'statechangetimemsec': '115', 'tickssincelaststatechange': '36888', 'stateupdatereason': '0', 'clmonowner': '0', 'clmonview': '0', 'groupcount': '0', 'hashid': '0', 'graceful': 'NO', 'healthmonitor': 'YES', 'appflowlog': 'ENABLED', 'autoscale': 'DISABLED', 'memberport': 0, 'serviceipstr': '0.0.0.0', 'passive': False, 'servicegroupeffectivestate': 'DOWN', 'monconnectionclose': 'NONE', 'nodefaultbindings': 'NO', 'nameserver': '0.0.0.0', 'dbsttl': 0, 'svcitmactsvcs': '0', 'svcitmboundsvcs': '0', 'responsetime': '0'}]})",
    "TRACE: EXIT: get() returned (200, {'errorcode': 0, 'message': 'Done', 'severity': 'NONE', 'servicegroup': [{'servicegroupname': 'example.com', 'numofconnections': 0, 'servicetype': 'SSL', 'td': '0', 'serviceconftype': True, 'cachetype': 'SERVER', 'maxclient': '0', 'maxreq': '0', 'cacheable': 'NO', 'cip': 'DISABLED', 'usip': 'NO', 'pathmonitor': 'NO', 'pathmonitorindv': 'NO', 'useproxyport': 'YES', 'monweight': '0', 'sp': 'OFF', 'rtspsessionidremap': 'OFF', 'clttimeout': 180, 'svrtimeout': 360, 'cka': 'YES', 'tcpb': 'YES', 'cmp': 'YES', 'maxbandwidth': '0', 'state': 'ENABLED', 'svrstate': 'DOWN', 'delay': 0, 'ip': '0.0.0.0', 'monthreshold': '0', 'monstate': 'ENABLED', 'monitor_state': 'Unknown', 'monstatcode': 0, 'monstatparam1': 0, 'monstatparam2': 0, 'monstatparam3': 0, 'monitortotalprobes': '0', 'monitortotalfailedprobes': '0', 'monitorcurrentfailedprobes': '0', 'downstateflush': 'ENABLED', 'statechangetimesec': 'Mon Dec 18 15:52:38 2023', 'statechangetimemsec': '115', 'tickssincelaststatechange': '36888', 'stateupdatereason': '0', 'clmonowner': '0', 'clmonview': '0', 'groupcount': '0', 'hashid': '0', 'graceful': 'NO', 'healthmonitor': 'YES', 'appflowlog': 'ENABLED', 'autoscale': 'DISABLED', 'memberport': 0, 'serviceipstr': '0.0.0.0', 'passive': False, 'servicegroupeffectivestate': 'DOWN', 'monconnectionclose': 'NONE', 'nodefaultbindings': 'NO', 'nameserver': '0.0.0.0', 'dbsttl': 0, 'svcitmactsvcs': '0', 'svcitmboundsvcs': '0', 'responsetime': '0'}]})",
    "TRACE: EXIT: get_resource() returned [{'servicegroupname': 'example.com', 'numofconnections': 0, 'servicetype': 'SSL', 'td': '0', 'serviceconftype': True, 'cachetype': 'SERVER', 'maxclient': '0', 'maxreq': '0', 'cacheable': 'NO', 'cip': 'DISABLED', 'usip': 'NO', 'pathmonitor': 'NO', 'pathmonitorindv': 'NO', 'useproxyport': 'YES', 'monweight': '0', 'sp': 'OFF', 'rtspsessionidremap': 'OFF', 'clttimeout': 180, 'svrtimeout': 360, 'cka': 'YES', 'tcpb': 'YES', 'cmp': 'YES', 'maxbandwidth': '0', 'state': 'ENABLED', 'svrstate': 'DOWN', 'delay': 0, 'ip': '0.0.0.0', 'monthreshold': '0', 'monstate': 'ENABLED', 'monitor_state': 'Unknown', 'monstatcode': 0, 'monstatparam1': 0, 'monstatparam2': 0, 'monstatparam3': 0, 'monitortotalprobes': '0', 'monitortotalfailedprobes': '0', 'monitorcurrentfailedprobes': '0', 'downstateflush': 'ENABLED', 'statechangetimesec': 'Mon Dec 18 15:52:38 2023', 'statechangetimemsec': '115', 'tickssincelaststatechange': '36888', 'stateupdatereason': '0', 'clmonowner': '0', 'clmonview': '0', 'groupcount': '0', 'hashid': '0', 'graceful': 'NO', 'healthmonitor': 'YES', 'appflowlog': 'ENABLED', 'autoscale': 'DISABLED', 'memberport': 0, 'serviceipstr': '0.0.0.0', 'passive': False, 'servicegroupeffectivestate': 'DOWN', 'monconnectionclose': 'NONE', 'nodefaultbindings': 'NO', 'nameserver': '0.0.0.0', 'dbsttl': 0, 'svcitmactsvcs': '0', 'svcitmboundsvcs': '0', 'responsetime': '0'}]",
    "TRACE: EXIT: get_existing_resource() returned {'servicegroupname': 'example.com', 'numofconnections': 0, 'servicetype': 'SSL','td': '0', 'serviceconftype': True, 'cachetype': 'SERVER', 'maxclient': '0', 'maxreq': '0', 'cacheable': 'NO', 'cip': 'DISABLED', 'usip': 'NO', 'pathmonitor': 'NO', 'pathmonitorindv': 'NO', 'useproxyport': 'YES', 'monweight': '0', 'sp': 'OFF', 'rtspsessionidremap': 'OFF', 'clttimeout': 180, 'svrtimeout': 360, 'cka': 'YES', 'tcpb': 'YES', 'cmp': 'YES', 'maxbandwidth': '0', 'state': 'ENABLED', 'svrstate': 'DOWN', 'delay': 0, 'ip': '0.0.0.0', 'monthreshold': '0', 'monstate': 'ENABLED', 'monitor_state': 'Unknown', 'monstatcode': 0, 'monstatparam1': 0, 'monstatparam2': 0, 'monstatparam3': 0, 'monitortotalprobes': '0', 'monitortotalfailedprobes': '0', 'monitorcurrentfailedprobes': '0', 'downstateflush': 'ENABLED', 'statechangetimesec': 'Mon Dec 18 15:52:38 2023', 'statechangetimemsec': '115', 'tickssincelaststatechange': '36888', 'stateupdatereason': '0', 'clmonowner': '0', 'clmonview': '0', 'groupcount': '0', 'hashid': '0', 'graceful': 'NO', 'healthmonitor': 'YES', 'appflowlog': 'ENABLED', 'autoscale': 'DISABLED', 'memberport': 0, 'serviceipstr': '0.0.0.0', 'passive': False, 'servicegroupeffectivestate': 'DOWN', 'monconnectionclose': 'NONE', 'nodefaultbindings':'NO', 'nameserver': '0.0.0.0', 'dbsttl': 0, 'svcitmactsvcs': '0', 'svcitmboundsvcs': '0', 'responsetime': '0'}",
    "TRACE: ENTRY: main() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>,), {}",
    "TRACE: ENTRY: create_or_update() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>,), {}",
    "TRACE: ENTRY: update_diff_list() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>,), {'existing': {'servicegroupname': 'example.com', 'numofconnections': 0, 'servicetype': 'SSL', 'td': '0', 'serviceconftype': True, 'cachetype': 'SERVER', 'maxclient': '0', 'maxreq': '0', 'cacheable': 'NO', 'cip': 'DISABLED', 'usip': 'NO', 'pathmonitor': 'NO', 'pathmonitorindv': 'NO', 'useproxyport': 'YES', 'monweight': '0', 'sp': 'OFF', 'rtspsessionidremap': 'OFF', 'clttimeout': 180, 'svrtimeout': 360, 'cka': 'YES', 'tcpb': 'YES', 'cmp': 'YES', 'maxbandwidth': '0', 'state': 'ENABLED', 'svrstate': 'DOWN', 'delay': 0, 'ip': '0.0.0.0', 'monthreshold': '0', 'monstate': 'ENABLED', 'monitor_state': 'Unknown', 'monstatcode': 0, 'monstatparam1': 0, 'monstatparam2': 0, 'monstatparam3': 0, 'monitortotalprobes': '0', 'monitortotalfailedprobes': '0', 'monitorcurrentfailedprobes': '0', 'downstateflush': 'ENABLED', 'statechangetimesec': 'Mon Dec 18 15:52:38 2023', 'statechangetimemsec': '115', 'tickssincelaststatechange': '36888', 'stateupdatereason': '0', 'clmonowner': '0', 'clmonview': '0', 'groupcount': '0', 'hashid': '0', 'graceful': 'NO', 'healthmonitor': 'YES', 'appflowlog': 'ENABLED', 'autoscale': 'DISABLED', 'memberport': 0, 'serviceipstr': '0.0.0.0', 'passive': False, 'servicegroupeffectivestate': 'DOWN', 'monconnectionclose': 'NONE', 'nodefaultbindings': 'NO', 'nameserver': '0.0.0.0', 'dbsttl': 0, 'svcitmactsvcs': '0', 'svcitmboundsvcs': '0','responsetime': '0'}, 'desired': {'servicegroupname': 'example.com', 'servicetype': 'SSL'}}",
    "TRACE: EXIT: update_diff_list() returned None",
    "TRACE: ENTRY: is_resource_identical() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>,), {}",
    "TRACE: ENTRY: is_attribute_equal() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>, 'servicegroupname', 'example.com', 'example.com'), {}",
    "TRACE: EXIT: is_attribute_equal() returned True",
    "TRACE: ENTRY: is_attribute_equal() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>, 'servicetype', 'SSL', 'SSL'), {}",
    "TRACE: EXIT: is_attribute_equal() returned True",
    "TRACE: EXIT: is_resource_identical() returned True",
    "INFO: Resource `servicegroup:example.com` exists and is identical. No change required.",
    "TRACE: EXIT: create_or_update() returned None",
    "TRACE: ENTRY: sync_all_bindings() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>,), {}",
    "TRACE: ENTRY: sync_single_binding() called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>, 'servicegroup_servicegroupmember_binding'), {}",
    "INFO: Binding mode is `desired`",
    "DEBUG: Desired binding members: [{'port': 443, 'servername': 'example.com', 'servicegroupname': 'example.com'}, {'ip': '127.0.0.1', 'port': 443, 'servicegroupname': 'example.com'}]",
    "TRACE: ENTRY: get_bindings() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>,), {'binding_name': 'servicegroup_servicegroupmember_binding', 'binding_id': 'example.com'}",
    "TRACE: ENTRY: get_resource() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>,), {'resource_name': 'servicegroup_servicegroupmember_binding', 'resource_id': 'example.com'}",
    "TRACE: ENTRY: get() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>,), {'resource': 'servicegroup_servicegroupmember_binding', 'id': 'example.com', 'args': None, 'attrs': None, 'filter': None}",
    "TRACE: ENTRY: url_builder() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>, 'servicegroup_servicegroupmember_binding'), {'id': 'example.com', 'args': None, 'attrs': None, 'filter': None}",
    "TRACE: EXIT: url_builder() returned https://netscaler.example.com/nitro/v1/config/servicegroup_servicegroupmember_binding/example.com",
    "TRACE: ENTRY: send() called with (<ansible_collections.netscaler.adc.plugins.module_utils.client.NitroAPIClient object at 0x7ff0dc2a3350>, 'GET', 'https://netscaler.example.com/nitro/v1/config/servicegroup_servicegroupmember_binding/example.com'), {}",
    "DEBUG: self={'_module': <ansible.module_utils.basic.AnsibleModule object at 0x7ff0dc2a0e50>, 'check_mode': False, 'api_path': 'nitro/v1/config', '_headers': {'Content-Type': 'application/json', 'User-Agent': 'ansible-ctxadc', 'X-NITRO-USER': '********', 'X-NITRO-PASS': '********'}}",
    "DEBUG: fetch_url()-resonse-info={'url': 'https://netscaler.example.com/nitro/v1/config/servicegroup_servicegroupmember_binding/example.com', 'status': 200, 'date': 'Mon, 18 Dec 2023 15:58:47 GMT', 'server': 'Apache', 'x-frame-options': 'SAMEORIGIN', 'expires': 'Thu, 19 Nov 1981 08:52:00 GMT', 'cache-control': 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0', 'pragma': 'no-cache', 'vary': 'Accept-Encoding', 'feature-policy': \"camera 'none'; microphone 'none'; geolocation 'none'\", 'referrer-policy': 'no-referrer', 'x-xss-protection': '1; mode=block', 'x-content-type-options': 'nosniff', 'content-length': '484', 'content-type': 'application/json; charset=utf-8', 'connection': 'close', 'cookies_string': '', 'cookies': {}, 'msg': 'OK (484 bytes)'}",
    "TRACE: EXIT: send() returned (200, {'errorcode': 0, 'message': 'Done', 'severity': 'NONE', 'servicegroup_servicegroupmember_binding': [{'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Mon Dec 18 15:54:14 2023', 'tickssincelaststatechange': '27338', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}]})",
    "TRACE: EXIT: get() returned (200, {'errorcode': 0, 'message': 'Done', 'severity': 'NONE', 'servicegroup_servicegroupmember_binding': [{'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Mon Dec 18 15:54:14 2023', 'tickssincelaststatechange': '27338', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}]})",
    "TRACE: EXIT: get_resource() returned [{'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate': 'DOWN', 'statechangetimesec': 'Mon Dec 18 15:54:14 2023', 'tickssincelaststatechange': '27338', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}]",
    "TRACE: EXIT: get_bindings() returned [{'servicegroupname': 'example.com', 'ip': '0.0.0.0', 'port': 443, 'svrstate':'DOWN', 'statechangetimesec': 'Mon Dec 18 15:54:14 2023', 'tickssincelaststatechange': '27338', 'weight': '1', 'servername': 'example.com', 'customserverid': 'None', 'serverid': '0', 'state': 'ENABLED', 'hashid': '0', 'graceful': 'NO', 'delay': 0, 'delay1': 0, 'nameserver': '0.0.0.0', 'dbsttl': 0}]",
    "TRACE: ENTRY: return_failure()called with (<ansible_collections.netscaler.adc.plugins.module_utils.module_executor.ModuleExecutor object at 0x7ff0dd1c9e50>, \"Exception <class 'KeyError'>: 'servername'\"), {}"
  ],
  "msg": "Exception <class 'KeyError'>: 'servername'"
}

This looks to me like the current workaround from aa29b50 changes the bindprimary_key to servername for all binding members if any of them is servername-based. At this point any ip based member runs into KeyError because it has no servername attribute.

@sumanth-lingappa
Copy link
Collaborator

This has been fixed in the above PR.
You can download the latest unreleased netscaler.adc ansible collection via below command

ansible-galaxy collection install "git+https://github.com/netscaler/ansible-collection-netscaleradc.git" --force

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants