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