1#!/usr/bin/python 2# 3# This file is part of Ansible 4# 5# Ansible is free software: you can redistribute it and/or modify 6# it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# Ansible is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with Ansible. If not, see <http://www.gnu.org/licenses/>. 17# 18 19ANSIBLE_METADATA = {'metadata_version': '1.1', 20 'status': ['preview'], 21 'supported_by': 'community'} 22 23 24DOCUMENTATION = ''' 25--- 26module: nxos_ping 27extends_documentation_fragment: nxos 28version_added: "2.1" 29short_description: Tests reachability using ping from Nexus switch. 30description: 31 - Tests reachability using ping from switch to a remote destination. 32 - For a general purpose network module, see the M(net_ping) module. 33 - For Windows targets, use the M(win_ping) module instead. 34 - For targets running Python, use the M(ping) module instead. 35author: 36 - Jason Edelman (@jedelman8) 37 - Gabriele Gerbino (@GGabriele) 38options: 39 dest: 40 description: 41 - IP address or hostname (resolvable by switch) of remote node. 42 required: true 43 count: 44 description: 45 - Number of packets to send. 46 default: 5 47 source: 48 description: 49 - Source IP Address or hostname (resolvable by switch) 50 vrf: 51 description: 52 - Outgoing VRF. 53 state: 54 description: 55 - Determines if the expected result is success or fail. 56 choices: [ absent, present ] 57 default: present 58notes: 59 - For a general purpose network module, see the M(net_ping) module. 60 - For Windows targets, use the M(win_ping) module instead. 61 - For targets running Python, use the M(ping) module instead. 62''' 63 64EXAMPLES = ''' 65- name: Test reachability to 8.8.8.8 using mgmt vrf 66 nxos_ping: 67 dest: 8.8.8.8 68 vrf: management 69 host: 68.170.147.165 70 71- name: Test reachability to a few different public IPs using mgmt vrf 72 nxos_ping: 73 dest: nxos_ping 74 vrf: management 75 host: 68.170.147.165 76 with_items: 77 - 8.8.8.8 78 - 4.4.4.4 79 - 198.6.1.4 80''' 81 82RETURN = ''' 83commands: 84 description: Show the command sent 85 returned: always 86 type: list 87 sample: ["ping 8.8.8.8 count 2 vrf management"] 88rtt: 89 description: Show RTT stats 90 returned: always 91 type: dict 92 sample: {"avg": 6.264, "max": 6.564, "min": 5.978} 93packets_rx: 94 description: Packets successfully received 95 returned: always 96 type: int 97 sample: 2 98packets_tx: 99 description: Packets successfully transmitted 100 returned: always 101 type: int 102 sample: 2 103packet_loss: 104 description: Percentage of packets lost 105 returned: always 106 type: str 107 sample: "0.00%" 108''' 109from ansible.module_utils.network.nxos.nxos import run_commands 110from ansible.module_utils.network.nxos.nxos import nxos_argument_spec, check_args 111from ansible.module_utils.basic import AnsibleModule 112 113 114def get_summary(results_list, reference_point): 115 summary_string = results_list[reference_point + 1] 116 summary_list = summary_string.split(',') 117 118 summary = dict( 119 packets_tx=int(summary_list[0].split('packets')[0].strip()), 120 packets_rx=int(summary_list[1].split('packets')[0].strip()), 121 packet_loss=summary_list[2].split('packet')[0].strip(), 122 ) 123 124 if 'bytes from' not in results_list[reference_point - 2]: 125 ping_pass = False 126 else: 127 ping_pass = True 128 129 return summary, ping_pass 130 131 132def get_rtt(results_list, packet_loss, location): 133 rtt = dict(min=None, avg=None, max=None) 134 135 if packet_loss != '100.00%': 136 rtt_string = results_list[location] 137 base = rtt_string.split('=')[1] 138 rtt_list = base.split('/') 139 140 rtt['min'] = float(rtt_list[0].lstrip()) 141 rtt['avg'] = float(rtt_list[1]) 142 rtt['max'] = float(rtt_list[2][:-3]) 143 144 return rtt 145 146 147def get_statistics_summary_line(response_as_list): 148 for each in response_as_list: 149 if '---' in each: 150 index = response_as_list.index(each) 151 return index 152 153 154def get_ping_results(command, module): 155 cmd = {'command': command, 'output': 'text'} 156 ping = run_commands(module, [cmd])[0] 157 158 if not ping: 159 module.fail_json(msg="An unexpected error occurred. Check all params.", 160 command=command, destination=module.params['dest'], 161 vrf=module.params['vrf'], 162 source=module.params['source']) 163 164 elif "can't bind to address" in ping: 165 module.fail_json(msg="Can't bind to source address.", command=command) 166 elif "bad context" in ping: 167 module.fail_json(msg="Wrong VRF name inserted.", command=command, 168 vrf=module.params['vrf']) 169 else: 170 splitted_ping = ping.split('\n') 171 reference_point = get_statistics_summary_line(splitted_ping) 172 summary, ping_pass = get_summary(splitted_ping, reference_point) 173 rtt = get_rtt(splitted_ping, summary['packet_loss'], reference_point + 2) 174 175 return (summary, rtt, ping_pass) 176 177 178def main(): 179 argument_spec = dict( 180 dest=dict(required=True), 181 count=dict(required=False, default=5, type='int'), 182 vrf=dict(required=False), 183 source=dict(required=False), 184 state=dict(required=False, choices=['present', 'absent'], default='present'), 185 ) 186 187 argument_spec.update(nxos_argument_spec) 188 189 module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) 190 191 warnings = list() 192 check_args(module, warnings) 193 194 destination = module.params['dest'] 195 count = module.params['count'] 196 state = module.params['state'] 197 198 ping_command = 'ping {0}'.format(destination) 199 for command in ['count', 'source', 'vrf']: 200 arg = module.params[command] 201 if arg: 202 ping_command += ' {0} {1}'.format(command, arg) 203 204 summary, rtt, ping_pass = get_ping_results(ping_command, module) 205 206 results = summary 207 results['rtt'] = rtt 208 results['commands'] = [ping_command] 209 210 if ping_pass and state == 'absent': 211 module.fail_json(msg="Ping succeeded unexpectedly") 212 elif not ping_pass and state == 'present': 213 module.fail_json(msg="Ping failed unexpectedly") 214 215 module.exit_json(**results) 216 217 218if __name__ == '__main__': 219 main() 220