From 1e5d602caecd96c9056ad77f9fc50cf25bf6fdfd Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Mon, 27 Jul 2020 13:48:46 -0700 Subject: [PATCH] working tracepath parser --- docs/parsers/tracepath.md | 120 ++++++++++++++++++++--- jc/parsers/tracepath.py | 196 ++++++++++++++++++++++++++++++++++---- 2 files changed, 286 insertions(+), 30 deletions(-) diff --git a/docs/parsers/tracepath.md b/docs/parsers/tracepath.md index 955eb26a..16bffe7d 100644 --- a/docs/parsers/tracepath.md +++ b/docs/parsers/tracepath.md @@ -11,11 +11,96 @@ Compatibility: Examples: - $ tracepath | jc --tracepath -p - [] + $ tracepath6 3ffe:2400:0:109::2 | jc --tracepath -p + { + "pmtu": 1480, + "forward_hops": 2, + "return_hops": 2, + "hops": [ + { + "ttl": 1, + "guess": true, + "host": "[LOCALHOST]", + "reply_ms": null, + "pmtu": 1500, + "asymmetric_difference": null, + "reached": false + }, + { + "ttl": 1, + "guess": false, + "host": "dust.inr.ac.ru", + "reply_ms": 0.411, + "pmtu": null, + "asymmetric_difference": null, + "reached": false + }, + { + "ttl": 2, + "guess": false, + "host": "dust.inr.ac.ru", + "reply_ms": 0.39, + "pmtu": 1480, + "asymmetric_difference": 1, + "reached": false + }, + { + "ttl": 2, + "guess": false, + "host": "3ffe:2400:0:109::2", + "reply_ms": 463.514, + "pmtu": null, + "asymmetric_difference": null, + "reached": true + } + ] + } + + $ tracepath6 3ffe:2400:0:109::2 | jc --tracepath -p -r + { + "pmtu": "1480", + "forward_hops": "2", + "return_hops": "2", + "hops": [ + { + "ttl": "1", + "guess": true, + "host": "[LOCALHOST]", + "reply_ms": null, + "pmtu": "1500", + "asymmetric_difference": null, + "reached": false + }, + { + "ttl": "1", + "guess": false, + "host": "dust.inr.ac.ru", + "reply_ms": "0.411", + "pmtu": null, + "asymmetric_difference": null, + "reached": false + }, + { + "ttl": "2", + "guess": false, + "host": "dust.inr.ac.ru", + "reply_ms": "0.390", + "pmtu": "1480", + "asymmetric_difference": "1", + "reached": false + }, + { + "ttl": "2", + "guess": false, + "host": "3ffe:2400:0:109::2", + "reply_ms": "463.514", + "pmtu": null, + "asymmetric_difference": null, + "reached": true + } + ] + } - $ tracepath | jc --tracepath -p -r - [] ## info ```python @@ -35,15 +120,24 @@ Parameters: Returns: - List of dictionaries. Structured data with the following schema: + Dictionary. Structured data with the following schema: - [ - { - "tracepath": string, - "bar": boolean, - "baz": integer - } - ] + { + "pmtu": integer, + "forward_hops": integer, + "return_hops": integer, + "hops": [ + { + "ttl": integer, + "guess": boolean, + "host": string, + "reply_ms": float, + "pmtu": integer, + "asymmetric_difference": integer, + "reached": boolean + } + ] + } ## parse ```python @@ -60,5 +154,5 @@ Parameters: Returns: - List of dictionaries. Raw or processed structured data. + Dictionary. Raw or processed structured data. diff --git a/jc/parsers/tracepath.py b/jc/parsers/tracepath.py index 6c08d363..f590033a 100644 --- a/jc/parsers/tracepath.py +++ b/jc/parsers/tracepath.py @@ -10,12 +10,98 @@ Compatibility: Examples: - $ tracepath | jc --tracepath -p - [] + $ tracepath6 3ffe:2400:0:109::2 | jc --tracepath -p + { + "pmtu": 1480, + "forward_hops": 2, + "return_hops": 2, + "hops": [ + { + "ttl": 1, + "guess": true, + "host": "[LOCALHOST]", + "reply_ms": null, + "pmtu": 1500, + "asymmetric_difference": null, + "reached": false + }, + { + "ttl": 1, + "guess": false, + "host": "dust.inr.ac.ru", + "reply_ms": 0.411, + "pmtu": null, + "asymmetric_difference": null, + "reached": false + }, + { + "ttl": 2, + "guess": false, + "host": "dust.inr.ac.ru", + "reply_ms": 0.39, + "pmtu": 1480, + "asymmetric_difference": 1, + "reached": false + }, + { + "ttl": 2, + "guess": false, + "host": "3ffe:2400:0:109::2", + "reply_ms": 463.514, + "pmtu": null, + "asymmetric_difference": null, + "reached": true + } + ] + } + + $ tracepath6 3ffe:2400:0:109::2 | jc --tracepath -p -r + { + "pmtu": "1480", + "forward_hops": "2", + "return_hops": "2", + "hops": [ + { + "ttl": "1", + "guess": true, + "host": "[LOCALHOST]", + "reply_ms": null, + "pmtu": "1500", + "asymmetric_difference": null, + "reached": false + }, + { + "ttl": "1", + "guess": false, + "host": "dust.inr.ac.ru", + "reply_ms": "0.411", + "pmtu": null, + "asymmetric_difference": null, + "reached": false + }, + { + "ttl": "2", + "guess": false, + "host": "dust.inr.ac.ru", + "reply_ms": "0.390", + "pmtu": "1480", + "asymmetric_difference": "1", + "reached": false + }, + { + "ttl": "2", + "guess": false, + "host": "3ffe:2400:0:109::2", + "reply_ms": "463.514", + "pmtu": null, + "asymmetric_difference": null, + "reached": true + } + ] + } - $ tracepath | jc --tracepath -p -r - [] """ +import re import jc.utils @@ -43,18 +129,59 @@ def process(proc_data): Returns: - List of dictionaries. Structured data with the following schema: + Dictionary. Structured data with the following schema: - [ - { - "tracepath": string, - "bar": boolean, - "baz": integer - } - ] + { + "pmtu": integer, + "forward_hops": integer, + "return_hops": integer, + "hops": [ + { + "ttl": integer, + "guess": boolean, + "host": string, + "reply_ms": float, + "pmtu": integer, + "asymmetric_difference": integer, + "reached": boolean + } + ] + } """ + int_list = ['pmtu', 'forward_hops', 'return_hops', 'ttl', 'asymmetric_difference'] + float_list = ['reply_ms'] + + for key, value in proc_data.items(): + for item in int_list: + if key in int_list: + try: + proc_data[key] = int(proc_data[key]) + except (ValueError, TypeError): + proc_data[key] = None + + for item in int_list: + if key in float_list: + try: + proc_data[key] = float(proc_data[key]) + except (ValueError, TypeError): + proc_data[key] = None + + if 'hops' in proc_data: + for entry in proc_data['hops']: + for key in int_list: + if key in entry: + try: + entry[key] = int(entry[key]) + except (ValueError, TypeError): + entry[key] = None + + for key in float_list: + if key in entry: + try: + entry[key] = float(entry[key]) + except (ValueError, TypeError): + entry[key] = None - # rebuild output for added semantic information return proc_data @@ -70,18 +197,53 @@ def parse(data, raw=False, quiet=False): Returns: - List of dictionaries. Raw or processed structured data. + Dictionary. Raw or processed structured data. """ if not quiet: jc.utils.compatibility(__name__, info.compatible) - raw_output = [] + RE_TTL_HOST = re.compile(r'^\s?(?P\d+)(?P\??):\s+(?P(?:no reply|\S+))') # groups: ttl, ttl_guess, host + RE_PMTU = re.compile(r'\spmtu\s(?P[\d]+)') # group: pmtu + RE_REPLY_MS = re.compile(r'\s(?P\d*\.\d*)ms') # group: reply_ms + RE_ASYMM = re.compile(r'\sasymm\s+(?P[\d]+)') # group: asymm + RE_REACHED = re.compile(r'\sreached') + RE_SUMMARY = re.compile(r'\s+Resume:\s+pmtu\s+(?P\d+)(?:\s+hops\s+(?P\d+))?(?:\s+back\s+(?P\d+))?') # groups: pmtu, hops, back + + raw_output = {} if jc.utils.has_data(data): + hops = [] for line in filter(None, data.splitlines()): - # parse the content - pass + # grab hop information + ttl_host = re.search(RE_TTL_HOST, line) + pmtu = re.search(RE_PMTU, line) + reply_ms = re.search(RE_REPLY_MS, line) + asymm = re.search(RE_ASYMM, line) + reached = re.search(RE_REACHED, line) + summary = re.search(RE_SUMMARY, line) + + if ttl_host: + hop = { + 'ttl': ttl_host.group('ttl'), + 'guess': bool(ttl_host.group('ttl_guess')), + 'host': ttl_host.group('host') if ttl_host.group('host') != 'no reply' else None, + 'reply_ms': reply_ms.group('reply_ms') if reply_ms else None, + 'pmtu': pmtu.group('pmtu') if pmtu else None, + 'asymmetric_difference': asymm.group('asymm') if asymm else None, + 'reached': bool(reached) + } + + hops.append(hop) + continue + + elif summary: + raw_output = { + 'pmtu': summary.group('pmtu') if summary.group('pmtu') else None, + 'forward_hops': summary.group('hops') if summary.group('hops') else None, + 'return_hops': summary.group('back') if summary.group('back') else None, + 'hops': hops + } if raw: return raw_output