Alex | 0989ecf | 2022-03-29 13:43:21 -0500 | [diff] [blame] | 1 | # Author: Alex Savatieiev (osavatieiev@mirantis.com; a.savex@gmail.com) |
| 2 | # Copyright 2019-2022 Mirantis, Inc. |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 3 | import json |
| 4 | import os |
| 5 | import platform |
| 6 | import sys |
| 7 | from copy import deepcopy |
| 8 | from multiprocessing.dummy import Pool |
| 9 | from subprocess import PIPE, Popen |
| 10 | |
| 11 | _os = platform.system() |
| 12 | _packets = {} |
| 13 | |
| 14 | _defaults = { |
| 15 | "ip": None, |
| 16 | "size": 0, |
| 17 | "fragmentation": False, |
| 18 | "count": 1, |
| 19 | "exit_timeout": 1, |
| 20 | "response_timeout": 1, |
| 21 | "numeric": True |
| 22 | } |
| 23 | |
| 24 | _help_message = \ |
| 25 | "Invalid parameters. Use: 'ping.py [PKT_SIZE] <IP>' or 'ping.py n.json'\n" |
| 26 | |
| 27 | _template = { |
| 28 | "returncode": -1, |
| 29 | "stdout": "", |
| 30 | "stderr": "" |
| 31 | } |
| 32 | |
| 33 | |
| 34 | def shell(command): |
| 35 | _ps = Popen( |
| 36 | " ".join(command), |
| 37 | shell=True, |
| 38 | stdout=PIPE, |
| 39 | stderr=PIPE |
| 40 | ) |
| 41 | _out = _ps.communicate() |
| 42 | _err = _out[1] |
| 43 | _out = _out[0] |
| 44 | return _ps.returncode, _out, _err |
| 45 | |
| 46 | |
| 47 | def write_help(): |
| 48 | _t = deepcopy(_template) |
| 49 | _t["returncode"] = 1 |
| 50 | _t["stderr"] = _help_message |
| 51 | write_outcome(_t) |
| 52 | |
| 53 | |
| 54 | def write_outcome(_params): |
| 55 | sys.stdout.write(json.dumps(_params)) |
| 56 | |
| 57 | |
| 58 | def do_ping(_params): |
| 59 | # Load params and defaults |
| 60 | _d = deepcopy(_defaults) |
| 61 | for key in _params: |
| 62 | _d[key] = _params[key] |
| 63 | |
| 64 | # Build cmd |
| 65 | _cmd = ["ping"] |
| 66 | if _os == "Darwin": |
| 67 | if not _d["fragmentation"]: |
| 68 | _cmd.append("-D") |
| 69 | if _d["exit_timeout"]: |
| 70 | _cmd += ["-t", str(_d["exit_timeout"])] |
| 71 | elif _os == "Linux": |
| 72 | if not _d["fragmentation"]: |
| 73 | _cmd += ["-M", "do"] |
| 74 | if _d["exit_timeout"]: |
| 75 | _cmd += ["-w", str(_d["exit_timeout"])] |
| 76 | else: |
| 77 | # Windows or other OS |
| 78 | _t = deepcopy(_template) |
| 79 | _t["returncode"] = 1 |
| 80 | _t["stderr"] = \ |
| 81 | "ping.py: '{}' support not implemented".format(_os) |
| 82 | write_outcome(_t) |
| 83 | sys.exit(1) |
| 84 | |
| 85 | if _d["size"]: |
| 86 | _cmd += ["-s", str(_d["size"])] |
| 87 | if _d["count"]: |
| 88 | _cmd += ["-c", str(_d["count"])] |
| 89 | if _d["numeric"]: |
| 90 | _cmd.append("-n") |
| 91 | if _d["response_timeout"]: |
| 92 | _cmd += ["-W", str(_d["response_timeout"])] |
| 93 | |
| 94 | _cmd.append(_d["ip"]) |
| 95 | # sys.stdout.write("# {}\n".format(" ".join(_cmd))) |
| 96 | _r, _out, _err = shell(_cmd) |
| 97 | |
| 98 | # TODO: parse results, latency, etc |
| 99 | _t = deepcopy(_template) |
| 100 | _t["returncode"] = _r |
| 101 | _t["stdout"] = _out |
| 102 | _t["stderr"] = _err |
| 103 | _params.update(_t) |
| 104 | return _params |
| 105 | |
| 106 | |
| 107 | def load_targets(filename): |
| 108 | # load target ips from json |
| 109 | with open(filename, "r") as f: |
| 110 | j = json.load(f) |
| 111 | |
| 112 | return j |
| 113 | |
| 114 | |
| 115 | if len(sys.argv) < 2: |
| 116 | # no params given |
| 117 | write_help() |
| 118 | elif len(sys.argv) < 3: |
| 119 | # one param: decide if it json file or IP |
| 120 | _arg = sys.argv[1] |
| 121 | if os.path.isfile(_arg): |
| 122 | _packets = load_targets(_arg) |
| 123 | # up to 15 packets at once |
| 124 | pool = Pool(15) |
| 125 | # prepare threaded map |
| 126 | _param_map = [] |
Alex | 3bc95f6 | 2020-03-05 17:00:04 -0600 | [diff] [blame] | 127 | for _node, _data in _packets.items(): |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 128 | if isinstance(_data, list): |
| 129 | for target in _data: |
| 130 | _param_map.append(target) |
| 131 | elif isinstance(_data, dict): |
| 132 | _param_map.append(_data) |
| 133 | else: |
| 134 | _t = deepcopy(_template) |
| 135 | _t["returncode"] = 1 |
| 136 | _t["stderr"] = \ |
| 137 | "TypeError: 'list' or 'dict' expected. " \ |
| 138 | "Got '{}': '{}'".format( |
| 139 | type(_data).__name__, |
| 140 | _data |
| 141 | ) |
| 142 | _packets[_node] = _t |
| 143 | _threaded_out = pool.map(do_ping, _param_map) |
| 144 | for _out in _threaded_out: |
Alex | 7b0ee9a | 2021-09-21 17:16:17 -0500 | [diff] [blame] | 145 | if sys.version_info[0] > 2: |
| 146 | # in python3 stdout will be a bytes object |
| 147 | _out['stdout'] = _out['stdout'].decode('utf-8') |
| 148 | _out['stderr'] = _out['stderr'].decode('utf-8') |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 149 | if isinstance(_packets[_out["tgt_host"]], dict): |
| 150 | _packets[_out["tgt_host"]] = _out |
| 151 | elif isinstance(_packets[_out["tgt_host"]], list): |
| 152 | _packets[_out["tgt_host"]][_out["ip_index"]] = _out |
Alex | 7b0ee9a | 2021-09-21 17:16:17 -0500 | [diff] [blame] | 153 | # dump |
Alex | e0c5b9e | 2019-04-23 18:51:23 -0500 | [diff] [blame] | 154 | sys.stdout.write(json.dumps(_packets)) |
| 155 | else: |
| 156 | # IP given |
| 157 | _ip = sys.argv[1] |
| 158 | write_outcome(do_ping(_ip)) |
| 159 | elif len(sys.argv) < 4: |
| 160 | # two params: size and IP |
| 161 | _s = sys.argv[1] |
| 162 | _ip = sys.argv[2] |
| 163 | write_outcome(do_ping(_ip, size=_s)) |
| 164 | else: |
| 165 | # too many params given |
| 166 | write_help() |