#!/usr/bin/python
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
#
from __future__ import absolute_import, division, print_function


__metaclass__ = type


DOCUMENTATION = """
module: nxos_ping
extends_documentation_fragment:
- cisco.nxos.nxos
short_description: Tests reachability using ping from Nexus switch.
description:
- Tests reachability using ping from switch to a remote destination.
- For a general purpose network module, see the M(ansible.netcommon.net_ping) module.
- For Windows targets, use the M(ansible.windows.win_ping) module instead.
- For targets running Python, use the M(ansible.builtin.ping) module instead.
version_added: 1.0.0
author:
- Jason Edelman (@jedelman8)
- Gabriele Gerbino (@GGabriele)
options:
  dest:
    description:
    - IP address or hostname (resolvable by switch) of remote node.
    required: true
    type: str
  count:
    description:
    - Number of packets to send.
    default: 5
    type: int
  source:
    description:
    - Source IP Address or hostname (resolvable by switch)
    type: str
  vrf:
    description:
    - Outgoing VRF.
    type: str
  df_bit:
    description:
    - Set the DF bit.
    default: false
    type: bool
  size:
    description:
    - Size of packets to send.
    type: int
  state:
    description:
    - Determines if the expected result is success or fail.
    choices:
    - absent
    - present
    default: present
    type: str
notes:
- Unsupported for Cisco MDS
- For a general purpose network module, see the M(ansible.netcommon.net_ping) module.
- For Windows targets, use the M(ansible.windows.win_ping) module instead.
- For targets running Python, use the M(ansible.builtin.ping) module instead.
"""

EXAMPLES = """
- name: Test reachability to 8.8.8.8 using mgmt vrf
  cisco.nxos.nxos_ping:
    dest: 8.8.8.8
    vrf: management
    host: 68.170.147.165

- name: Test reachability to a few different public IPs using mgmt vrf
  cisco.nxos.nxos_ping:
    dest: "{{ item }}"
    vrf: management
    host: 68.170.147.165
  with_items:
    - 8.8.8.8
    - 4.4.4.4
    - 198.6.1.4

- name: Test reachability to 8.8.8.8 using mgmt vrf, size and df-bit
  cisco.nxos.nxos_ping:
    dest: 8.8.8.8
    df_bit: true
    size: 1400
    vrf: management
"""

RETURN = """
commands:
    description: Show the command sent
    returned: always
    type: list
    sample: ["ping 8.8.8.8 count 2 vrf management"]
rtt:
    description: Show RTT stats
    returned: always
    type: dict
    sample: {"avg": 6.264, "max": 6.564, "min": 5.978}
packets_rx:
    description: Packets successfully received
    returned: always
    type: int
    sample: 2
packets_tx:
    description: Packets successfully transmitted
    returned: always
    type: int
    sample: 2
packet_loss:
    description: Percentage of packets lost
    returned: always
    type: str
    sample: "0.00%"
"""
from ansible.module_utils.basic import AnsibleModule

from ansible_collections.cisco.nxos.plugins.module_utils.network.nxos.nxos import run_commands


def get_summary(results_list, reference_point):
    summary_string = results_list[reference_point + 1]
    summary_list = summary_string.split(",")

    summary = dict(
        packets_tx=int(summary_list[0].split("packets")[0].strip()),
        packets_rx=int(summary_list[1].split("packets")[0].strip()),
        packet_loss=summary_list[2].split("packet")[0].strip(),
    )

    if "bytes from" not in results_list[reference_point - 2]:
        ping_pass = False
    else:
        ping_pass = True

    return summary, ping_pass


def get_rtt(results_list, packet_loss, location):
    rtt = dict(min=None, avg=None, max=None)

    if packet_loss != "100.00%":
        rtt_string = results_list[location]
        base = rtt_string.split("=")[1]
        rtt_list = base.split("/")

        rtt["min"] = float(rtt_list[0].lstrip())
        rtt["avg"] = float(rtt_list[1])
        rtt["max"] = float(rtt_list[2][:-3])

    return rtt


def get_statistics_summary_line(response_as_list):
    for each in response_as_list:
        if "---" in each:
            index = response_as_list.index(each)
    return index


def get_ping_results(command, module):
    cmd = {"command": command, "output": "text"}
    ping = run_commands(module, [cmd])[0]

    if not ping:
        module.fail_json(
            msg="An unexpected error occurred. Check all params.",
            command=command,
            destination=module.params["dest"],
            vrf=module.params["vrf"],
            source=module.params["source"],
            size=module.params["size"],
            df_bit=module.params["df_bit"],
        )

    elif "can't bind to address" in ping:
        module.fail_json(msg="Can't bind to source address.", command=command)
    elif "bad context" in ping:
        module.fail_json(
            msg="Wrong VRF name inserted.",
            command=command,
            vrf=module.params["vrf"],
        )
    else:
        splitted_ping = ping.split("\n")
        reference_point = get_statistics_summary_line(splitted_ping)
        summary, ping_pass = get_summary(splitted_ping, reference_point)
        rtt = get_rtt(splitted_ping, summary["packet_loss"], reference_point + 2)

    return (summary, rtt, ping_pass)


def main():
    argument_spec = dict(
        dest=dict(required=True),
        count=dict(required=False, default=5, type="int"),
        vrf=dict(required=False),
        source=dict(required=False),
        size=dict(required=False, type="int"),
        df_bit=dict(required=False, default=False, type="bool"),
        state=dict(required=False, choices=["present", "absent"], default="present"),
    )

    module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)

    destination = module.params["dest"]
    state = module.params["state"]
    size = module.params["size"]
    df_bit = module.params["df_bit"]

    ping_command = "ping {0}".format(destination)
    for command in ["count", "source", "vrf"]:
        arg = module.params[command]
        if arg:
            ping_command += " {0} {1}".format(command, arg)

    if size:
        ping_command += " packet-size {0}".format(size)

    if df_bit:
        ping_command += " df-bit"

    summary, rtt, ping_pass = get_ping_results(ping_command, module)

    results = summary
    results["rtt"] = rtt
    results["commands"] = [ping_command]

    if ping_pass and state == "absent":
        module.fail_json(msg="Ping succeeded unexpectedly")
    elif not ping_pass and state == "present":
        module.fail_json(msg="Ping failed unexpectedly")

    module.exit_json(**results)


if __name__ == "__main__":
    main()
