1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00
community.general/plugins/modules/network/icx/icx_ping.py
Ansible Core Team aebc1b03fd Initial commit
2020-03-09 09:11:07 +00:00

269 lines
7.9 KiB
Python

#!/usr/bin/python
# Copyright: Ansible Project
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
from __future__ import absolute_import, division, print_function
__metaclass__ = type
ANSIBLE_METADATA = {'metadata_version': '1.1',
'status': ['preview'],
'supported_by': 'community'}
DOCUMENTATION = '''
---
module: icx_ping
author: "Ruckus Wireless (@Commscope)"
short_description: Tests reachability using ping from Ruckus ICX 7000 series switches
description:
- Tests reachability using ping from switch to a remote destination.
notes:
- Tested against ICX 10.1
options:
count:
description:
- Number of packets to send. Default is 1.
type: int
dest:
description:
- ip-addr | host-name | vrf vrf-name | ipv6 [ ipv6-addr | host-name | vrf vrf-name] (resolvable by switch) of the remote node.
required: true
type: str
timeout:
description:
- Specifies the time, in milliseconds for which the device waits for a reply from the pinged device.
The value can range from 1 to 4294967296. The default is 5000 (5 seconds).
type: int
ttl:
description:
- Specifies the time to live as a maximum number of hops. The value can range from 1 to 255. The default is 64.
type: int
size:
description:
- Specifies the size of the ICMP data portion of the packet, in bytes. This is the payload and does not include the header.
The value can range from 0 to 10000. The default is 16..
type: int
source:
description:
- IP address to be used as the origin of the ping packets.
type: str
vrf:
description:
- Specifies the Virtual Routing and Forwarding (VRF) instance of the device to be pinged.
type: str
state:
description:
- Determines if the expected result is success or fail.
type: str
choices: [ absent, present ]
default: present
'''
EXAMPLES = r'''
- name: Test reachability to 10.10.10.10
icx_ping:
dest: 10.10.10.10
- name: Test reachability to ipv6 address from source with timeout
icx_ping:
dest: ipv6 2001:cdba:0000:0000:0000:0000:3257:9652
source: 10.1.1.1
timeout: 100000
- name: Test reachability to 10.1.1.1 through vrf using 5 packets
icx_ping:
dest: 10.1.1.1
vrf: x.x.x.x
count: 5
- name: Test unreachability to 10.30.30.30
icx_ping:
dest: 10.40.40.40
state: absent
- name: Test reachability to ipv4 with ttl and packet size
icx_ping:
dest: 10.10.10.10
ttl: 20
size: 500
'''
RETURN = '''
commands:
description: Show the command sent.
returned: always
type: list
sample: ["ping 10.40.40.40 count 20 source loopback0", "ping 10.40.40.40"]
packet_loss:
description: Percentage of packets lost.
returned: always
type: str
sample: "0%"
packets_rx:
description: Packets successfully received.
returned: always
type: int
sample: 20
packets_tx:
description: Packets successfully transmitted.
returned: always
type: int
sample: 20
rtt:
description: Show RTT stats.
returned: always
type: dict
sample: {"avg": 2, "max": 8, "min": 1}
'''
from ansible.module_utils._text import to_text
from ansible_collections.community.general.plugins.module_utils.network.icx.icx import run_commands
from ansible.module_utils.basic import AnsibleModule
from ansible.module_utils.connection import Connection, ConnectionError
import re
def build_ping(dest, count=None, source=None, timeout=None, ttl=None, size=None, vrf=None):
"""
Function to build the command to send to the terminal for the switch
to execute. All args come from the module's unique params.
"""
if vrf is not None:
cmd = "ping vrf {0} {1}".format(vrf, dest)
else:
cmd = "ping {0}".format(dest)
if count is not None:
cmd += " count {0}".format(str(count))
if timeout is not None:
cmd += " timeout {0}".format(str(timeout))
if ttl is not None:
cmd += " ttl {0}".format(str(ttl))
if size is not None:
cmd += " size {0}".format(str(size))
if source is not None:
cmd += " source {0}".format(source)
return cmd
def parse_ping(ping_stats):
"""
Function used to parse the statistical information from the ping response.
Example: "Success rate is 100 percent (5/5), round-trip min/avg/max=40/51/55 ms."
Returns the percent of packet loss, received packets, transmitted packets, and RTT dict.
"""
if ping_stats.startswith('Success'):
rate_re = re.compile(r"^\w+\s+\w+\s+\w+\s+(?P<pct>\d+)\s+\w+\s+\((?P<rx>\d+)/(?P<tx>\d+)\)")
rtt_re = re.compile(r".*,\s+\S+\s+\S+=(?P<min>\d+)/(?P<avg>\d+)/(?P<max>\d+)\s+\w+\.+\s*$|.*\s*$")
rate = rate_re.match(ping_stats)
rtt = rtt_re.match(ping_stats)
return rate.group("pct"), rate.group("rx"), rate.group("tx"), rtt.groupdict()
else:
rate_re = re.compile(r"^Sending+\s+(?P<tx>\d+),")
rate = rate_re.match(ping_stats)
rtt = {'avg': 0, 'max': 0, 'min': 0}
return 0, 0, rate.group('tx'), rtt
def validate_results(module, loss, results):
"""
This function is used to validate whether the ping results were unexpected per "state" param.
"""
state = module.params["state"]
if state == "present" and loss == 100:
module.fail_json(msg="Ping failed unexpectedly", **results)
elif state == "absent" and loss < 100:
module.fail_json(msg="Ping succeeded unexpectedly", **results)
def validate_fail(module, responses):
if ("Success" in responses or "No reply" in responses) is False:
module.fail_json(msg=responses)
def validate_parameters(module, timeout, count):
if timeout and not 1 <= int(timeout) <= 4294967294:
module.fail_json(msg="bad value for timeout - valid range (1-4294967294)")
if count and not 1 <= int(count) <= 4294967294:
module.fail_json(msg="bad value for count - valid range (1-4294967294)")
def main():
""" main entry point for module execution
"""
argument_spec = dict(
count=dict(type="int"),
dest=dict(type="str", required=True),
timeout=dict(type="int"),
ttl=dict(type="int"),
size=dict(type="int"),
source=dict(type="str"),
state=dict(type="str", choices=["absent", "present"], default="present"),
vrf=dict(type="str")
)
module = AnsibleModule(argument_spec=argument_spec)
count = module.params["count"]
dest = module.params["dest"]
source = module.params["source"]
timeout = module.params["timeout"]
ttl = module.params["ttl"]
size = module.params["size"]
vrf = module.params["vrf"]
results = {}
warnings = list()
if warnings:
results["warnings"] = warnings
response = ''
try:
validate_parameters(module, timeout, count)
results["commands"] = [build_ping(dest, count, source, timeout, ttl, size, vrf)]
ping_results = run_commands(module, commands=results["commands"])
ping_results_list = ping_results[0].split("\n")
except ConnectionError as exc:
module.fail_json(msg=to_text(exc, errors='surrogate_then_replace'))
validate_fail(module, ping_results[0])
stats = ""
statserror = ''
for line in ping_results_list:
if line.startswith('Sending'):
statserror = line
if line.startswith('Success'):
stats = line
elif line.startswith('No reply'):
stats = statserror
success, rx, tx, rtt = parse_ping(stats)
loss = abs(100 - int(success))
results["packet_loss"] = str(loss) + "%"
results["packets_rx"] = int(rx)
results["packets_tx"] = int(tx)
# Convert rtt values to int
for k, v in rtt.items():
if rtt[k] is not None:
rtt[k] = int(v)
results["rtt"] = rtt
validate_results(module, loss, results)
module.exit_json(**results)
if __name__ == '__main__':
main()