---
# Test code for the netapp_e_iscsi_interface module
# (c) 2018, NetApp, Inc
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)


# ***********************
# *** Local test data ***
# ***********************
- name: NetApp Test iSCSI Interface module
  fail:
    msg: 'Please define netapp_e_api_username, netapp_e_api_password, netapp_e_api_host, and netapp_e_ssid.'
  when:  netapp_e_api_username is undefined or netapp_e_api_password is undefined
          or netapp_e_api_host is undefined or netapp_e_ssid is undefined
  vars:
    credentials: &creds
      api_url: "https://{{ netapp_e_api_host }}/devmgr/v2"
      api_username: "{{ netapp_e_api_username }}"
      api_password: "{{ netapp_e_api_password }}"
      ssid: "{{ netapp_e_ssid }}"
      validate_certs: no
    array: &array
        subnet: 255.255.255.0
        gateway: 10.10.10.1
        A:
        - channel: 1
          max_frame_size: 1500
        - channel: 2
          max_frame_size: 2000
        - channel: 3
          max_frame_size: 9000
        - channel: 4
          max_frame_size: 1500
        - channel: 5
          max_frame_size: 2000
        - channel: 6
          max_frame_size: 9000
        B:
        - channel: 1
          max_frame_size: 9000
        - channel: 2
          max_frame_size: 1500
        - channel: 3
          max_frame_size: 2000
        - channel: 4
          max_frame_size: 9000
        - channel: 5
          max_frame_size: 1500
        - channel: 6
          max_frame_size: 2000


# ***************************************************
# *** Ensure python jmespath package is installed ***
# ***************************************************
- name: Ensure that jmespath is installed
  pip:
    name: jmespath
    state: enabled
  register: jmespath
- fail:
    msg: "Restart playbook, the jmespath package was installed and is need for the playbook's execution."
  when: jmespath.changed


# ************************************
# *** Set local playbook test data ***
# ************************************
- name: set credentials
  set_fact:
    credentials: *creds
- name: set array
  set_fact:
    array: *array

- name: Show some debug information
  debug:
    msg: "Using user={{ credentials.api_username }} on server={{ credentials.api_url }}."


# *****************************************
# *** Disable all controller A channels ***
# *****************************************
- name: Disable all controller A ports
  netapp_e_iscsi_interface:
    <<: *creds
    controller: "A"
    channel: "{{ item.channel }}"
    state: disabled
  loop: "{{ lookup('list', array.A) }}"

# Delay to give time for the asynchronous symbol call has complete
- pause:
    seconds: 30

# Request all controller's iscsi host interface information
- name: Collect iscsi port information
  uri:
    url: "{{ xpath_filter_url }}?query=controller/hostInterfaces//iscsi"
    user: "{{ credentials.api_username }}"
    password: "{{ credentials.api_password }}"
    body_format: json
    validate_certs: no
  register: result
  vars:
    xpath_filter_url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/graph/xpath-filter"

# Extract controller A's port information from the iscsi host interfaces list
#   Note: min filter is used because there are only two controller ids and the smaller corresponds with controller A
- name: Get controller A's controllerId
  set_fact:
    controller_a_id: "{{ result | json_query('json[*].controllerId') | min }}"

# Collect all port information associated with controller A
- name: Get controller A's port information
  set_fact:
    controller_a: "{{ result | json_query(controller_a_query) }}"
  vars:
    controller_a_query: "json[?controllerId=='{{ controller_a_id }}']"

# Confirm controller A's ports are disabled
- name: Verify all controller A ports are disabled
  assert:
    that: "{{ item.ipv4Enabled == false }}"
    msg: "Controller A, channel {{ item.channel }} is not disabled"
  loop: "{{ controller_a }}"


# *****************************************
# *** Disable all controller B channels ***
# *****************************************
- name: Disable all controller B ports
  netapp_e_iscsi_interface:
    <<: *creds
    controller: "B"
    channel: "{{ item.channel }}"
    state: disabled
  loop: "{{ lookup('list', array.B) }}"

# Delay to give time for the asynchronous symbol call has complete
- pause:
    seconds: 30

# Request all controller's iscsi host interface information
- name: Collect iscsi port information
  uri:
    url: "{{ xpath_filter_url }}?query=controller/hostInterfaces//iscsi"
    user: "{{ credentials.api_username }}"
    password: "{{ credentials.api_password }}"
    body_format: json
    validate_certs: no
  register: result
  vars:
    xpath_filter_url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/graph/xpath-filter"

# Extract controller B's port information from the iscsi host interfaces list
#   Note: min filter is used because there are only two controller ids and the smaller corresponds with controller B
- name: Get controller B's controllerId
  set_fact:
    controller_b_id: "{{ result | json_query('json[*].controllerId') | max }}"

# Collect all port information associated with controller B
- name: Get controller B's port information
  set_fact:
    controller_b: "{{ result | json_query(controller_b_query) }}"
  vars:
    controller_b_query: "json[?controllerId=='{{ controller_b_id }}']"

# Confirm controller B's ports are disabled
- name: Verify all controller B ports are disabled
  assert:
    that: "{{ item.ipv4Enabled == false }}"
    msg: "Controller B, channel {{ item.channel }} is not disabled"
  loop: "{{ controller_b }}"


# *****************************************************
# *** Configure all controller A's ports statically ***
# *****************************************************
- name: Configure controller A's port to use a static configuration method
  netapp_e_iscsi_interface:
    <<: *creds
    controller: "A"
    channel: "{{ item.channel }}"
    state: enabled
    config_method: static
    address: "{{ array.gateway.split('.')[:3] | join('.') }}.{{ item.channel }}"
    subnet_mask: "{{ array.subnet }}"
    gateway: "{{ array.gateway }}"
    max_frame_size: "{{ item.max_frame_size }}"
  loop: "{{ lookup('list', array.A) }}"

# Delay to give time for the asynchronous symbol call has complete
- pause:
    seconds: 30

# Request a list of iscsi host interfaces
- name: Collect array information
  uri:
    url: "{{ xpath_filter }}?query=controller/hostInterfaces//iscsi"
    user: "{{ credentials.api_username }}"
    password: "{{ credentials.api_password }}"
    body_format: json
    validate_certs: no
  register: result
  vars:
    xpath_filter: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/graph/xpath-filter"

# Extract controller A's port information from the iscsi host interfaces list
#   Note: min filter is used because there are only two controller ids and the smaller corresponds with controller A
- name: Get controller A's controllerId
  set_fact:
    controller_a_id: "{{ result | json_query('json[*].controllerId') | min }}"

# Compile any iscsi port information associated with controller A
- name: Get controller A's port information
  set_fact:
    controller_a: "{{ result | json_query(controller_a_query) }}"
  vars:
    controller_a_query: "json[?controllerId=='{{ controller_a_id }}']"

# Confirm that controller A ports are statically defined with the expected MTU, gateway, subnet and ipv4 address
- name: Verify expected controller A's port configuration
  assert:
    that: "{{ item[0].channel != item[1].channel or
              ( item[0].ipv4Data.ipv4AddressConfigMethod == 'configStatic' and
                item[0].interfaceData.ethernetData.maximumFramePayloadSize == item[1].max_frame_size and
                item[0].ipv4Data.ipv4AddressData.ipv4GatewayAddress == array.gateway and
                item[0].ipv4Data.ipv4AddressData.ipv4SubnetMask == array.subnet and
                item[0].ipv4Data.ipv4AddressData.ipv4Address == partial_address + item[1].channel | string ) }}"
    msg: "Failed to configure controller A, channel {{ item[0].channel }}"
  loop: "{{ query('nested', lookup('list', controller_a), lookup('list', array.A) ) }}"
  vars:
    partial_address: "{{ array.gateway.split('.')[:3] | join('.') + '.' }}"


# *******************************************************************************************
# *** Configure controller B's channels for dhcp and specific frame maximum payload sizes ***
# *******************************************************************************************
- name: Configure controller B's ports to use dhcp with different MTU
  netapp_e_iscsi_interface:
    <<: *creds
    controller: "B"
    channel: "{{ item.channel }}"
    state: enabled
    config_method: dhcp
    max_frame_size: "{{ item.max_frame_size }}"
  loop: "{{ lookup('list', array.B) }}"

# Delay to give time for the asynchronous symbol call has complete
- pause:
    seconds: 30

# request a list of iscsi host interfaces
- name: Collect array information
  uri:
    url: "{{ xpath_filter_url }}?query=controller/hostInterfaces//iscsi"
    user: "{{ credentials.api_username }}"
    password: "{{ credentials.api_password }}"
    body_format: json
    validate_certs: no
  register: result
  vars:
    xpath_filter_url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/graph/xpath-filter"

# Extract controller B's port information from the iscsi host interfaces list
#   Note: max filter is used because there are only two controller ids and the larger corresponds with controller B
- name: Get controller B's controllerId
  set_fact:
    controller_b_id: "{{ result | json_query('json[*].controllerId') | max }}"
- name: Get controller B port information list
  set_fact:
    controller_b: "{{ result | json_query(controller_b_query) }}"
  vars:
    controller_b_query: "json[?controllerId=='{{ controller_b_id }}']"

# Using a nested loop of array information and expected information, verify that each channel has the appropriate max
#   frame payload size and is configured for dhcp
- name: Verify expected controller B's port configuration
  assert:
    that: "{{ item[0].channel != item[1].channel or
              ( item[0].ipv4Data.ipv4AddressConfigMethod == 'configDhcp' and
                item[0].interfaceData.ethernetData.maximumFramePayloadSize == item[1].max_frame_size ) }}"
    msg: >
            Failed to configure controller channel {{ item[0].channel }} for dhcp
            and/or maximum frame size of {{ item[1].max_frame_size }}!
  loop: "{{ query('nested', lookup('list', controller_b), lookup('list', array.B)) }}"


# *******************************************************************************************
# *** Configure controller A's channels for dhcp and specific frame maximum payload sizes ***
# *******************************************************************************************
- name: Configure controller A's ports to use dhcp with different MTU
  netapp_e_iscsi_interface:
    <<: *creds
    controller: "A"
    channel: "{{ item.channel }}"
    state: enabled
    config_method: dhcp
    max_frame_size: "{{ item.max_frame_size }}"
  loop: "{{ lookup('list', array.A) }}"

# Delay to give time for the asynchronous symbol call has complete
- pause:
    seconds: 30

# Request a list of iscsi host interfaces
- name: Collect array information
  uri:
    url: "{{ xpath_filter_url }}?query=controller/hostInterfaces//iscsi"
    user: "{{ credentials.api_username }}"
    password: "{{ credentials.api_password }}"
    body_format: json
    validate_certs: no
  register: result
  vars:
    xpath_filter_url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/graph/xpath-filter"

# Extract controller A's port information from the iscsi host interfaces list
#   Note: min filter is used because there are only two controller ids and the larger corresponds with controller A
- name: Get controller A's controllerId
  set_fact:
    controller_a_id: "{{ result | json_query('json[*].controllerId') | min }}"
- name: Get controller A port information list
  set_fact:
    controller_a: "{{ result | json_query(controller_a_query) }}"
  vars:
    controller_a_query: "json[?controllerId=='{{ controller_a_id }}']"

# Using a nested loop of array information and expected information, verify that each channel has the appropriate max
#   frame payload size and is configured for dhcp
- name: Verify expected controller A's port configuration
  assert:
    that: "{{ item[0].channel != item[1].channel or
              ( item[0].ipv4Data.ipv4AddressConfigMethod == 'configDhcp' and
                item[0].interfaceData.ethernetData.maximumFramePayloadSize == item[1].max_frame_size ) }}"
    msg: >
            Failed to configure controller channel {{ item[0].channel }} for dhcp
            and/or maximum frame size of {{ item[1].max_frame_size }}!
  loop: "{{ query('nested', lookup('list', controller_a), lookup('list', array.A)) }}"


# *****************************************************
# *** Configure all controller B's ports statically ***
# *****************************************************
- name: Configure controller B's ports to use a static configuration method
  netapp_e_iscsi_interface:
    <<: *creds
    controller: "B"
    channel: "{{ item.channel }}"
    state: enabled
    config_method: static
    address: "{{ array.gateway.split('.')[:3] | join('.') }}.{{ item.channel }}"
    subnet_mask: "{{ array.subnet }}"
    gateway: "{{ array.gateway }}"
    max_frame_size: "{{ item.max_frame_size }}"
  loop: "{{ lookup('list', array.B) }}"

# Delay to give time for the asynchronous symbol call has complete
- pause:
    seconds: 30

# request a list of iscsi host interfaces
- name: Collect array information
  uri:
    url: "{{ xpath_filter }}?query=controller/hostInterfaces//iscsi"
    user: "{{ credentials.api_username }}"
    password: "{{ credentials.api_password }}"
    body_format: json
    validate_certs: no
  register: result
  vars:
    xpath_filter: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/graph/xpath-filter"

# Extract controller B's port information from the iscsi host interfaces list
#   Note: min filter is used because there are only two controller ids and the smaller corresponds with controller B
- name: Get controller B's controllerId
  set_fact:
    controller_b_id: "{{ result | json_query('json[*].controllerId') | max }}"

# Compile any iscsi port information associated with controller B
- name: Get controller B's port information
  set_fact:
    controller_b: "{{ result | json_query(controller_b_query) }}"
  vars:
    controller_b_query: "json[?controllerId=='{{ controller_b_id }}']"

# Confirm that controller B ports are statically defined with the expected MTU, gateway, subnet and ipv4 address
- name: Verify expected controller B's port configuration
  assert:
    that: "{{ item[0].channel != item[1].channel or
              ( item[0].ipv4Data.ipv4AddressConfigMethod == 'configStatic' and
                item[0].interfaceData.ethernetData.maximumFramePayloadSize == item[1].max_frame_size and
                item[0].ipv4Data.ipv4AddressData.ipv4GatewayAddress == array.gateway and
                item[0].ipv4Data.ipv4AddressData.ipv4SubnetMask == array.subnet and
                item[0].ipv4Data.ipv4AddressData.ipv4Address == partial_address + item[1].channel | string ) }}"
    msg: "Failed to configure controller B, channel {{ item[0].channel }}"
  loop: "{{ query('nested', lookup('list', controller_b), lookup('list', array.B) ) }}"
  vars:
    partial_address: "{{ array.gateway.split('.')[:3] | join('.') + '.' }}"


# **************************************
# *** Disable all controller B ports ***
# **************************************
- name: Disable all controller B's ports
  netapp_e_iscsi_interface:
    <<: *creds
    controller: "B"
    channel: "{{ item.channel }}"
    state: disabled
  loop: "{{ lookup('list', array.B) }}"

# Delay to give time for the asynchronous symbol call has complete
- pause:
    seconds: 30

# Request controller iscsi host interface information
- name: Collect iscsi port information
  uri:
    url: "{{ xpath_filter_url }}?query=controller/hostInterfaces//iscsi"
    user: "{{ credentials.api_username }}"
    password: "{{ credentials.api_password }}"
    body_format: json
    validate_certs: no
  register: result
  vars:
    xpath_filter_url: "{{ credentials.api_url }}/storage-systems/{{ credentials.ssid }}/graph/xpath-filter"

# Extract controller A's port information from the iscsi host interfaces list
#   Note: min filter is used because there are only two controller ids and the smaller corresponds with controller B
- name: Get controller B's controllerId
  set_fact:
    controller_b_id: "{{ result | json_query('json[*].controllerId') | max }}"

# Compile any iscsi port information associated with controller B
- name: Get controller B's port information
  set_fact:
    controller_b: "{{ result | json_query(controller_b_query) }}"
  vars:
    controller_b_query: "json[?controllerId=='{{ controller_b_id }}']"

# Confirm that all of controller B's ports are disabled
- name: Verify all controller B ports are disabled
  assert:
    that: "{{ item.ipv4Enabled == false }}"
    msg: "Controller B, channel {{ item.channel }} is not disabled"
  loop: "{{ controller_b }}"
