From 2c3e9ddfe47f56cd7edc2fdf6317f7ecec0b7918 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 14:18:55 -0700 Subject: [PATCH 01/21] add ntpq parser for issue #31 --- changelog.txt | 3 + docgen.sh | 1 + docs/parsers/ntpq.md | 235 +++++++++++++++++++++ jc/cli.py | 1 + jc/parsers/ntpq.py | 291 ++++++++++++++++++++++++++ tests/fixtures/centos-7.7/ntpq-p.out | 6 + tests/fixtures/centos-7.7/ntpq-pn.out | 6 + 7 files changed, 543 insertions(+) create mode 100644 docs/parsers/ntpq.md create mode 100644 jc/parsers/ntpq.py create mode 100644 tests/fixtures/centos-7.7/ntpq-p.out create mode 100644 tests/fixtures/centos-7.7/ntpq-pn.out diff --git a/changelog.txt b/changelog.txt index 7b71da39..744fbef1 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,8 @@ jc changelog +20200xxxx vX.X.X +- Added ntpq command parser + 20200308 v1.8.1 - CLI and history parser optimizations by https://github.com/philippeitis - Refactored magic syntax function and added tests (https://github.com/philippeitis) diff --git a/docgen.sh b/docgen.sh index b37f7cde..a9746502 100755 --- a/docgen.sh +++ b/docgen.sh @@ -31,6 +31,7 @@ pydocmd simple jc.parsers.lsmod+ > ../docs/parsers/lsmod.md pydocmd simple jc.parsers.lsof+ > ../docs/parsers/lsof.md pydocmd simple jc.parsers.mount+ > ../docs/parsers/mount.md pydocmd simple jc.parsers.netstat+ > ../docs/parsers/netstat.md +pydocmd simple jc.parsers.ntpq+ > ../docs/parsers/ntpq.md pydocmd simple jc.parsers.passwd+ > ../docs/parsers/passwd.md pydocmd simple jc.parsers.pip_list+ > ../docs/parsers/pip_list.md pydocmd simple jc.parsers.pip_show+ > ../docs/parsers/pip_show.md diff --git a/docs/parsers/ntpq.md b/docs/parsers/ntpq.md new file mode 100644 index 00000000..d0062147 --- /dev/null +++ b/docs/parsers/ntpq.md @@ -0,0 +1,235 @@ +# jc.parsers.ntpq +jc - JSON CLI output utility ntpq Parser + +Usage: + + specify --ntpq as the first argument if the piped input is coming from ntpq -p + +Compatibility: + + 'linux' + +Examples: + + $ ntpq -p | jc --ntpq -p + [ + { + "selection_state": null, + "remote": "44.190.6.254", + "refid": "127.67.113.92", + "st": 2, + "t": "u", + "when": 1, + "poll": 64, + "reach": 1, + "delay": 23.399, + "offset": -2.805, + "jitter": 2.131 + }, + { + "selection_state": null, + "remote": "ntp.wdc1.us.lea", + "refid": "130.133.1.10", + "st": 2, + "t": "u", + "when": null, + "poll": 64, + "reach": 1, + "delay": 93.053, + "offset": -0.807, + "jitter": 2.839 + }, + { + "selection_state": null, + "remote": "clock.team-cymr", + "refid": "204.9.54.119", + "st": 2, + "t": "u", + "when": null, + "poll": 64, + "reach": 1, + "delay": 70.337, + "offset": -2.909, + "jitter": 2.6 + }, + { + "selection_state": null, + "remote": "mirror1.sjc02.s", + "refid": "216.218.254.202", + "st": 2, + "t": "u", + "when": 2, + "poll": 64, + "reach": 1, + "delay": 29.325, + "offset": 1.044, + "jitter": 4.069 + } + ] + + $ ntpq -pn| jc --ntpq -p + [ + { + "selection_state": "+", + "remote": "44.190.6.254", + "refid": "127.67.113.92", + "st": 2, + "t": "u", + "when": 66, + "poll": 64, + "reach": 377, + "delay": 22.69, + "offset": -0.392, + "jitter": 2.085 + }, + { + "selection_state": "-", + "remote": "108.59.2.24", + "refid": "130.133.1.10", + "st": 2, + "t": "u", + "when": 63, + "poll": 64, + "reach": 377, + "delay": 90.805, + "offset": 2.84, + "jitter": 1.908 + }, + { + "selection_state": "+", + "remote": "38.229.71.1", + "refid": "204.9.54.119", + "st": 2, + "t": "u", + "when": 64, + "poll": 64, + "reach": 377, + "delay": 68.699, + "offset": -0.61, + "jitter": 2.576 + }, + { + "selection_state": "*", + "remote": "72.5.72.15", + "refid": "216.218.254.202", + "st": 2, + "t": "u", + "when": 63, + "poll": 64, + "reach": 377, + "delay": 22.654, + "offset": 0.231, + "jitter": 1.964 + } + ] + + $ ntpq -pn| jc --ntpq -p -r + [ + { + "selection_state": "+", + "remote": "44.190.6.254", + "refid": "127.67.113.92", + "st": "2", + "t": "u", + "when": "66", + "poll": "64", + "reach": "377", + "delay": "22.690", + "offset": "-0.392", + "jitter": "2.085" + }, + { + "selection_state": "-", + "remote": "108.59.2.24", + "refid": "130.133.1.10", + "st": "2", + "t": "u", + "when": "63", + "poll": "64", + "reach": "377", + "delay": "90.805", + "offset": "2.840", + "jitter": "1.908" + }, + { + "selection_state": "+", + "remote": "38.229.71.1", + "refid": "204.9.54.119", + "st": "2", + "t": "u", + "when": "64", + "poll": "64", + "reach": "377", + "delay": "68.699", + "offset": "-0.610", + "jitter": "2.576" + }, + { + "selection_state": "*", + "remote": "72.5.72.15", + "refid": "216.218.254.202", + "st": "2", + "t": "u", + "when": "63", + "poll": "64", + "reach": "377", + "delay": "22.654", + "offset": "0.231", + "jitter": "1.964" + } + ] + +## info +```python +info(self, /, *args, **kwargs) +``` + +## process +```python +process(proc_data) +``` + +Final processing to conform to the schema. + +Parameters: + + proc_data: (dictionary) raw structured data to process + +Returns: + + List of dictionaries. Structured data with the following schema: + + [ + { + "selection_state": string, # space/~ converted to null + "remote": string, + "refid": string, + "st": integer, + "t": string, + "when": integer, # - converted to null + "poll": integer, + "reach": integer, + "delay": float, + "offset": float, + "jitter": float + }, + ] + + +## parse +```python +parse(data, raw=False, quiet=False) +``` + +Main text parsing function + +Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + +Returns: + + List of dictionaries. Raw or processed structured data. + diff --git a/jc/cli.py b/jc/cli.py index a89d4eed..c49f9d04 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -49,6 +49,7 @@ parsers = [ 'lsof', 'mount', 'netstat', + 'ntpq', 'passwd', 'pip-list', 'pip-show', diff --git a/jc/parsers/ntpq.py b/jc/parsers/ntpq.py new file mode 100644 index 00000000..466dbcd9 --- /dev/null +++ b/jc/parsers/ntpq.py @@ -0,0 +1,291 @@ +"""jc - JSON CLI output utility ntpq Parser + +Usage: + + specify --ntpq as the first argument if the piped input is coming from ntpq -p + +Compatibility: + + 'linux' + +Examples: + + $ ntpq -p | jc --ntpq -p + [ + { + "selection_state": null, + "remote": "44.190.6.254", + "refid": "127.67.113.92", + "st": 2, + "t": "u", + "when": 1, + "poll": 64, + "reach": 1, + "delay": 23.399, + "offset": -2.805, + "jitter": 2.131 + }, + { + "selection_state": null, + "remote": "ntp.wdc1.us.lea", + "refid": "130.133.1.10", + "st": 2, + "t": "u", + "when": null, + "poll": 64, + "reach": 1, + "delay": 93.053, + "offset": -0.807, + "jitter": 2.839 + }, + { + "selection_state": null, + "remote": "clock.team-cymr", + "refid": "204.9.54.119", + "st": 2, + "t": "u", + "when": null, + "poll": 64, + "reach": 1, + "delay": 70.337, + "offset": -2.909, + "jitter": 2.6 + }, + { + "selection_state": null, + "remote": "mirror1.sjc02.s", + "refid": "216.218.254.202", + "st": 2, + "t": "u", + "when": 2, + "poll": 64, + "reach": 1, + "delay": 29.325, + "offset": 1.044, + "jitter": 4.069 + } + ] + + $ ntpq -pn| jc --ntpq -p + [ + { + "selection_state": "+", + "remote": "44.190.6.254", + "refid": "127.67.113.92", + "st": 2, + "t": "u", + "when": 66, + "poll": 64, + "reach": 377, + "delay": 22.69, + "offset": -0.392, + "jitter": 2.085 + }, + { + "selection_state": "-", + "remote": "108.59.2.24", + "refid": "130.133.1.10", + "st": 2, + "t": "u", + "when": 63, + "poll": 64, + "reach": 377, + "delay": 90.805, + "offset": 2.84, + "jitter": 1.908 + }, + { + "selection_state": "+", + "remote": "38.229.71.1", + "refid": "204.9.54.119", + "st": 2, + "t": "u", + "when": 64, + "poll": 64, + "reach": 377, + "delay": 68.699, + "offset": -0.61, + "jitter": 2.576 + }, + { + "selection_state": "*", + "remote": "72.5.72.15", + "refid": "216.218.254.202", + "st": 2, + "t": "u", + "when": 63, + "poll": 64, + "reach": 377, + "delay": 22.654, + "offset": 0.231, + "jitter": 1.964 + } + ] + + $ ntpq -pn| jc --ntpq -p -r + [ + { + "selection_state": "+", + "remote": "44.190.6.254", + "refid": "127.67.113.92", + "st": "2", + "t": "u", + "when": "66", + "poll": "64", + "reach": "377", + "delay": "22.690", + "offset": "-0.392", + "jitter": "2.085" + }, + { + "selection_state": "-", + "remote": "108.59.2.24", + "refid": "130.133.1.10", + "st": "2", + "t": "u", + "when": "63", + "poll": "64", + "reach": "377", + "delay": "90.805", + "offset": "2.840", + "jitter": "1.908" + }, + { + "selection_state": "+", + "remote": "38.229.71.1", + "refid": "204.9.54.119", + "st": "2", + "t": "u", + "when": "64", + "poll": "64", + "reach": "377", + "delay": "68.699", + "offset": "-0.610", + "jitter": "2.576" + }, + { + "selection_state": "*", + "remote": "72.5.72.15", + "refid": "216.218.254.202", + "st": "2", + "t": "u", + "when": "63", + "poll": "64", + "reach": "377", + "delay": "22.654", + "offset": "0.231", + "jitter": "1.964" + } + ] +""" +import jc.utils +import jc.parsers.universal + + +class info(): + version = '1.0' + description = 'ntpq command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + # details = 'enter any other details here' + + # compatible options: linux, darwin, cygwin, win32, aix, freebsd + compatible = ['linux'] + magic_commands = ['ntpq'] + + +__version__ = info.version + + +def process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (dictionary) raw structured data to process + + Returns: + + List of dictionaries. Structured data with the following schema: + + [ + { + "selection_state": string, # space/~ converted to null + "remote": string, + "refid": string, + "st": integer, + "t": string, + "when": integer, # - converted to null + "poll": integer, + "reach": integer, + "delay": float, + "offset": float, + "jitter": float + }, + ] + + """ + for entry in proc_data: + + if entry['selection_state'] == '~': + entry['selection_state'] = None + + int_list = ['st', 'when', 'poll', 'reach'] + for key in int_list: + if key in entry: + try: + entry[key] = int(entry[key]) + except (ValueError): + entry[key] = None + + float_list = ['delay', 'offset', 'jitter'] + for key in float_list: + if key in entry: + try: + entry[key] = float(entry[key]) + except (ValueError): + entry[key] = None + + return proc_data + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of dictionaries. Raw or processed structured data. + """ + if not quiet: + jc.utils.compatibility(__name__, info.compatible) + + raw_output = [] + + cleandata = data.splitlines() + cleandata[0] = 'selection_state ' + cleandata[0] + cleandata[0] = cleandata[0].lower() + + # delete header delimiter + del cleandata[1] + + # separate first character with a space for easier parsing + for i, line in enumerate(cleandata[1:]): + if line[0] == ' ': + cleandata[i + 1] = '~ ' + line[1:] + else: + cleandata[i + 1] = line[:1] + ' ' + line[1:] + + raw_output = jc.parsers.universal.simple_table_parse(cleandata) + + if raw: + return raw_output + else: + return process(raw_output) diff --git a/tests/fixtures/centos-7.7/ntpq-p.out b/tests/fixtures/centos-7.7/ntpq-p.out new file mode 100644 index 00000000..5cefcac5 --- /dev/null +++ b/tests/fixtures/centos-7.7/ntpq-p.out @@ -0,0 +1,6 @@ + remote refid st t when poll reach delay offset jitter +============================================================================== + 44.190.6.254 127.67.113.92 2 u 1 64 1 23.399 -2.805 2.131 + ntp.wdc1.us.lea 130.133.1.10 2 u - 64 1 93.053 -0.807 2.839 + clock.team-cymr 204.9.54.119 2 u - 64 1 70.337 -2.909 2.600 + mirror1.sjc02.s 216.218.254.202 2 u 2 64 1 29.325 1.044 4.069 diff --git a/tests/fixtures/centos-7.7/ntpq-pn.out b/tests/fixtures/centos-7.7/ntpq-pn.out new file mode 100644 index 00000000..9bbce795 --- /dev/null +++ b/tests/fixtures/centos-7.7/ntpq-pn.out @@ -0,0 +1,6 @@ + remote refid st t when poll reach delay offset jitter +============================================================================== ++44.190.6.254 127.67.113.92 2 u 66 64 377 22.690 -0.392 2.085 +-108.59.2.24 130.133.1.10 2 u 63 64 377 90.805 2.840 1.908 ++38.229.71.1 204.9.54.119 2 u 64 64 377 68.699 -0.610 2.576 +*72.5.72.15 216.218.254.202 2 u 63 64 377 22.654 0.231 1.964 From 762a886d6fdf5c2ad21d81868d138fb32621ed1c Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 15:17:25 -0700 Subject: [PATCH 02/21] add ntpq tests --- tests/fixtures/centos-7.7/ntpq-p.json | 1 + tests/fixtures/centos-7.7/ntpq-pn.json | 1 + tests/fixtures/ubuntu-18.04/ntpq-p.json | 1 + tests/fixtures/ubuntu-18.04/ntpq-p.out | 21 ++++++++ tests/fixtures/ubuntu-18.04/ntpq-pn.json | 1 + tests/fixtures/ubuntu-18.04/ntpq-pn.out | 21 ++++++++ tests/fixtures/ubuntu-18.04/ntqp-p.out | 21 ++++++++ tests/test_ntpq.py | 66 ++++++++++++++++++++++++ 8 files changed, 133 insertions(+) create mode 100644 tests/fixtures/centos-7.7/ntpq-p.json create mode 100644 tests/fixtures/centos-7.7/ntpq-pn.json create mode 100644 tests/fixtures/ubuntu-18.04/ntpq-p.json create mode 100644 tests/fixtures/ubuntu-18.04/ntpq-p.out create mode 100644 tests/fixtures/ubuntu-18.04/ntpq-pn.json create mode 100644 tests/fixtures/ubuntu-18.04/ntpq-pn.out create mode 100644 tests/fixtures/ubuntu-18.04/ntqp-p.out create mode 100644 tests/test_ntpq.py diff --git a/tests/fixtures/centos-7.7/ntpq-p.json b/tests/fixtures/centos-7.7/ntpq-p.json new file mode 100644 index 00000000..afdb4cca --- /dev/null +++ b/tests/fixtures/centos-7.7/ntpq-p.json @@ -0,0 +1 @@ +[{"selection_state": null, "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, "t": "u", "when": 1, "poll": 64, "reach": 1, "delay": 23.399, "offset": -2.805, "jitter": 2.131}, {"selection_state": null, "remote": "ntp.wdc1.us.lea", "refid": "130.133.1.10", "st": 2, "t": "u", "when": null, "poll": 64, "reach": 1, "delay": 93.053, "offset": -0.807, "jitter": 2.839}, {"selection_state": null, "remote": "clock.team-cymr", "refid": "204.9.54.119", "st": 2, "t": "u", "when": null, "poll": 64, "reach": 1, "delay": 70.337, "offset": -2.909, "jitter": 2.6}, {"selection_state": null, "remote": "mirror1.sjc02.s", "refid": "216.218.254.202", "st": 2, "t": "u", "when": 2, "poll": 64, "reach": 1, "delay": 29.325, "offset": 1.044, "jitter": 4.069}] diff --git a/tests/fixtures/centos-7.7/ntpq-pn.json b/tests/fixtures/centos-7.7/ntpq-pn.json new file mode 100644 index 00000000..7e0a675f --- /dev/null +++ b/tests/fixtures/centos-7.7/ntpq-pn.json @@ -0,0 +1 @@ +[{"selection_state": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, "t": "u", "when": 66, "poll": 64, "reach": 377, "delay": 22.69, "offset": -0.392, "jitter": 2.085}, {"selection_state": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": 2, "t": "u", "when": 63, "poll": 64, "reach": 377, "delay": 90.805, "offset": 2.84, "jitter": 1.908}, {"selection_state": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": 2, "t": "u", "when": 64, "poll": 64, "reach": 377, "delay": 68.699, "offset": -0.61, "jitter": 2.576}, {"selection_state": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": 2, "t": "u", "when": 63, "poll": 64, "reach": 377, "delay": 22.654, "offset": 0.231, "jitter": 1.964}] diff --git a/tests/fixtures/ubuntu-18.04/ntpq-p.json b/tests/fixtures/ubuntu-18.04/ntpq-p.json new file mode 100644 index 00000000..b23f1230 --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/ntpq-p.json @@ -0,0 +1 @@ +[{"selection_state": null, "remote": "0.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "1.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "2.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "3.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "ntp.ubuntu.com", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": "+", "remote": "216.126.233.109", "refid": "128.227.205.3", "st": 2, "t": "u", "when": 24, "poll": 64, "reach": 1, "delay": 95.876, "offset": -65.426, "jitter": 4.502}, {"selection_state": "+", "remote": "time.cloudflare", "refid": "10.4.0.197", "st": 3, "t": "u", "when": 33, "poll": 64, "reach": 1, "delay": 25.239, "offset": -69.809, "jitter": 3.313}, {"selection_state": "+", "remote": "titan.crash-ove", "refid": "139.78.97.128", "st": 2, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 60.008, "offset": -70.991, "jitter": 2.884}, {"selection_state": "*", "remote": "clock.nyc.he.ne", "refid": ".CDMA.", "st": 1, "t": "u", "when": 31, "poll": 64, "reach": 1, "delay": 90.113, "offset": -65.938, "jitter": 4.621}, {"selection_state": "-", "remote": "vf2.bbnx.net", "refid": "252.74.143.178", "st": 2, "t": "u", "when": 34, "poll": 64, "reach": 1, "delay": 209.667, "offset": -81.337, "jitter": 5.547}, {"selection_state": "+", "remote": "t2.time.bf1.yah", "refid": "98.139.133.62", "st": 3, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 104.541, "offset": -67.956, "jitter": 5.731}, {"selection_state": null, "remote": "pugot.canonical", "refid": "17.253.34.125", "st": 2, "t": "u", "when": 43, "poll": 64, "reach": 1, "delay": 176.609, "offset": -65.874, "jitter": 0.0}, {"selection_state": "-", "remote": "50-205-244-110-", "refid": "50.205.244.27", "st": 2, "t": "u", "when": 30, "poll": 64, "reach": 1, "delay": 76.322, "offset": -65.135, "jitter": 2.334}, {"selection_state": "+", "remote": "1.time.dbsinet.", "refid": "146.186.222.14", "st": 2, "t": "u", "when": 31, "poll": 64, "reach": 1, "delay": 110.723, "offset": -65.056, "jitter": 6.198}, {"selection_state": null, "remote": "golem.canonical", "refid": "17.253.34.123", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 186.589, "offset": -61.769, "jitter": 0.0}, {"selection_state": "-", "remote": "103.105.51.156", "refid": "206.55.191.142", "st": 2, "t": "u", "when": 30, "poll": 64, "reach": 1, "delay": 38.164, "offset": -65.286, "jitter": 6.412}, {"selection_state": null, "remote": "alphyn.canonica", "refid": "17.253.108.125", "st": 2, "t": "u", "when": 44, "poll": 64, "reach": 1, "delay": 105.626, "offset": -63.73, "jitter": 0.0}, {"selection_state": "-", "remote": "vf1.bbnx.net", "refid": "253.109.221.150", "st": 2, "t": "u", "when": 31, "poll": 64, "reach": 1, "delay": 101.941, "offset": -73.597, "jitter": 3.39}, {"selection_state": null, "remote": "chilipepper.can", "refid": "145.238.203.14", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 188.989, "offset": -59.352, "jitter": 0.0}] diff --git a/tests/fixtures/ubuntu-18.04/ntpq-p.out b/tests/fixtures/ubuntu-18.04/ntpq-p.out new file mode 100644 index 00000000..9125637f --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/ntpq-p.out @@ -0,0 +1,21 @@ + remote refid st t when poll reach delay offset jitter +============================================================================== + 0.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 1.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 2.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 3.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + ntp.ubuntu.com .POOL. 16 p - 64 0 0.000 0.000 0.000 ++216.126.233.109 128.227.205.3 2 u 24 64 1 95.876 -65.426 4.502 ++time.cloudflare 10.4.0.197 3 u 33 64 1 25.239 -69.809 3.313 ++titan.crash-ove 139.78.97.128 2 u 35 64 1 60.008 -70.991 2.884 +*clock.nyc.he.ne .CDMA. 1 u 31 64 1 90.113 -65.938 4.621 +-vf2.bbnx.net 252.74.143.178 2 u 34 64 1 209.667 -81.337 5.547 ++t2.time.bf1.yah 98.139.133.62 3 u 35 64 1 104.541 -67.956 5.731 + pugot.canonical 17.253.34.125 2 u 43 64 1 176.609 -65.874 0.000 +-50-205-244-110- 50.205.244.27 2 u 30 64 1 76.322 -65.135 2.334 ++1.time.dbsinet. 146.186.222.14 2 u 31 64 1 110.723 -65.056 6.198 + golem.canonical 17.253.34.123 2 u 40 64 1 186.589 -61.769 0.000 +-103.105.51.156 206.55.191.142 2 u 30 64 1 38.164 -65.286 6.412 + alphyn.canonica 17.253.108.125 2 u 44 64 1 105.626 -63.730 0.000 +-vf1.bbnx.net 253.109.221.150 2 u 31 64 1 101.941 -73.597 3.390 + chilipepper.can 145.238.203.14 2 u 40 64 1 188.989 -59.352 0.000 diff --git a/tests/fixtures/ubuntu-18.04/ntpq-pn.json b/tests/fixtures/ubuntu-18.04/ntpq-pn.json new file mode 100644 index 00000000..3c1dd9ed --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/ntpq-pn.json @@ -0,0 +1 @@ +[{"selection_state": null, "remote": "0.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "1.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "2.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "3.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "ntp.ubuntu.com", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": "+", "remote": "216.126.233.109", "refid": "128.227.205.3", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 95.876, "offset": -65.426, "jitter": 4.502}, {"selection_state": "+", "remote": "162.159.200.123", "refid": "10.4.0.197", "st": 3, "t": "u", "when": 39, "poll": 64, "reach": 1, "delay": 25.239, "offset": -69.809, "jitter": 3.313}, {"selection_state": "+", "remote": "47.190.36.235", "refid": "139.78.97.128", "st": 2, "t": "u", "when": 41, "poll": 64, "reach": 1, "delay": 60.008, "offset": -70.991, "jitter": 2.884}, {"selection_state": "*", "remote": "209.51.161.238", "refid": ".CDMA.", "st": 1, "t": "u", "when": 37, "poll": 64, "reach": 1, "delay": 90.113, "offset": -65.938, "jitter": 4.621}, {"selection_state": "-", "remote": "23.31.21.164", "refid": "252.74.143.178", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 209.667, "offset": -81.337, "jitter": 5.547}, {"selection_state": "+", "remote": "72.30.35.88", "refid": "98.139.133.62", "st": 3, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 104.541, "offset": -67.956, "jitter": 5.731}, {"selection_state": null, "remote": "91.189.94.4", "refid": "17.253.34.125", "st": 2, "t": "u", "when": 48, "poll": 64, "reach": 1, "delay": 176.609, "offset": -65.874, "jitter": 0.0}, {"selection_state": "-", "remote": "50.205.244.110", "refid": "50.205.244.27", "st": 2, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 76.322, "offset": -65.135, "jitter": 2.334}, {"selection_state": "+", "remote": "199.223.248.99", "refid": "146.186.222.14", "st": 2, "t": "u", "when": 36, "poll": 64, "reach": 1, "delay": 110.723, "offset": -65.056, "jitter": 6.198}, {"selection_state": null, "remote": "91.189.89.199", "refid": "17.253.34.123", "st": 2, "t": "u", "when": 45, "poll": 64, "reach": 1, "delay": 186.589, "offset": -61.769, "jitter": 0.0}, {"selection_state": "-", "remote": "103.105.51.156", "refid": "206.55.191.142", "st": 2, "t": "u", "when": 34, "poll": 64, "reach": 1, "delay": 38.164, "offset": -65.286, "jitter": 6.412}, {"selection_state": null, "remote": "91.189.91.157", "refid": "17.253.108.125", "st": 2, "t": "u", "when": 48, "poll": 64, "reach": 1, "delay": 105.626, "offset": -63.73, "jitter": 0.0}, {"selection_state": "-", "remote": "23.31.21.163", "refid": "253.109.221.150", "st": 2, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 101.941, "offset": -73.597, "jitter": 3.39}, {"selection_state": null, "remote": "91.189.89.198", "refid": "145.238.203.14", "st": 2, "t": "u", "when": 44, "poll": 64, "reach": 1, "delay": 188.989, "offset": -59.352, "jitter": 0.0}] diff --git a/tests/fixtures/ubuntu-18.04/ntpq-pn.out b/tests/fixtures/ubuntu-18.04/ntpq-pn.out new file mode 100644 index 00000000..f9fe62e7 --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/ntpq-pn.out @@ -0,0 +1,21 @@ + remote refid st t when poll reach delay offset jitter +============================================================================== + 0.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 1.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 2.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 3.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + ntp.ubuntu.com .POOL. 16 p - 64 0 0.000 0.000 0.000 ++216.126.233.109 128.227.205.3 2 u 40 64 1 95.876 -65.426 4.502 ++162.159.200.123 10.4.0.197 3 u 39 64 1 25.239 -69.809 3.313 ++47.190.36.235 139.78.97.128 2 u 41 64 1 60.008 -70.991 2.884 +*209.51.161.238 .CDMA. 1 u 37 64 1 90.113 -65.938 4.621 +-23.31.21.164 252.74.143.178 2 u 40 64 1 209.667 -81.337 5.547 ++72.30.35.88 98.139.133.62 3 u 40 64 1 104.541 -67.956 5.731 + 91.189.94.4 17.253.34.125 2 u 48 64 1 176.609 -65.874 0.000 +-50.205.244.110 50.205.244.27 2 u 35 64 1 76.322 -65.135 2.334 ++199.223.248.99 146.186.222.14 2 u 36 64 1 110.723 -65.056 6.198 + 91.189.89.199 17.253.34.123 2 u 45 64 1 186.589 -61.769 0.000 +-103.105.51.156 206.55.191.142 2 u 34 64 1 38.164 -65.286 6.412 + 91.189.91.157 17.253.108.125 2 u 48 64 1 105.626 -63.730 0.000 +-23.31.21.163 253.109.221.150 2 u 35 64 1 101.941 -73.597 3.390 + 91.189.89.198 145.238.203.14 2 u 44 64 1 188.989 -59.352 0.000 diff --git a/tests/fixtures/ubuntu-18.04/ntqp-p.out b/tests/fixtures/ubuntu-18.04/ntqp-p.out new file mode 100644 index 00000000..9125637f --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/ntqp-p.out @@ -0,0 +1,21 @@ + remote refid st t when poll reach delay offset jitter +============================================================================== + 0.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 1.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 2.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 3.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + ntp.ubuntu.com .POOL. 16 p - 64 0 0.000 0.000 0.000 ++216.126.233.109 128.227.205.3 2 u 24 64 1 95.876 -65.426 4.502 ++time.cloudflare 10.4.0.197 3 u 33 64 1 25.239 -69.809 3.313 ++titan.crash-ove 139.78.97.128 2 u 35 64 1 60.008 -70.991 2.884 +*clock.nyc.he.ne .CDMA. 1 u 31 64 1 90.113 -65.938 4.621 +-vf2.bbnx.net 252.74.143.178 2 u 34 64 1 209.667 -81.337 5.547 ++t2.time.bf1.yah 98.139.133.62 3 u 35 64 1 104.541 -67.956 5.731 + pugot.canonical 17.253.34.125 2 u 43 64 1 176.609 -65.874 0.000 +-50-205-244-110- 50.205.244.27 2 u 30 64 1 76.322 -65.135 2.334 ++1.time.dbsinet. 146.186.222.14 2 u 31 64 1 110.723 -65.056 6.198 + golem.canonical 17.253.34.123 2 u 40 64 1 186.589 -61.769 0.000 +-103.105.51.156 206.55.191.142 2 u 30 64 1 38.164 -65.286 6.412 + alphyn.canonica 17.253.108.125 2 u 44 64 1 105.626 -63.730 0.000 +-vf1.bbnx.net 253.109.221.150 2 u 31 64 1 101.941 -73.597 3.390 + chilipepper.can 145.238.203.14 2 u 40 64 1 188.989 -59.352 0.000 diff --git a/tests/test_ntpq.py b/tests/test_ntpq.py new file mode 100644 index 00000000..59843474 --- /dev/null +++ b/tests/test_ntpq.py @@ -0,0 +1,66 @@ +import os +import unittest +import json +import jc.parsers.ntpq + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + + def setUp(self): + # input + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ntpq-p.out'), 'r', encoding='utf-8') as f: + self.centos_7_7_ntpq_p = f.read() + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ntpq-p.out'), 'r', encoding='utf-8') as f: + self.ubuntu_18_4_ntpq_p = f.read() + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ntpq-pn.out'), 'r', encoding='utf-8') as f: + self.centos_7_7_ntpq_pn = f.read() + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ntpq-pn.out'), 'r', encoding='utf-8') as f: + self.ubuntu_18_4_ntpq_pn = f.read() + + # output + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ntpq-p.json'), 'r', encoding='utf-8') as f: + self.centos_7_7_ntpq_p_json = json.loads(f.read()) + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ntpq-p.json'), 'r', encoding='utf-8') as f: + self.ubuntu_18_4_ntpq_p_json = json.loads(f.read()) + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ntpq-pn.json'), 'r', encoding='utf-8') as f: + self.centos_7_7_ntpq_pn_json = json.loads(f.read()) + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ntpq-pn.json'), 'r', encoding='utf-8') as f: + self.ubuntu_18_4_ntpq_pn_json = json.loads(f.read()) + + def test_ntpq_p_centos_7_7(self): + """ + Test 'ntpq -p' on Centos 7.7 + """ + self.assertEqual(jc.parsers.ntpq.parse(self.centos_7_7_ntpq_p, quiet=True), self.centos_7_7_ntpq_p_json) + + def test_ntpq_p_ubuntu_18_4(self): + """ + Test 'ntpq -p' on Ubuntu 18.4 + """ + self.assertEqual(jc.parsers.ntpq.parse(self.ubuntu_18_4_ntpq_p, quiet=True), self.ubuntu_18_4_ntpq_p_json) + + def test_ntpq_pn_centos_7_7(self): + """ + Test 'ntpq -pn' on Centos 7.7 + """ + self.assertEqual(jc.parsers.ntpq.parse(self.centos_7_7_ntpq_pn, quiet=True), self.centos_7_7_ntpq_pn_json) + + def test_ntpq_pn_ubuntu_18_4(self): + """ + Test 'ntpq -pn' on Ubuntu 18.4 + """ + self.assertEqual(jc.parsers.ntpq.parse(self.ubuntu_18_4_ntpq_pn, quiet=True), self.ubuntu_18_4_ntpq_pn_json) + + + + +if __name__ == '__main__': + unittest.main() From b24d0c3a475b88d9ccf1a8fe29715ee60972fcad Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 18:00:26 -0700 Subject: [PATCH 03/21] ntpq docs --- README.md | 33 +++++++++++++++++++++++++++++++++ jc/parsers/ntpq.py | 3 +-- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4c6d4118..79c55443 100755 --- a/README.md +++ b/README.md @@ -109,6 +109,7 @@ The JSON output can be compact (default) or pretty formatted with the `-p` optio - `--lsof` enables the `lsof` command parser - `--mount` enables the `mount` command parser - `--netstat` enables the `netstat` command parser +- `--ntpq` enables the `ntpq -p` command parser - `--passwd` enables the `/etc/passwd` file parser - `--pip-list` enables the `pip list` command parser - `--pip-show` enables the `pip show` command parser @@ -1484,6 +1485,38 @@ $ sudo netstat -apee | jc --netstat -p # or: sudo jc -p netstat -apee ... ] ``` +### ntpq +``` +$ ntpq -p | jc --ntpq -p # or: jc -p ntpq -p +[ + { + "selection_state": null, + "remote": "44.190.6.254", + "refid": "127.67.113.92", + "st": 2, + "t": "u", + "when": 1, + "poll": 64, + "reach": 1, + "delay": 23.399, + "offset": -2.805, + "jitter": 2.131 + }, + { + "selection_state": null, + "remote": "mirror1.sjc02.s", + "refid": "216.218.254.202", + "st": 2, + "t": "u", + "when": 2, + "poll": 64, + "reach": 1, + "delay": 29.325, + "offset": 1.044, + "jitter": 4.069 + } +] +``` ### /etc/passwd file ``` $ cat /etc/passwd | jc --passwd -p diff --git a/jc/parsers/ntpq.py b/jc/parsers/ntpq.py index 466dbcd9..64322c30 100644 --- a/jc/parsers/ntpq.py +++ b/jc/parsers/ntpq.py @@ -184,10 +184,9 @@ import jc.parsers.universal class info(): version = '1.0' - description = 'ntpq command parser' + description = 'ntpq -p command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' - # details = 'enter any other details here' # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux'] From 51935deb2ad18e4ea3ca16954ce810354f6095a9 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 18:00:47 -0700 Subject: [PATCH 04/21] timedatectl test fixtures --- tests/fixtures/centos-7.7/timedatectl.out | 14 ++++++++++++++ tests/fixtures/ubuntu-18.04/timedatectl.out | 7 +++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/fixtures/centos-7.7/timedatectl.out create mode 100644 tests/fixtures/ubuntu-18.04/timedatectl.out diff --git a/tests/fixtures/centos-7.7/timedatectl.out b/tests/fixtures/centos-7.7/timedatectl.out new file mode 100644 index 00000000..c18e6768 --- /dev/null +++ b/tests/fixtures/centos-7.7/timedatectl.out @@ -0,0 +1,14 @@ + Local time: Tue 2020-03-10 17:53:21 PDT + Universal time: Wed 2020-03-11 00:53:21 UTC + RTC time: Wed 2020-03-11 00:53:21 + Time zone: America/Los_Angeles (PDT, -0700) + NTP enabled: yes +NTP synchronized: yes + RTC in local TZ: no + DST active: yes + Last DST change: DST began at + Sun 2020-03-08 01:59:59 PST + Sun 2020-03-08 03:00:00 PDT + Next DST change: DST ends (the clock jumps one hour backwards) at + Sun 2020-11-01 01:59:59 PDT + Sun 2020-11-01 01:00:00 PST diff --git a/tests/fixtures/ubuntu-18.04/timedatectl.out b/tests/fixtures/ubuntu-18.04/timedatectl.out new file mode 100644 index 00000000..38f9a472 --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/timedatectl.out @@ -0,0 +1,7 @@ + Local time: Wed 2020-03-11 00:51:11 UTC + Universal time: Wed 2020-03-11 00:51:11 UTC + RTC time: Wed 2020-03-11 00:51:11 + Time zone: Etc/UTC (UTC, +0000) + System clock synchronized: yes +systemd-timesyncd.service active: yes + RTC in local TZ: no From 99804ea06e0c70e3a82ddc7f9c7a42343374d700 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 18:37:55 -0700 Subject: [PATCH 05/21] added timedatectl status parser --- README.md | 15 +++++ changelog.txt | 1 + docgen.sh | 1 + docs/parsers/timedatectl.md | 85 ++++++++++++++++++++++++++ jc/cli.py | 1 + jc/parsers/timedatectl.py | 119 ++++++++++++++++++++++++++++++++++++ 6 files changed, 222 insertions(+) create mode 100644 docs/parsers/timedatectl.md create mode 100644 jc/parsers/timedatectl.py diff --git a/README.md b/README.md index 79c55443..c0d1f9f1 100755 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ The JSON output can be compact (default) or pretty formatted with the `-p` optio - `--systemctl-lj` enables the `systemctl list-jobs` command parser - `--systemctl-ls` enables the `systemctl list-sockets` command parser - `--systemctl-luf` enables the `systemctl list-unit-files` command parser +- `--timedatectl` enables the `timedatectl status` command parser - `--uname` enables the `uname -a` command parser - `--uptime` enables the `uptime` command parser - `--w` enables the `w` command parser @@ -2024,6 +2025,20 @@ $ systemctl list-unit-files | jc --systemctl-luf -p # or: jc -p system ... ] ``` +### timedatectl status +``` +$ timedatectl | jc --timedatectl -p +{ + "local_time": "Tue 2020-03-10 17:53:21 PDT", + "universal_time": "Wed 2020-03-11 00:53:21 UTC", + "rtc_time": "Wed 2020-03-11 00:53:21", + "time_zone": "America/Los_Angeles (PDT, -0700)", + "ntp_enabled": true, + "ntp_synchronized": true, + "rtc_in_local_tz": false, + "dst_active": true +} +``` ### uname -a ``` $ uname -a | jc --uname -p # or: jc -p uname -a diff --git a/changelog.txt b/changelog.txt index 744fbef1..68fe5f6e 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ jc changelog 20200xxxx vX.X.X - Added ntpq command parser +- Added timedatectl status parser 20200308 v1.8.1 - CLI and history parser optimizations by https://github.com/philippeitis diff --git a/docgen.sh b/docgen.sh index a9746502..244adc54 100755 --- a/docgen.sh +++ b/docgen.sh @@ -44,6 +44,7 @@ pydocmd simple jc.parsers.systemctl+ > ../docs/parsers/systemctl.md pydocmd simple jc.parsers.systemctl_lj+ > ../docs/parsers/systemctl_lj.md pydocmd simple jc.parsers.systemctl_ls+ > ../docs/parsers/systemctl_ls.md pydocmd simple jc.parsers.systemctl_luf+ > ../docs/parsers/systemctl_luf.md +pydocmd simple jc.parsers.timedatectl+ > ../docs/parsers/timedatectl.md pydocmd simple jc.parsers.uname+ > ../docs/parsers/uname.md pydocmd simple jc.parsers.uptime+ > ../docs/parsers/uptime.md pydocmd simple jc.parsers.w+ > ../docs/parsers/w.md diff --git a/docs/parsers/timedatectl.md b/docs/parsers/timedatectl.md new file mode 100644 index 00000000..7f8341bd --- /dev/null +++ b/docs/parsers/timedatectl.md @@ -0,0 +1,85 @@ +# jc.parsers.timedatectl +jc - JSON CLI output utility timedatectl Parser + +Usage: + + specify --timedatectl as the first argument if the piped input is coming from timedatectl or timedatectl status + +Compatibility: + + 'linux' + +Examples: + + $ timedatectl | jc --timedatectl -p + { + "local_time": "Tue 2020-03-10 17:53:21 PDT", + "universal_time": "Wed 2020-03-11 00:53:21 UTC", + "rtc_time": "Wed 2020-03-11 00:53:21", + "time_zone": "America/Los_Angeles (PDT, -0700)", + "ntp_enabled": true, + "ntp_synchronized": true, + "rtc_in_local_tz": false, + "dst_active": true + } + + $ timedatectl | jc --timedatectl -p -r + { + "local_time": "Tue 2020-03-10 17:53:21 PDT", + "universal_time": "Wed 2020-03-11 00:53:21 UTC", + "rtc_time": "Wed 2020-03-11 00:53:21", + "time_zone": "America/Los_Angeles (PDT, -0700)", + "ntp_enabled": "yes", + "ntp_synchronized": "yes", + "rtc_in_local_tz": "no", + "dst_active": "yes" + } + +## info +```python +info(self, /, *args, **kwargs) +``` + +## process +```python +process(proc_data) +``` + +Final processing to conform to the schema. + +Parameters: + + proc_data: (dictionary) raw structured data to process + +Returns: + + Dictionary. Structured data with the following schema: + + { + "local_time": string, + "universal_time": string, + "rtc_time": string, + "time_zone": string, + "ntp_enabled": boolean, + "ntp_synchronized": boolean, + "rtc_in_local_tz": boolean, + "dst_active": boolean + } + +## parse +```python +parse(data, raw=False, quiet=False) +``` + +Main text parsing function + +Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + +Returns: + + Dictionary. Raw or processed structured data. + diff --git a/jc/cli.py b/jc/cli.py index c49f9d04..5e819aab 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -62,6 +62,7 @@ parsers = [ 'systemctl-lj', 'systemctl-ls', 'systemctl-luf', + 'timedatectl', 'uname', 'uptime', 'w', diff --git a/jc/parsers/timedatectl.py b/jc/parsers/timedatectl.py new file mode 100644 index 00000000..5c796d78 --- /dev/null +++ b/jc/parsers/timedatectl.py @@ -0,0 +1,119 @@ +"""jc - JSON CLI output utility timedatectl Parser + +Usage: + + specify --timedatectl as the first argument if the piped input is coming from timedatectl or timedatectl status + +Compatibility: + + 'linux' + +Examples: + + $ timedatectl | jc --timedatectl -p + { + "local_time": "Tue 2020-03-10 17:53:21 PDT", + "universal_time": "Wed 2020-03-11 00:53:21 UTC", + "rtc_time": "Wed 2020-03-11 00:53:21", + "time_zone": "America/Los_Angeles (PDT, -0700)", + "ntp_enabled": true, + "ntp_synchronized": true, + "rtc_in_local_tz": false, + "dst_active": true + } + + $ timedatectl | jc --timedatectl -p -r + { + "local_time": "Tue 2020-03-10 17:53:21 PDT", + "universal_time": "Wed 2020-03-11 00:53:21 UTC", + "rtc_time": "Wed 2020-03-11 00:53:21", + "time_zone": "America/Los_Angeles (PDT, -0700)", + "ntp_enabled": "yes", + "ntp_synchronized": "yes", + "rtc_in_local_tz": "no", + "dst_active": "yes" + } +""" +import jc.utils + + +class info(): + version = '1.0' + description = 'timedatectl status command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + # details = 'enter any other details here' + + # compatible options: linux, darwin, cygwin, win32, aix, freebsd + compatible = ['linux'] + magic_commands = ['timedatectl', 'timedatectl status'] + + +__version__ = info.version + + +def process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (dictionary) raw structured data to process + + Returns: + + Dictionary. Structured data with the following schema: + + { + "local_time": string, + "universal_time": string, + "rtc_time": string, + "time_zone": string, + "ntp_enabled": boolean, + "ntp_synchronized": boolean, + "rtc_in_local_tz": boolean, + "dst_active": boolean + } + """ + # boolean changes + bool_list = ['ntp_enabled', 'ntp_synchronized', 'rtc_in_local_tz', 'dst_active'] + for key in proc_data: + if key in bool_list: + try: + proc_data[key] = True if proc_data[key] == 'yes' else False + except (ValueError): + proc_data[key] = None + + return proc_data + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + + Returns: + + Dictionary. Raw or processed structured data. + """ + if not quiet: + jc.utils.compatibility(__name__, info.compatible) + + raw_output = {} + + for line in filter(None, data.splitlines()): + linedata = line.split(':', maxsplit=1) + raw_output[linedata[0].strip().lower().replace(' ', '_')] = linedata[1].strip() + + if linedata[0].strip() == 'DST active': + break + + if raw: + return raw_output + else: + return process(raw_output) From 391d06f68d1f45d33590d5407d3a5e4f723af717 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 20:16:41 -0700 Subject: [PATCH 06/21] change selection_state to state --- README.md | 4 +-- docs/parsers/ntpq.md | 26 +++++++++---------- jc/parsers/ntpq.py | 32 ++++++++++++------------ tests/fixtures/centos-7.7/ntpq-p.json | 2 +- tests/fixtures/centos-7.7/ntpq-pn.json | 2 +- tests/fixtures/ubuntu-18.04/ntpq-p.json | 2 +- tests/fixtures/ubuntu-18.04/ntpq-pn.json | 2 +- 7 files changed, 35 insertions(+), 35 deletions(-) diff --git a/README.md b/README.md index c0d1f9f1..c17a36d7 100755 --- a/README.md +++ b/README.md @@ -1491,7 +1491,7 @@ $ sudo netstat -apee | jc --netstat -p # or: sudo jc -p netstat -apee $ ntpq -p | jc --ntpq -p # or: jc -p ntpq -p [ { - "selection_state": null, + "state": null, "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, @@ -1504,7 +1504,7 @@ $ ntpq -p | jc --ntpq -p # or: jc -p ntpq -p "jitter": 2.131 }, { - "selection_state": null, + "state": null, "remote": "mirror1.sjc02.s", "refid": "216.218.254.202", "st": 2, diff --git a/docs/parsers/ntpq.md b/docs/parsers/ntpq.md index d0062147..343da1f5 100644 --- a/docs/parsers/ntpq.md +++ b/docs/parsers/ntpq.md @@ -14,7 +14,7 @@ Examples: $ ntpq -p | jc --ntpq -p [ { - "selection_state": null, + "state": null, "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, @@ -27,7 +27,7 @@ Examples: "jitter": 2.131 }, { - "selection_state": null, + "state": null, "remote": "ntp.wdc1.us.lea", "refid": "130.133.1.10", "st": 2, @@ -40,7 +40,7 @@ Examples: "jitter": 2.839 }, { - "selection_state": null, + "state": null, "remote": "clock.team-cymr", "refid": "204.9.54.119", "st": 2, @@ -53,7 +53,7 @@ Examples: "jitter": 2.6 }, { - "selection_state": null, + "state": null, "remote": "mirror1.sjc02.s", "refid": "216.218.254.202", "st": 2, @@ -70,7 +70,7 @@ Examples: $ ntpq -pn| jc --ntpq -p [ { - "selection_state": "+", + "state": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, @@ -83,7 +83,7 @@ Examples: "jitter": 2.085 }, { - "selection_state": "-", + "state": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": 2, @@ -96,7 +96,7 @@ Examples: "jitter": 1.908 }, { - "selection_state": "+", + "state": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": 2, @@ -109,7 +109,7 @@ Examples: "jitter": 2.576 }, { - "selection_state": "*", + "state": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": 2, @@ -126,7 +126,7 @@ Examples: $ ntpq -pn| jc --ntpq -p -r [ { - "selection_state": "+", + "state": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": "2", @@ -139,7 +139,7 @@ Examples: "jitter": "2.085" }, { - "selection_state": "-", + "state": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": "2", @@ -152,7 +152,7 @@ Examples: "jitter": "1.908" }, { - "selection_state": "+", + "state": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": "2", @@ -165,7 +165,7 @@ Examples: "jitter": "2.576" }, { - "selection_state": "*", + "state": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": "2", @@ -201,7 +201,7 @@ Returns: [ { - "selection_state": string, # space/~ converted to null + "state": string, # space/~ converted to null "remote": string, "refid": string, "st": integer, diff --git a/jc/parsers/ntpq.py b/jc/parsers/ntpq.py index 64322c30..cb41b626 100644 --- a/jc/parsers/ntpq.py +++ b/jc/parsers/ntpq.py @@ -13,7 +13,7 @@ Examples: $ ntpq -p | jc --ntpq -p [ { - "selection_state": null, + "state": null, "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, @@ -26,7 +26,7 @@ Examples: "jitter": 2.131 }, { - "selection_state": null, + "state": null, "remote": "ntp.wdc1.us.lea", "refid": "130.133.1.10", "st": 2, @@ -39,7 +39,7 @@ Examples: "jitter": 2.839 }, { - "selection_state": null, + "state": null, "remote": "clock.team-cymr", "refid": "204.9.54.119", "st": 2, @@ -52,7 +52,7 @@ Examples: "jitter": 2.6 }, { - "selection_state": null, + "state": null, "remote": "mirror1.sjc02.s", "refid": "216.218.254.202", "st": 2, @@ -69,7 +69,7 @@ Examples: $ ntpq -pn| jc --ntpq -p [ { - "selection_state": "+", + "state": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, @@ -82,7 +82,7 @@ Examples: "jitter": 2.085 }, { - "selection_state": "-", + "state": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": 2, @@ -95,7 +95,7 @@ Examples: "jitter": 1.908 }, { - "selection_state": "+", + "state": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": 2, @@ -108,7 +108,7 @@ Examples: "jitter": 2.576 }, { - "selection_state": "*", + "state": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": 2, @@ -125,7 +125,7 @@ Examples: $ ntpq -pn| jc --ntpq -p -r [ { - "selection_state": "+", + "state": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": "2", @@ -138,7 +138,7 @@ Examples: "jitter": "2.085" }, { - "selection_state": "-", + "state": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": "2", @@ -151,7 +151,7 @@ Examples: "jitter": "1.908" }, { - "selection_state": "+", + "state": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": "2", @@ -164,7 +164,7 @@ Examples: "jitter": "2.576" }, { - "selection_state": "*", + "state": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": "2", @@ -210,7 +210,7 @@ def process(proc_data): [ { - "selection_state": string, # space/~ converted to null + "state": string, # space/~ converted to null "remote": string, "refid": string, "st": integer, @@ -227,8 +227,8 @@ def process(proc_data): """ for entry in proc_data: - if entry['selection_state'] == '~': - entry['selection_state'] = None + if entry['state'] == '~': + entry['state'] = None int_list = ['st', 'when', 'poll', 'reach'] for key in int_list: @@ -269,7 +269,7 @@ def parse(data, raw=False, quiet=False): raw_output = [] cleandata = data.splitlines() - cleandata[0] = 'selection_state ' + cleandata[0] + cleandata[0] = 'state ' + cleandata[0] cleandata[0] = cleandata[0].lower() # delete header delimiter diff --git a/tests/fixtures/centos-7.7/ntpq-p.json b/tests/fixtures/centos-7.7/ntpq-p.json index afdb4cca..1dbdd318 100644 --- a/tests/fixtures/centos-7.7/ntpq-p.json +++ b/tests/fixtures/centos-7.7/ntpq-p.json @@ -1 +1 @@ -[{"selection_state": null, "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, "t": "u", "when": 1, "poll": 64, "reach": 1, "delay": 23.399, "offset": -2.805, "jitter": 2.131}, {"selection_state": null, "remote": "ntp.wdc1.us.lea", "refid": "130.133.1.10", "st": 2, "t": "u", "when": null, "poll": 64, "reach": 1, "delay": 93.053, "offset": -0.807, "jitter": 2.839}, {"selection_state": null, "remote": "clock.team-cymr", "refid": "204.9.54.119", "st": 2, "t": "u", "when": null, "poll": 64, "reach": 1, "delay": 70.337, "offset": -2.909, "jitter": 2.6}, {"selection_state": null, "remote": "mirror1.sjc02.s", "refid": "216.218.254.202", "st": 2, "t": "u", "when": 2, "poll": 64, "reach": 1, "delay": 29.325, "offset": 1.044, "jitter": 4.069}] +[{"state": null, "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, "t": "u", "when": 1, "poll": 64, "reach": 1, "delay": 23.399, "offset": -2.805, "jitter": 2.131}, {"state": null, "remote": "ntp.wdc1.us.lea", "refid": "130.133.1.10", "st": 2, "t": "u", "when": null, "poll": 64, "reach": 1, "delay": 93.053, "offset": -0.807, "jitter": 2.839}, {"state": null, "remote": "clock.team-cymr", "refid": "204.9.54.119", "st": 2, "t": "u", "when": null, "poll": 64, "reach": 1, "delay": 70.337, "offset": -2.909, "jitter": 2.6}, {"state": null, "remote": "mirror1.sjc02.s", "refid": "216.218.254.202", "st": 2, "t": "u", "when": 2, "poll": 64, "reach": 1, "delay": 29.325, "offset": 1.044, "jitter": 4.069}] diff --git a/tests/fixtures/centos-7.7/ntpq-pn.json b/tests/fixtures/centos-7.7/ntpq-pn.json index 7e0a675f..2ad28cbf 100644 --- a/tests/fixtures/centos-7.7/ntpq-pn.json +++ b/tests/fixtures/centos-7.7/ntpq-pn.json @@ -1 +1 @@ -[{"selection_state": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, "t": "u", "when": 66, "poll": 64, "reach": 377, "delay": 22.69, "offset": -0.392, "jitter": 2.085}, {"selection_state": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": 2, "t": "u", "when": 63, "poll": 64, "reach": 377, "delay": 90.805, "offset": 2.84, "jitter": 1.908}, {"selection_state": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": 2, "t": "u", "when": 64, "poll": 64, "reach": 377, "delay": 68.699, "offset": -0.61, "jitter": 2.576}, {"selection_state": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": 2, "t": "u", "when": 63, "poll": 64, "reach": 377, "delay": 22.654, "offset": 0.231, "jitter": 1.964}] +[{"state": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, "t": "u", "when": 66, "poll": 64, "reach": 377, "delay": 22.69, "offset": -0.392, "jitter": 2.085}, {"state": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": 2, "t": "u", "when": 63, "poll": 64, "reach": 377, "delay": 90.805, "offset": 2.84, "jitter": 1.908}, {"state": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": 2, "t": "u", "when": 64, "poll": 64, "reach": 377, "delay": 68.699, "offset": -0.61, "jitter": 2.576}, {"state": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": 2, "t": "u", "when": 63, "poll": 64, "reach": 377, "delay": 22.654, "offset": 0.231, "jitter": 1.964}] diff --git a/tests/fixtures/ubuntu-18.04/ntpq-p.json b/tests/fixtures/ubuntu-18.04/ntpq-p.json index b23f1230..5e1fb02c 100644 --- a/tests/fixtures/ubuntu-18.04/ntpq-p.json +++ b/tests/fixtures/ubuntu-18.04/ntpq-p.json @@ -1 +1 @@ -[{"selection_state": null, "remote": "0.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "1.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "2.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "3.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "ntp.ubuntu.com", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": "+", "remote": "216.126.233.109", "refid": "128.227.205.3", "st": 2, "t": "u", "when": 24, "poll": 64, "reach": 1, "delay": 95.876, "offset": -65.426, "jitter": 4.502}, {"selection_state": "+", "remote": "time.cloudflare", "refid": "10.4.0.197", "st": 3, "t": "u", "when": 33, "poll": 64, "reach": 1, "delay": 25.239, "offset": -69.809, "jitter": 3.313}, {"selection_state": "+", "remote": "titan.crash-ove", "refid": "139.78.97.128", "st": 2, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 60.008, "offset": -70.991, "jitter": 2.884}, {"selection_state": "*", "remote": "clock.nyc.he.ne", "refid": ".CDMA.", "st": 1, "t": "u", "when": 31, "poll": 64, "reach": 1, "delay": 90.113, "offset": -65.938, "jitter": 4.621}, {"selection_state": "-", "remote": "vf2.bbnx.net", "refid": "252.74.143.178", "st": 2, "t": "u", "when": 34, "poll": 64, "reach": 1, "delay": 209.667, "offset": -81.337, "jitter": 5.547}, {"selection_state": "+", "remote": "t2.time.bf1.yah", "refid": "98.139.133.62", "st": 3, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 104.541, "offset": -67.956, "jitter": 5.731}, {"selection_state": null, "remote": "pugot.canonical", "refid": "17.253.34.125", "st": 2, "t": "u", "when": 43, "poll": 64, "reach": 1, "delay": 176.609, "offset": -65.874, "jitter": 0.0}, {"selection_state": "-", "remote": "50-205-244-110-", "refid": "50.205.244.27", "st": 2, "t": "u", "when": 30, "poll": 64, "reach": 1, "delay": 76.322, "offset": -65.135, "jitter": 2.334}, {"selection_state": "+", "remote": "1.time.dbsinet.", "refid": "146.186.222.14", "st": 2, "t": "u", "when": 31, "poll": 64, "reach": 1, "delay": 110.723, "offset": -65.056, "jitter": 6.198}, {"selection_state": null, "remote": "golem.canonical", "refid": "17.253.34.123", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 186.589, "offset": -61.769, "jitter": 0.0}, {"selection_state": "-", "remote": "103.105.51.156", "refid": "206.55.191.142", "st": 2, "t": "u", "when": 30, "poll": 64, "reach": 1, "delay": 38.164, "offset": -65.286, "jitter": 6.412}, {"selection_state": null, "remote": "alphyn.canonica", "refid": "17.253.108.125", "st": 2, "t": "u", "when": 44, "poll": 64, "reach": 1, "delay": 105.626, "offset": -63.73, "jitter": 0.0}, {"selection_state": "-", "remote": "vf1.bbnx.net", "refid": "253.109.221.150", "st": 2, "t": "u", "when": 31, "poll": 64, "reach": 1, "delay": 101.941, "offset": -73.597, "jitter": 3.39}, {"selection_state": null, "remote": "chilipepper.can", "refid": "145.238.203.14", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 188.989, "offset": -59.352, "jitter": 0.0}] +[{"state": null, "remote": "0.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"state": null, "remote": "1.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"state": null, "remote": "2.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"state": null, "remote": "3.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"state": null, "remote": "ntp.ubuntu.com", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"state": "+", "remote": "216.126.233.109", "refid": "128.227.205.3", "st": 2, "t": "u", "when": 24, "poll": 64, "reach": 1, "delay": 95.876, "offset": -65.426, "jitter": 4.502}, {"state": "+", "remote": "time.cloudflare", "refid": "10.4.0.197", "st": 3, "t": "u", "when": 33, "poll": 64, "reach": 1, "delay": 25.239, "offset": -69.809, "jitter": 3.313}, {"state": "+", "remote": "titan.crash-ove", "refid": "139.78.97.128", "st": 2, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 60.008, "offset": -70.991, "jitter": 2.884}, {"state": "*", "remote": "clock.nyc.he.ne", "refid": ".CDMA.", "st": 1, "t": "u", "when": 31, "poll": 64, "reach": 1, "delay": 90.113, "offset": -65.938, "jitter": 4.621}, {"state": "-", "remote": "vf2.bbnx.net", "refid": "252.74.143.178", "st": 2, "t": "u", "when": 34, "poll": 64, "reach": 1, "delay": 209.667, "offset": -81.337, "jitter": 5.547}, {"state": "+", "remote": "t2.time.bf1.yah", "refid": "98.139.133.62", "st": 3, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 104.541, "offset": -67.956, "jitter": 5.731}, {"state": null, "remote": "pugot.canonical", "refid": "17.253.34.125", "st": 2, "t": "u", "when": 43, "poll": 64, "reach": 1, "delay": 176.609, "offset": -65.874, "jitter": 0.0}, {"state": "-", "remote": "50-205-244-110-", "refid": "50.205.244.27", "st": 2, "t": "u", "when": 30, "poll": 64, "reach": 1, "delay": 76.322, "offset": -65.135, "jitter": 2.334}, {"state": "+", "remote": "1.time.dbsinet.", "refid": "146.186.222.14", "st": 2, "t": "u", "when": 31, "poll": 64, "reach": 1, "delay": 110.723, "offset": -65.056, "jitter": 6.198}, {"state": null, "remote": "golem.canonical", "refid": "17.253.34.123", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 186.589, "offset": -61.769, "jitter": 0.0}, {"state": "-", "remote": "103.105.51.156", "refid": "206.55.191.142", "st": 2, "t": "u", "when": 30, "poll": 64, "reach": 1, "delay": 38.164, "offset": -65.286, "jitter": 6.412}, {"state": null, "remote": "alphyn.canonica", "refid": "17.253.108.125", "st": 2, "t": "u", "when": 44, "poll": 64, "reach": 1, "delay": 105.626, "offset": -63.73, "jitter": 0.0}, {"state": "-", "remote": "vf1.bbnx.net", "refid": "253.109.221.150", "st": 2, "t": "u", "when": 31, "poll": 64, "reach": 1, "delay": 101.941, "offset": -73.597, "jitter": 3.39}, {"state": null, "remote": "chilipepper.can", "refid": "145.238.203.14", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 188.989, "offset": -59.352, "jitter": 0.0}] diff --git a/tests/fixtures/ubuntu-18.04/ntpq-pn.json b/tests/fixtures/ubuntu-18.04/ntpq-pn.json index 3c1dd9ed..24224484 100644 --- a/tests/fixtures/ubuntu-18.04/ntpq-pn.json +++ b/tests/fixtures/ubuntu-18.04/ntpq-pn.json @@ -1 +1 @@ -[{"selection_state": null, "remote": "0.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "1.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "2.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "3.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": null, "remote": "ntp.ubuntu.com", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"selection_state": "+", "remote": "216.126.233.109", "refid": "128.227.205.3", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 95.876, "offset": -65.426, "jitter": 4.502}, {"selection_state": "+", "remote": "162.159.200.123", "refid": "10.4.0.197", "st": 3, "t": "u", "when": 39, "poll": 64, "reach": 1, "delay": 25.239, "offset": -69.809, "jitter": 3.313}, {"selection_state": "+", "remote": "47.190.36.235", "refid": "139.78.97.128", "st": 2, "t": "u", "when": 41, "poll": 64, "reach": 1, "delay": 60.008, "offset": -70.991, "jitter": 2.884}, {"selection_state": "*", "remote": "209.51.161.238", "refid": ".CDMA.", "st": 1, "t": "u", "when": 37, "poll": 64, "reach": 1, "delay": 90.113, "offset": -65.938, "jitter": 4.621}, {"selection_state": "-", "remote": "23.31.21.164", "refid": "252.74.143.178", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 209.667, "offset": -81.337, "jitter": 5.547}, {"selection_state": "+", "remote": "72.30.35.88", "refid": "98.139.133.62", "st": 3, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 104.541, "offset": -67.956, "jitter": 5.731}, {"selection_state": null, "remote": "91.189.94.4", "refid": "17.253.34.125", "st": 2, "t": "u", "when": 48, "poll": 64, "reach": 1, "delay": 176.609, "offset": -65.874, "jitter": 0.0}, {"selection_state": "-", "remote": "50.205.244.110", "refid": "50.205.244.27", "st": 2, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 76.322, "offset": -65.135, "jitter": 2.334}, {"selection_state": "+", "remote": "199.223.248.99", "refid": "146.186.222.14", "st": 2, "t": "u", "when": 36, "poll": 64, "reach": 1, "delay": 110.723, "offset": -65.056, "jitter": 6.198}, {"selection_state": null, "remote": "91.189.89.199", "refid": "17.253.34.123", "st": 2, "t": "u", "when": 45, "poll": 64, "reach": 1, "delay": 186.589, "offset": -61.769, "jitter": 0.0}, {"selection_state": "-", "remote": "103.105.51.156", "refid": "206.55.191.142", "st": 2, "t": "u", "when": 34, "poll": 64, "reach": 1, "delay": 38.164, "offset": -65.286, "jitter": 6.412}, {"selection_state": null, "remote": "91.189.91.157", "refid": "17.253.108.125", "st": 2, "t": "u", "when": 48, "poll": 64, "reach": 1, "delay": 105.626, "offset": -63.73, "jitter": 0.0}, {"selection_state": "-", "remote": "23.31.21.163", "refid": "253.109.221.150", "st": 2, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 101.941, "offset": -73.597, "jitter": 3.39}, {"selection_state": null, "remote": "91.189.89.198", "refid": "145.238.203.14", "st": 2, "t": "u", "when": 44, "poll": 64, "reach": 1, "delay": 188.989, "offset": -59.352, "jitter": 0.0}] +[{"state": null, "remote": "0.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"state": null, "remote": "1.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"state": null, "remote": "2.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"state": null, "remote": "3.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"state": null, "remote": "ntp.ubuntu.com", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0}, {"state": "+", "remote": "216.126.233.109", "refid": "128.227.205.3", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 95.876, "offset": -65.426, "jitter": 4.502}, {"state": "+", "remote": "162.159.200.123", "refid": "10.4.0.197", "st": 3, "t": "u", "when": 39, "poll": 64, "reach": 1, "delay": 25.239, "offset": -69.809, "jitter": 3.313}, {"state": "+", "remote": "47.190.36.235", "refid": "139.78.97.128", "st": 2, "t": "u", "when": 41, "poll": 64, "reach": 1, "delay": 60.008, "offset": -70.991, "jitter": 2.884}, {"state": "*", "remote": "209.51.161.238", "refid": ".CDMA.", "st": 1, "t": "u", "when": 37, "poll": 64, "reach": 1, "delay": 90.113, "offset": -65.938, "jitter": 4.621}, {"state": "-", "remote": "23.31.21.164", "refid": "252.74.143.178", "st": 2, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 209.667, "offset": -81.337, "jitter": 5.547}, {"state": "+", "remote": "72.30.35.88", "refid": "98.139.133.62", "st": 3, "t": "u", "when": 40, "poll": 64, "reach": 1, "delay": 104.541, "offset": -67.956, "jitter": 5.731}, {"state": null, "remote": "91.189.94.4", "refid": "17.253.34.125", "st": 2, "t": "u", "when": 48, "poll": 64, "reach": 1, "delay": 176.609, "offset": -65.874, "jitter": 0.0}, {"state": "-", "remote": "50.205.244.110", "refid": "50.205.244.27", "st": 2, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 76.322, "offset": -65.135, "jitter": 2.334}, {"state": "+", "remote": "199.223.248.99", "refid": "146.186.222.14", "st": 2, "t": "u", "when": 36, "poll": 64, "reach": 1, "delay": 110.723, "offset": -65.056, "jitter": 6.198}, {"state": null, "remote": "91.189.89.199", "refid": "17.253.34.123", "st": 2, "t": "u", "when": 45, "poll": 64, "reach": 1, "delay": 186.589, "offset": -61.769, "jitter": 0.0}, {"state": "-", "remote": "103.105.51.156", "refid": "206.55.191.142", "st": 2, "t": "u", "when": 34, "poll": 64, "reach": 1, "delay": 38.164, "offset": -65.286, "jitter": 6.412}, {"state": null, "remote": "91.189.91.157", "refid": "17.253.108.125", "st": 2, "t": "u", "when": 48, "poll": 64, "reach": 1, "delay": 105.626, "offset": -63.73, "jitter": 0.0}, {"state": "-", "remote": "23.31.21.163", "refid": "253.109.221.150", "st": 2, "t": "u", "when": 35, "poll": 64, "reach": 1, "delay": 101.941, "offset": -73.597, "jitter": 3.39}, {"state": null, "remote": "91.189.89.198", "refid": "145.238.203.14", "st": 2, "t": "u", "when": 44, "poll": 64, "reach": 1, "delay": 188.989, "offset": -59.352, "jitter": 0.0}] From e3a6c05a58a2451e70975d8fabf644c56603c73d Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 20:26:53 -0700 Subject: [PATCH 07/21] timedatectl fixes, tests, and fixtures for issue #42 --- docs/parsers/timedatectl.md | 18 +++++---- jc/parsers/timedatectl.py | 21 +++++----- tests/fixtures/centos-7.7/timedatectl.json | 1 + tests/fixtures/ubuntu-18.04/timedatectl.json | 1 + tests/test_timedatectl.py | 40 ++++++++++++++++++++ 5 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 tests/fixtures/centos-7.7/timedatectl.json create mode 100644 tests/fixtures/ubuntu-18.04/timedatectl.json create mode 100644 tests/test_timedatectl.py diff --git a/docs/parsers/timedatectl.md b/docs/parsers/timedatectl.md index 7f8341bd..1f7f86b2 100644 --- a/docs/parsers/timedatectl.md +++ b/docs/parsers/timedatectl.md @@ -56,14 +56,16 @@ Returns: Dictionary. Structured data with the following schema: { - "local_time": string, - "universal_time": string, - "rtc_time": string, - "time_zone": string, - "ntp_enabled": boolean, - "ntp_synchronized": boolean, - "rtc_in_local_tz": boolean, - "dst_active": boolean + "local_time": string, + "universal_time": string, + "rtc_time": string, + "time_zone": string, + "ntp_enabled": boolean, + "ntp_synchronized": boolean, + "system_clock_synchronized": boolean, + "systemd-timesyncd.service_active": boolean, + "rtc_in_local_tz": boolean, + "dst_active": boolean } ## parse diff --git a/jc/parsers/timedatectl.py b/jc/parsers/timedatectl.py index 5c796d78..256e0c73 100644 --- a/jc/parsers/timedatectl.py +++ b/jc/parsers/timedatectl.py @@ -65,18 +65,21 @@ def process(proc_data): Dictionary. Structured data with the following schema: { - "local_time": string, - "universal_time": string, - "rtc_time": string, - "time_zone": string, - "ntp_enabled": boolean, - "ntp_synchronized": boolean, - "rtc_in_local_tz": boolean, - "dst_active": boolean + "local_time": string, + "universal_time": string, + "rtc_time": string, + "time_zone": string, + "ntp_enabled": boolean, + "ntp_synchronized": boolean, + "system_clock_synchronized": boolean, + "systemd-timesyncd.service_active": boolean, + "rtc_in_local_tz": boolean, + "dst_active": boolean } """ # boolean changes - bool_list = ['ntp_enabled', 'ntp_synchronized', 'rtc_in_local_tz', 'dst_active'] + bool_list = ['ntp_enabled', 'ntp_synchronized', 'rtc_in_local_tz', 'dst_active', + 'system_clock_synchronized', 'systemd-timesyncd.service_active'] for key in proc_data: if key in bool_list: try: diff --git a/tests/fixtures/centos-7.7/timedatectl.json b/tests/fixtures/centos-7.7/timedatectl.json new file mode 100644 index 00000000..11d2bbce --- /dev/null +++ b/tests/fixtures/centos-7.7/timedatectl.json @@ -0,0 +1 @@ +{"local_time": "Tue 2020-03-10 17:53:21 PDT", "universal_time": "Wed 2020-03-11 00:53:21 UTC", "rtc_time": "Wed 2020-03-11 00:53:21", "time_zone": "America/Los_Angeles (PDT, -0700)", "ntp_enabled": true, "ntp_synchronized": true, "rtc_in_local_tz": false, "dst_active": true} diff --git a/tests/fixtures/ubuntu-18.04/timedatectl.json b/tests/fixtures/ubuntu-18.04/timedatectl.json new file mode 100644 index 00000000..4cc48998 --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/timedatectl.json @@ -0,0 +1 @@ +{"local_time": "Wed 2020-03-11 00:51:11 UTC", "universal_time": "Wed 2020-03-11 00:51:11 UTC", "rtc_time": "Wed 2020-03-11 00:51:11", "time_zone": "Etc/UTC (UTC, +0000)", "system_clock_synchronized": true, "systemd-timesyncd.service_active": true, "rtc_in_local_tz": false} diff --git a/tests/test_timedatectl.py b/tests/test_timedatectl.py new file mode 100644 index 00000000..52e086e2 --- /dev/null +++ b/tests/test_timedatectl.py @@ -0,0 +1,40 @@ +import os +import unittest +import json +import jc.parsers.timedatectl + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + + def setUp(self): + # input + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/timedatectl.out'), 'r', encoding='utf-8') as f: + self.centos_7_7_timedatectl = f.read() + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/timedatectl.out'), 'r', encoding='utf-8') as f: + self.ubuntu_18_4_timedatectl = f.read() + + # output + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/timedatectl.json'), 'r', encoding='utf-8') as f: + self.centos_7_7_timedatectl_json = json.loads(f.read()) + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/timedatectl.json'), 'r', encoding='utf-8') as f: + self.ubuntu_18_4_timedatectl_json = json.loads(f.read()) + + def test_timedatectl_centos_7_7(self): + """ + Test 'timedatectl' on Centos 7.7 + """ + self.assertEqual(jc.parsers.timedatectl.parse(self.centos_7_7_timedatectl, quiet=True), self.centos_7_7_timedatectl_json) + + def test_timedatectl_ubuntu_18_4(self): + """ + Test 'timedatectl' on Ubuntu 18.4 + """ + self.assertEqual(jc.parsers.timedatectl.parse(self.ubuntu_18_4_timedatectl, quiet=True), self.ubuntu_18_4_timedatectl_json) + + +if __name__ == '__main__': + unittest.main() From c6c9e06496683d2dd3586d17085801c7e698d960 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 20:35:52 -0700 Subject: [PATCH 08/21] added airport command parser --- changelog.txt | 3 +- docgen.sh | 1 + docs/parsers/airport.md | 64 ++++++++++++++++ jc/cli.py | 1 + jc/parsers/airport.py | 98 ++++++++++++++++++++++++ tests/fixtures/osx-10.14.6/airport-I.out | 15 ++++ tests/fixtures/osx-10.14.6/airport-s.out | 15 ++++ 7 files changed, 196 insertions(+), 1 deletion(-) create mode 100644 docs/parsers/airport.md create mode 100644 jc/parsers/airport.py create mode 100644 tests/fixtures/osx-10.14.6/airport-I.out create mode 100644 tests/fixtures/osx-10.14.6/airport-s.out diff --git a/changelog.txt b/changelog.txt index 68fe5f6e..35bd791d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,7 +2,8 @@ jc changelog 20200xxxx vX.X.X - Added ntpq command parser -- Added timedatectl status parser +- Added timedatectl status command parser +- Added airport command parser 20200308 v1.8.1 - CLI and history parser optimizations by https://github.com/philippeitis diff --git a/docgen.sh b/docgen.sh index 244adc54..462f76eb 100755 --- a/docgen.sh +++ b/docgen.sh @@ -4,6 +4,7 @@ cd jc pydocmd simple jc+ > ../docs/readme.md pydocmd simple utils+ > ../docs/utils.md +pydocmd simple jc.parsers.airport+ > ../docs/parsers/airport.md pydocmd simple jc.parsers.arp+ > ../docs/parsers/arp.md pydocmd simple jc.parsers.blkid+ > ../docs/parsers/blkid.md pydocmd simple jc.parsers.crontab+ > ../docs/parsers/crontab.md diff --git a/docs/parsers/airport.md b/docs/parsers/airport.md new file mode 100644 index 00000000..08e848f7 --- /dev/null +++ b/docs/parsers/airport.md @@ -0,0 +1,64 @@ +# jc.parsers.airport +jc - JSON CLI output utility airport Parser + +Usage: + + specify --airport as the first argument if the piped input is coming from airport + +Compatibility: + + 'darwin' + +Examples: + + $ airport | jc --airport -p + { + + } + + $ airport | jc --airport -p -r + { + + } + +## info +```python +info(self, /, *args, **kwargs) +``` + +## process +```python +process(proc_data) +``` + +Final processing to conform to the schema. + +Parameters: + + proc_data: (dictionary) raw structured data to process + +Returns: + + Dictionary. Structured data with the following schema: + + { + + } + +## parse +```python +parse(data, raw=False, quiet=False) +``` + +Main text parsing function + +Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + +Returns: + + Dictionary. Raw or processed structured data. + diff --git a/jc/cli.py b/jc/cli.py index 5e819aab..c4a1e326 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -22,6 +22,7 @@ class info(): __version__ = info.version parsers = [ + 'airport', 'arp', 'blkid', 'crontab', diff --git a/jc/parsers/airport.py b/jc/parsers/airport.py new file mode 100644 index 00000000..c8968efe --- /dev/null +++ b/jc/parsers/airport.py @@ -0,0 +1,98 @@ +"""jc - JSON CLI output utility airport Parser + +Usage: + + specify --airport as the first argument if the piped input is coming from airport + +Compatibility: + + 'darwin' + +Examples: + + $ airport | jc --airport -p + { + + } + + $ airport | jc --airport -p -r + { + + } +""" +import jc.utils + + +class info(): + version = '1.0' + description = 'airport command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + # details = 'enter any other details here' + + # compatible options: linux, darwin, cygwin, win32, aix, freebsd + compatible = ['darwin'] + magic_commands = ['airport'] + + +__version__ = info.version + + +def process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (dictionary) raw structured data to process + + Returns: + + Dictionary. Structured data with the following schema: + + { + + } + """ + # boolean changes + ''' + bool_list = ['ntp_enabled', 'ntp_synchronized', 'rtc_in_local_tz', 'dst_active', + 'system_clock_synchronized', 'systemd-timesyncd.service_active'] + for key in proc_data: + if key in bool_list: + try: + proc_data[key] = True if proc_data[key] == 'yes' else False + except (ValueError): + proc_data[key] = None + ''' + + return proc_data + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + + Returns: + + Dictionary. Raw or processed structured data. + """ + if not quiet: + jc.utils.compatibility(__name__, info.compatible) + + raw_output = {} + + for line in filter(None, data.splitlines()): + linedata = line.split(':', maxsplit=1) + raw_output[linedata[0].strip().lower().replace(' ', '_')] = linedata[1].strip() + + if raw: + return raw_output + else: + return process(raw_output) diff --git a/tests/fixtures/osx-10.14.6/airport-I.out b/tests/fixtures/osx-10.14.6/airport-I.out new file mode 100644 index 00000000..c2dad159 --- /dev/null +++ b/tests/fixtures/osx-10.14.6/airport-I.out @@ -0,0 +1,15 @@ + agrCtlRSSI: -66 + agrExtRSSI: 0 + agrCtlNoise: -90 + agrExtNoise: 0 + state: running + op mode: station + lastTxRate: 195 + maxRate: 867 +lastAssocStatus: 0 + 802.11 auth: open + link auth: wpa2-psk + BSSID: 3c:37:86:15:ad:f9 + SSID: BrazzleDazzle + MCS: 0 + channel: 48,80 diff --git a/tests/fixtures/osx-10.14.6/airport-s.out b/tests/fixtures/osx-10.14.6/airport-s.out new file mode 100644 index 00000000..1f0254ce --- /dev/null +++ b/tests/fixtures/osx-10.14.6/airport-s.out @@ -0,0 +1,15 @@ + SSID BSSID RSSI CHANNEL HT CC SECURITY (auth/unicast/group) + DIRECT-4A-HP OfficeJet 3830 00:68:eb:2b:b7:3b -91 6 Y -- WPA2(PSK/AES/AES) + xfinitywifi 6e:e3:0e:c1:74:df -87 36 Y US NONE + XFINITY 8e:e3:0e:c1:74:df -86 36 Y US WPA2(802.1x/AES/AES) + YouAreInfected-5 5c:e3:0e:c1:74:df -86 36 Y US WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP) + xfinitywifi 6e:e3:0e:b8:55:8f -85 11 Y US NONE + xfinitywifi 6e:e3:0e:c1:74:de -79 6 Y US NONE + ngHub_319451N814D6D 04:a1:51:56:5c:eb -77 11 Y US WPA2(PSK/AES/AES) + YouAreInfected 5c:e3:0e:c1:74:de -76 6 Y US WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP) + YuanFamily_Extender2G a0:04:60:86:11:89 -76 11 Y -- WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP) + YuanFamily 5c:e3:0e:b8:15:9f -73 11 Y US WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP) + SnazzleDazzle 3c:37:86:15:fd:b5 -70 48 Y US WPA2(PSK/AES/AES) + SnazzleDazzle 42:37:86:15:fd:b3 -62 3,+1 Y US WPA2(PSK/AES/AES) + SnazzleDazzle 42:37:86:15:aa:f7 -54 3 Y US WPA2(PSK/AES/AES) + SnazzleDazzle 3c:37:86:15:aa:f9 -64 48 Y US WPA2(PSK/AES/AES) From 52494321fcfe0dc7ee71d8d78210b3c10372a237 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 20:55:07 -0700 Subject: [PATCH 09/21] fixes and docs for airport parser issue #46 --- README.md | 22 ++++++++ docs/parsers/airport.md | 48 ++++++++++++++++-- jc/parsers/airport.py | 62 +++++++++++++++++++---- tests/fixtures/osx-10.14.6/airport-I.json | 1 + tests/fixtures/osx-10.14.6/airport-I.out | 2 +- 5 files changed, 120 insertions(+), 15 deletions(-) create mode 100644 tests/fixtures/osx-10.14.6/airport-I.json diff --git a/README.md b/README.md index c17a36d7..5525fbee 100755 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ jc [OPTIONS] COMMAND The JSON output can be compact (default) or pretty formatted with the `-p` option. ### Parsers +- `--airport` enables the `airport` command parser (OSX) - `--arp` enables the `arp` command parser - `--blkid` enables the `blkid` command parser - `--crontab` enables the `crontab` command and file parser @@ -164,6 +165,27 @@ Tested on: - Excellent constructive feedback from Ilya Sher (https://github.com/ilyash-b) ## Examples +### airport +``` +$ airport | jc --airport -p +{ + "agrctlrssi": -66, + "agrextrssi": 0, + "agrctlnoise": -90, + "agrextnoise": 0, + "state": "running", + "op_mode": "station", + "lasttxrate": 195, + "maxrate": 867, + "lastassocstatus": 0, + "802_11_auth": "open", + "link_auth": "wpa2-psk", + "bssid": "3c:37:86:15:ad:f9", + "ssid": "SnazzleDazzle", + "mcs": 0, + "channel": "48,80" +} +``` ### arp ``` $ arp | jc --arp -p # or: jc -p arp diff --git a/docs/parsers/airport.md b/docs/parsers/airport.md index 08e848f7..81e27be0 100644 --- a/docs/parsers/airport.md +++ b/docs/parsers/airport.md @@ -13,12 +13,40 @@ Examples: $ airport | jc --airport -p { - + "agrctlrssi": -66, + "agrextrssi": 0, + "agrctlnoise": -90, + "agrextnoise": 0, + "state": "running", + "op_mode": "station", + "lasttxrate": 195, + "maxrate": 867, + "lastassocstatus": 0, + "802_11_auth": "open", + "link_auth": "wpa2-psk", + "bssid": "3c:37:86:15:ad:f9", + "ssid": "SnazzleDazzle", + "mcs": 0, + "channel": "48,80" } $ airport | jc --airport -p -r { - + "agrctlrssi": "-66", + "agrextrssi": "0", + "agrctlnoise": "-90", + "agrextnoise": "0", + "state": "running", + "op_mode": "station", + "lasttxrate": "195", + "maxrate": "867", + "lastassocstatus": "0", + "802_11_auth": "open", + "link_auth": "wpa2-psk", + "bssid": "3c:37:86:15:ad:f9", + "ssid": "SnazzleDazzle", + "mcs": "0", + "channel": "48,80" } ## info @@ -42,7 +70,21 @@ Returns: Dictionary. Structured data with the following schema: { - + "agrctlrssi": integer, + "agrextrssi": integer, + "agrctlnoise": integer, + "agrextnoise": integer, + "state": string, + "op_mode": string, + "lasttxrate": integer, + "maxrate": integer, + "lastassocstatus": integer, + "802_11_auth": string, + "link_auth": string, + "bssid": string, + "ssid": string, + "mcs": integer, + "channel": string } ## parse diff --git a/jc/parsers/airport.py b/jc/parsers/airport.py index c8968efe..bffa01ba 100644 --- a/jc/parsers/airport.py +++ b/jc/parsers/airport.py @@ -12,12 +12,40 @@ Examples: $ airport | jc --airport -p { - + "agrctlrssi": -66, + "agrextrssi": 0, + "agrctlnoise": -90, + "agrextnoise": 0, + "state": "running", + "op_mode": "station", + "lasttxrate": 195, + "maxrate": 867, + "lastassocstatus": 0, + "802_11_auth": "open", + "link_auth": "wpa2-psk", + "bssid": "3c:37:86:15:ad:f9", + "ssid": "SnazzleDazzle", + "mcs": 0, + "channel": "48,80" } $ airport | jc --airport -p -r { - + "agrctlrssi": "-66", + "agrextrssi": "0", + "agrctlnoise": "-90", + "agrextnoise": "0", + "state": "running", + "op_mode": "station", + "lasttxrate": "195", + "maxrate": "867", + "lastassocstatus": "0", + "802_11_auth": "open", + "link_auth": "wpa2-psk", + "bssid": "3c:37:86:15:ad:f9", + "ssid": "SnazzleDazzle", + "mcs": "0", + "channel": "48,80" } """ import jc.utils @@ -51,20 +79,32 @@ def process(proc_data): Dictionary. Structured data with the following schema: { - + "agrctlrssi": integer, + "agrextrssi": integer, + "agrctlnoise": integer, + "agrextnoise": integer, + "state": string, + "op_mode": string, + "lasttxrate": integer, + "maxrate": integer, + "lastassocstatus": integer, + "802_11_auth": string, + "link_auth": string, + "bssid": string, + "ssid": string, + "mcs": integer, + "channel": string } """ - # boolean changes - ''' - bool_list = ['ntp_enabled', 'ntp_synchronized', 'rtc_in_local_tz', 'dst_active', - 'system_clock_synchronized', 'systemd-timesyncd.service_active'] + # integer changes + int_list = ['agrctlrssi', 'agrextrssi', 'agrctlnoise', 'agrextnoise', + 'lasttxrate', 'maxrate', 'lastassocstatus', 'mcs'] for key in proc_data: - if key in bool_list: + if key in int_list: try: - proc_data[key] = True if proc_data[key] == 'yes' else False + proc_data[key] = int(proc_data[key]) except (ValueError): proc_data[key] = None - ''' return proc_data @@ -90,7 +130,7 @@ def parse(data, raw=False, quiet=False): for line in filter(None, data.splitlines()): linedata = line.split(':', maxsplit=1) - raw_output[linedata[0].strip().lower().replace(' ', '_')] = linedata[1].strip() + raw_output[linedata[0].strip().lower().replace(' ', '_').replace('.', '_')] = linedata[1].strip() if raw: return raw_output diff --git a/tests/fixtures/osx-10.14.6/airport-I.json b/tests/fixtures/osx-10.14.6/airport-I.json new file mode 100644 index 00000000..f0155ef1 --- /dev/null +++ b/tests/fixtures/osx-10.14.6/airport-I.json @@ -0,0 +1 @@ +{"agrctlrssi": -66, "agrextrssi": 0, "agrctlnoise": -90, "agrextnoise": 0, "state": "running", "op_mode": "station", "lasttxrate": 195, "maxrate": 867, "lastassocstatus": 0, "802_11_auth": "open", "link_auth": "wpa2-psk", "bssid": "3c:37:86:15:ad:f9", "ssid": "SnazzleDazzle", "mcs": 0, "channel": "48,80"} diff --git a/tests/fixtures/osx-10.14.6/airport-I.out b/tests/fixtures/osx-10.14.6/airport-I.out index c2dad159..e6eeb569 100644 --- a/tests/fixtures/osx-10.14.6/airport-I.out +++ b/tests/fixtures/osx-10.14.6/airport-I.out @@ -10,6 +10,6 @@ lastAssocStatus: 0 802.11 auth: open link auth: wpa2-psk BSSID: 3c:37:86:15:ad:f9 - SSID: BrazzleDazzle + SSID: SnazzleDazzle MCS: 0 channel: 48,80 From 553bfbe1a0dd866851ba7eca66e9295c2097ad86 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 21:02:17 -0700 Subject: [PATCH 10/21] tests passing for airport -I. Issue #46 --- docs/parsers/airport.md | 9 ++++++--- jc/parsers/airport.py | 9 ++++++--- tests/test_airport.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 tests/test_airport.py diff --git a/docs/parsers/airport.md b/docs/parsers/airport.md index 81e27be0..ccd89474 100644 --- a/docs/parsers/airport.md +++ b/docs/parsers/airport.md @@ -3,7 +3,10 @@ jc - JSON CLI output utility airport Parser Usage: - specify --airport as the first argument if the piped input is coming from airport + specify --airport as the first argument if the piped input is coming from airport (OSX) + + This program can be found at: + /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport Compatibility: @@ -11,7 +14,7 @@ Compatibility: Examples: - $ airport | jc --airport -p + $ airport -I | jc --airport -p { "agrctlrssi": -66, "agrextrssi": 0, @@ -30,7 +33,7 @@ Examples: "channel": "48,80" } - $ airport | jc --airport -p -r + $ airport -I | jc --airport -p -r { "agrctlrssi": "-66", "agrextrssi": "0", diff --git a/jc/parsers/airport.py b/jc/parsers/airport.py index bffa01ba..5ea938e6 100644 --- a/jc/parsers/airport.py +++ b/jc/parsers/airport.py @@ -2,7 +2,10 @@ Usage: - specify --airport as the first argument if the piped input is coming from airport + specify --airport as the first argument if the piped input is coming from airport (OSX) + + This program can be found at: + /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport Compatibility: @@ -10,7 +13,7 @@ Compatibility: Examples: - $ airport | jc --airport -p + $ airport -I | jc --airport -p { "agrctlrssi": -66, "agrextrssi": 0, @@ -29,7 +32,7 @@ Examples: "channel": "48,80" } - $ airport | jc --airport -p -r + $ airport -I | jc --airport -p -r { "agrctlrssi": "-66", "agrextrssi": "0", diff --git a/tests/test_airport.py b/tests/test_airport.py new file mode 100644 index 00000000..91907515 --- /dev/null +++ b/tests/test_airport.py @@ -0,0 +1,28 @@ +import os +import unittest +import json +import jc.parsers.airport + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + + def setUp(self): + # input + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/airport-I.out'), 'r', encoding='utf-8') as f: + self.osx_10_14_6_airport_I = f.read() + + # output + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/airport-I.json'), 'r', encoding='utf-8') as f: + self.osx_10_14_6_airport_I_json = json.loads(f.read()) + + def test_airport_I_osx_10_14_6(self): + """ + Test 'airport -I' on OSX 10.14.6 + """ + self.assertEqual(jc.parsers.airport.parse(self.osx_10_14_6_airport_I, quiet=True), self.osx_10_14_6_airport_I_json) + + +if __name__ == '__main__': + unittest.main() From 12a370deed03ba42d7b7ebb410195ce5f32bb9c2 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 21:51:02 -0700 Subject: [PATCH 11/21] add airport -s parser for issue #46 --- README.md | 47 +++++++++- changelog.txt | 2 +- docgen.sh | 1 + docs/parsers/airport.md | 4 +- docs/parsers/airport_s.md | 136 ++++++++++++++++++++++++++++ jc/cli.py | 1 + jc/parsers/airport.py | 5 +- jc/parsers/airport_s.py | 185 ++++++++++++++++++++++++++++++++++++++ 8 files changed, 372 insertions(+), 9 deletions(-) create mode 100644 docs/parsers/airport_s.md create mode 100644 jc/parsers/airport_s.py diff --git a/README.md b/README.md index 5525fbee..3bf6c83c 100755 --- a/README.md +++ b/README.md @@ -82,7 +82,8 @@ jc [OPTIONS] COMMAND The JSON output can be compact (default) or pretty formatted with the `-p` option. ### Parsers -- `--airport` enables the `airport` command parser (OSX) +- `--airport` enables the `airport -I` command parser (OSX) +- `--airport-s` enables the `airport -s` command parser (OSX) - `--arp` enables the `arp` command parser - `--blkid` enables the `blkid` command parser - `--crontab` enables the `crontab` command and file parser @@ -165,9 +166,9 @@ Tested on: - Excellent constructive feedback from Ilya Sher (https://github.com/ilyash-b) ## Examples -### airport +### airport -I ``` -$ airport | jc --airport -p +$ airport -I | jc --airport -p { "agrctlrssi": -66, "agrextrssi": 0, @@ -186,6 +187,46 @@ $ airport | jc --airport -p "channel": "48,80" } ``` +### airport -s +``` +$ airport -s | jc --airport-s -p +[ + { + "ssid": "DIRECT-4A-HP OfficeJet 3830", + "bssid": "00:67:eb:2a:a7:3b", + "rssi": -90, + "channel": "6", + "ht": true, + "cc": "--", + "security": [ + "WPA2(PSK/AES/AES)" + ] + }, + { + "ssid": "Latitude38", + "bssid": "c0:ff:d5:d2:7a:f3", + "rssi": -85, + "channel": "11", + "ht": true, + "cc": "US", + "security": [ + "WPA2(PSK/AES/AES)" + ] + }, + { + "ssid": "xfinitywifi", + "bssid": "6e:e3:0e:b8:45:99", + "rssi": -83, + "channel": "11", + "ht": true, + "cc": "US", + "security": [ + "NONE" + ] + }, + ... +] +``` ### arp ``` $ arp | jc --arp -p # or: jc -p arp diff --git a/changelog.txt b/changelog.txt index 35bd791d..b5e92fa0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -3,7 +3,7 @@ jc changelog 20200xxxx vX.X.X - Added ntpq command parser - Added timedatectl status command parser -- Added airport command parser +- Added airport -I and airport -s command parser 20200308 v1.8.1 - CLI and history parser optimizations by https://github.com/philippeitis diff --git a/docgen.sh b/docgen.sh index 462f76eb..cb682684 100755 --- a/docgen.sh +++ b/docgen.sh @@ -5,6 +5,7 @@ cd jc pydocmd simple jc+ > ../docs/readme.md pydocmd simple utils+ > ../docs/utils.md pydocmd simple jc.parsers.airport+ > ../docs/parsers/airport.md +pydocmd simple jc.parsers.airport_s+ > ../docs/parsers/airport_s.md pydocmd simple jc.parsers.arp+ > ../docs/parsers/arp.md pydocmd simple jc.parsers.blkid+ > ../docs/parsers/blkid.md pydocmd simple jc.parsers.crontab+ > ../docs/parsers/crontab.md diff --git a/docs/parsers/airport.md b/docs/parsers/airport.md index ccd89474..3735793d 100644 --- a/docs/parsers/airport.md +++ b/docs/parsers/airport.md @@ -1,9 +1,9 @@ # jc.parsers.airport -jc - JSON CLI output utility airport Parser +jc - JSON CLI output utility airport -I Parser Usage: - specify --airport as the first argument if the piped input is coming from airport (OSX) + specify --airport as the first argument if the piped input is coming from airport -I (OSX) This program can be found at: /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport diff --git a/docs/parsers/airport_s.md b/docs/parsers/airport_s.md new file mode 100644 index 00000000..992fc78d --- /dev/null +++ b/docs/parsers/airport_s.md @@ -0,0 +1,136 @@ +# jc.parsers.airport_s +jc - JSON CLI output utility airport -s Parser + +Usage: + + specify --airport as the first argument if the piped input is coming from airport -s (OSX) + + This program can be found at: + /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport + +Compatibility: + + 'darwin' + +Examples: + + $ airport -s | jc --airport-s -p + [ + { + "ssid": "DIRECT-4A-HP OfficeJet 3830", + "bssid": "00:67:eb:2a:a7:3b", + "rssi": -90, + "channel": "6", + "ht": true, + "cc": "--", + "security": [ + "WPA2(PSK/AES/AES)" + ] + }, + { + "ssid": "Latitude38", + "bssid": "c0:ff:d5:d2:7a:f3", + "rssi": -85, + "channel": "11", + "ht": true, + "cc": "US", + "security": [ + "WPA2(PSK/AES/AES)" + ] + }, + { + "ssid": "xfinitywifi", + "bssid": "6e:e3:0e:b8:45:99", + "rssi": -83, + "channel": "11", + "ht": true, + "cc": "US", + "security": [ + "NONE" + ] + }, + ... + ] + + $ airport -s | jc --airport -p -r + [ + { + "ssid": "DIRECT-F3-HP ENVY 5660 series", + "bssid": "b0:5a:da:6f:0a:d4", + "rssi": "-93", + "channel": "1", + "ht": "Y", + "cc": "--", + "security": "WPA2(PSK/AES/AES)" + }, + { + "ssid": "YouAreInfected-5", + "bssid": "5c:e3:0e:c2:85:da", + "rssi": "-85", + "channel": "36", + "ht": "Y", + "cc": "US", + "security": "WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP)" + }, + { + "ssid": "YuanFamily", + "bssid": "5c:e3:0e:b8:5f:9a", + "rssi": "-84", + "channel": "11", + "ht": "Y", + "cc": "US", + "security": "WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP)" + }, + ... + ] + +## info +```python +info(self, /, *args, **kwargs) +``` + +## process +```python +process(proc_data) +``` + +Final processing to conform to the schema. + +Parameters: + + proc_data: (dictionary) raw structured data to process + +Returns: + + List of dictionaries. Structured data with the following schema: + [ + { + "ssid": string, + "bssid": string, + "rssi": integer, + "channel": string, + "ht": boolean, + "cc": string, + "security": [ + string, + ] + } + ] + +## parse +```python +parse(data, raw=False, quiet=False) +``` + +Main text parsing function + +Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + +Returns: + + List of dictionaries. Raw or processed structured data. + diff --git a/jc/cli.py b/jc/cli.py index c4a1e326..f996563d 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -23,6 +23,7 @@ __version__ = info.version parsers = [ 'airport', + 'airport-s', 'arp', 'blkid', 'crontab', diff --git a/jc/parsers/airport.py b/jc/parsers/airport.py index 5ea938e6..5a21a689 100644 --- a/jc/parsers/airport.py +++ b/jc/parsers/airport.py @@ -1,8 +1,8 @@ -"""jc - JSON CLI output utility airport Parser +"""jc - JSON CLI output utility airport -I Parser Usage: - specify --airport as the first argument if the piped input is coming from airport (OSX) + specify --airport as the first argument if the piped input is coming from airport -I (OSX) This program can be found at: /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport @@ -63,7 +63,6 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['darwin'] - magic_commands = ['airport'] __version__ = info.version diff --git a/jc/parsers/airport_s.py b/jc/parsers/airport_s.py new file mode 100644 index 00000000..36ada5c4 --- /dev/null +++ b/jc/parsers/airport_s.py @@ -0,0 +1,185 @@ +"""jc - JSON CLI output utility airport -s Parser + +Usage: + + specify --airport as the first argument if the piped input is coming from airport -s (OSX) + + This program can be found at: + /System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport + +Compatibility: + + 'darwin' + +Examples: + + $ airport -s | jc --airport-s -p + [ + { + "ssid": "DIRECT-4A-HP OfficeJet 3830", + "bssid": "00:67:eb:2a:a7:3b", + "rssi": -90, + "channel": "6", + "ht": true, + "cc": "--", + "security": [ + "WPA2(PSK/AES/AES)" + ] + }, + { + "ssid": "Latitude38", + "bssid": "c0:ff:d5:d2:7a:f3", + "rssi": -85, + "channel": "11", + "ht": true, + "cc": "US", + "security": [ + "WPA2(PSK/AES/AES)" + ] + }, + { + "ssid": "xfinitywifi", + "bssid": "6e:e3:0e:b8:45:99", + "rssi": -83, + "channel": "11", + "ht": true, + "cc": "US", + "security": [ + "NONE" + ] + }, + ... + ] + + $ airport -s | jc --airport -p -r + [ + { + "ssid": "DIRECT-F3-HP ENVY 5660 series", + "bssid": "b0:5a:da:6f:0a:d4", + "rssi": "-93", + "channel": "1", + "ht": "Y", + "cc": "--", + "security": "WPA2(PSK/AES/AES)" + }, + { + "ssid": "YouAreInfected-5", + "bssid": "5c:e3:0e:c2:85:da", + "rssi": "-85", + "channel": "36", + "ht": "Y", + "cc": "US", + "security": "WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP)" + }, + { + "ssid": "YuanFamily", + "bssid": "5c:e3:0e:b8:5f:9a", + "rssi": "-84", + "channel": "11", + "ht": "Y", + "cc": "US", + "security": "WPA(PSK/AES,TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP)" + }, + ... + ] +""" +import jc.utils +import jc.parsers.universal + + +class info(): + version = '1.0' + description = 'airport -s command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + # details = 'enter any other details here' + + # compatible options: linux, darwin, cygwin, win32, aix, freebsd + compatible = ['darwin'] + + +__version__ = info.version + + +def process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (dictionary) raw structured data to process + + Returns: + + List of dictionaries. Structured data with the following schema: + [ + { + "ssid": string, + "bssid": string, + "rssi": integer, + "channel": string, + "ht": boolean, + "cc": string, + "security": [ + string, + ] + } + ] + """ + for entry in proc_data: + + # integers + int_list = ['rssi'] + for key in int_list: + if key in entry: + try: + entry[key] = int(entry[key]) + except (ValueError): + entry[key] = None + + # booleans + bool_list = ['ht'] + for key in entry: + if key in bool_list: + try: + entry[key] = True if entry[key] == 'Y' else False + except (ValueError): + entry[key] = None + + if 'security' in entry: + entry['security'] = entry['security'].split() + + return proc_data + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of dictionaries. Raw or processed structured data. + """ + if not quiet: + jc.utils.compatibility(__name__, info.compatible) + + cleandata = data.splitlines() + + # fix headers + cleandata[0] = cleandata[0].lower() + cleandata[0] = cleandata[0].replace('-', '_') + cleandata[0] = cleandata[0].replace('security (auth/unicast/group)', 'security') + + # parse the data + raw_output = jc.parsers.universal.sparse_table_parse(cleandata) + + if raw: + return raw_output + else: + return process(raw_output) From 798e6bb7d939176bb36771a6d41bd55403d583be Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 22:03:44 -0700 Subject: [PATCH 12/21] tests passing for airport -s. issue #46 --- tests/fixtures/osx-10.14.6/airport-s.json | 1 + tests/test_airport_s.py | 28 +++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/fixtures/osx-10.14.6/airport-s.json create mode 100644 tests/test_airport_s.py diff --git a/tests/fixtures/osx-10.14.6/airport-s.json b/tests/fixtures/osx-10.14.6/airport-s.json new file mode 100644 index 00000000..5a6a49c2 --- /dev/null +++ b/tests/fixtures/osx-10.14.6/airport-s.json @@ -0,0 +1 @@ +[{"ssid": "DIRECT-4A-HP OfficeJet 3830", "bssid": "00:68:eb:2b:b7:3b", "rssi": -91, "channel": "6", "ht": true, "cc": "--", "security": ["WPA2(PSK/AES/AES)"]}, {"ssid": "xfinitywifi", "bssid": "6e:e3:0e:c1:74:df", "rssi": -87, "channel": "36", "ht": true, "cc": "US", "security": ["NONE"]}, {"ssid": "XFINITY", "bssid": "8e:e3:0e:c1:74:df", "rssi": -86, "channel": "36", "ht": true, "cc": "US", "security": ["WPA2(802.1x/AES/AES)"]}, {"ssid": "YouAreInfected-5", "bssid": "5c:e3:0e:c1:74:df", "rssi": -86, "channel": "36", "ht": true, "cc": "US", "security": ["WPA(PSK/AES,TKIP/TKIP)", "WPA2(PSK/AES,TKIP/TKIP)"]}, {"ssid": "xfinitywifi", "bssid": "6e:e3:0e:b8:55:8f", "rssi": -85, "channel": "11", "ht": true, "cc": "US", "security": ["NONE"]}, {"ssid": "xfinitywifi", "bssid": "6e:e3:0e:c1:74:de", "rssi": -79, "channel": "6", "ht": true, "cc": "US", "security": ["NONE"]}, {"ssid": "ngHub_319451N814D6D", "bssid": "04:a1:51:56:5c:eb", "rssi": -77, "channel": "11", "ht": true, "cc": "US", "security": ["WPA2(PSK/AES/AES)"]}, {"ssid": "YouAreInfected", "bssid": "5c:e3:0e:c1:74:de", "rssi": -76, "channel": "6", "ht": true, "cc": "US", "security": ["WPA(PSK/AES,TKIP/TKIP)", "WPA2(PSK/AES,TKIP/TKIP)"]}, {"ssid": "YuanFamily_Extender2G", "bssid": "a0:04:60:86:11:89", "rssi": -76, "channel": "11", "ht": true, "cc": "--", "security": ["WPA(PSK/AES,TKIP/TKIP)", "WPA2(PSK/AES,TKIP/TKIP)"]}, {"ssid": "YuanFamily", "bssid": "5c:e3:0e:b8:15:9f", "rssi": -73, "channel": "11", "ht": true, "cc": "US", "security": ["WPA(PSK/AES,TKIP/TKIP)", "WPA2(PSK/AES,TKIP/TKIP)"]}, {"ssid": "SnazzleDazzle", "bssid": "3c:37:86:15:fd:b5", "rssi": -70, "channel": "48", "ht": true, "cc": "US", "security": ["WPA2(PSK/AES/AES)"]}, {"ssid": "SnazzleDazzle", "bssid": "42:37:86:15:fd:b3", "rssi": -62, "channel": "3,+1", "ht": true, "cc": "US", "security": ["WPA2(PSK/AES/AES)"]}, {"ssid": "SnazzleDazzle", "bssid": "42:37:86:15:aa:f7", "rssi": -54, "channel": "3", "ht": true, "cc": "US", "security": ["WPA2(PSK/AES/AES)"]}, {"ssid": "SnazzleDazzle", "bssid": "3c:37:86:15:aa:f9", "rssi": -64, "channel": "48", "ht": true, "cc": "US", "security": ["WPA2(PSK/AES/AES)"]}] diff --git a/tests/test_airport_s.py b/tests/test_airport_s.py new file mode 100644 index 00000000..2b964f01 --- /dev/null +++ b/tests/test_airport_s.py @@ -0,0 +1,28 @@ +import os +import unittest +import json +import jc.parsers.airport_s + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + + def setUp(self): + # input + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/airport-s.out'), 'r', encoding='utf-8') as f: + self.osx_10_14_6_airport_s = f.read() + + # output + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/airport-s.json'), 'r', encoding='utf-8') as f: + self.osx_10_14_6_airport_s_json = json.loads(f.read()) + + def test_airport_s_osx_10_14_6(self): + """ + Test 'airport -s' on OSX 10.14.6 + """ + self.assertEqual(jc.parsers.airport_s.parse(self.osx_10_14_6_airport_s, quiet=True), self.osx_10_14_6_airport_s_json) + + +if __name__ == '__main__': + unittest.main() From e9b0bc1409a824a82dabd2479892b7d18bad8c3f Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 22:03:54 -0700 Subject: [PATCH 13/21] doc update --- jc/parsers/airport.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/parsers/airport.py b/jc/parsers/airport.py index 5a21a689..82eea4e0 100644 --- a/jc/parsers/airport.py +++ b/jc/parsers/airport.py @@ -56,7 +56,7 @@ import jc.utils class info(): version = '1.0' - description = 'airport command parser' + description = 'airport -I command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' # details = 'enter any other details here' From 40c05346f4098f8eea14c42da07df3faa143587a Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 22:16:40 -0700 Subject: [PATCH 14/21] re-adding optimizations from https://github.com/philippeitis --- jc/parsers/history.py | 37 ++++++++++++------------------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/jc/parsers/history.py b/jc/parsers/history.py index 2624a520..d6ae4eca 100644 --- a/jc/parsers/history.py +++ b/jc/parsers/history.py @@ -80,21 +80,11 @@ def process(proc_data): # rebuild output for added semantic information processed = [] for k, v in proc_data.items(): - proc_line = {} - proc_line['line'] = k - proc_line['command'] = v + proc_line = { + 'line': int(k) if k.isdigit() else None, + 'command': v, + } processed.append(proc_line) - - for entry in processed: - int_list = ['line'] - for key in int_list: - if key in entry: - try: - key_int = int(entry[key]) - entry[key] = key_int - except (ValueError): - entry[key] = None - return processed @@ -121,17 +111,14 @@ def parse(data, raw=False, quiet=False): # split lines and clear out any non-ascii chars linedata = data.encode('ascii', errors='ignore').decode().splitlines() - # Clear any blank lines - cleandata = list(filter(None, linedata)) - - if cleandata: - for entry in cleandata: - try: - parsed_line = entry.split(maxsplit=1) - raw_output[parsed_line[0]] = parsed_line[1] - except IndexError: - # need to catch indexerror in case there is weird input from prior commands - pass + # Skip any blank lines + for entry in filter(None, linedata): + try: + parsed_line = entry.split(maxsplit=1) + raw_output[parsed_line[0]] = parsed_line[1] + except IndexError: + # need to catch indexerror in case there is weird input from prior commands + pass if raw: return raw_output From 64d78956eb33ca0a2564fded3d12729ee036a915 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 10 Mar 2020 22:18:26 -0700 Subject: [PATCH 15/21] update acknowledgment --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 3bf6c83c..b9c39371 100755 --- a/README.md +++ b/README.md @@ -159,6 +159,7 @@ Tested on: - OSX 10.14.6 ## Acknowledgments +- CI automation and code optimizations from https://github.com/philippeitis - `ifconfig-parser` module from https://github.com/KnightWhoSayNi/ifconfig-parser - `xmltodict` module from https://github.com/martinblech/xmltodict by Martín Blech - `ruamel.yaml` library from https://pypi.org/project/ruamel.yaml by Anthon van der Neut From 970493ab9346a344b21be7614903ad81bc65a6e9 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Wed, 11 Mar 2020 06:22:54 -0700 Subject: [PATCH 16/21] add magic commands --- jc/parsers/airport.py | 1 + jc/parsers/airport_s.py | 1 + 2 files changed, 2 insertions(+) diff --git a/jc/parsers/airport.py b/jc/parsers/airport.py index 82eea4e0..ecc582e2 100644 --- a/jc/parsers/airport.py +++ b/jc/parsers/airport.py @@ -63,6 +63,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['darwin'] + magic_commands = ['airport -I'] __version__ = info.version diff --git a/jc/parsers/airport_s.py b/jc/parsers/airport_s.py index 36ada5c4..9f8e3e1b 100644 --- a/jc/parsers/airport_s.py +++ b/jc/parsers/airport_s.py @@ -96,6 +96,7 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['darwin'] + magic_commands = ['airport -s'] __version__ = info.version From 8e02e5c75a11cf205299ee6f87b67f9b787cf55e Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Wed, 11 Mar 2020 09:21:14 -0700 Subject: [PATCH 17/21] fix issue with getting options with some commands #47 --- jc/cli.py | 8 +++----- tests/test_cli.py | 3 +++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/jc/cli.py b/jc/cli.py index f996563d..c7bf174a 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -198,20 +198,18 @@ def generate_magic_command(args): return False, None # correctly parse escape characters and spaces with shlex - args_given = " ".join(map(shlex.quote, args[1:])).split() + args_given = ' '.join(map(shlex.quote, args[1:])).split() options = [] # find the options - popped = 0 - for i, arg in enumerate(args_given): + for arg in list(args_given): # parser found - use standard syntax if arg.startswith('--'): return False, None # option found - populate option list elif arg.startswith('-'): - options.append(args_given.pop(i - popped)[1:]) - popped += 1 + options.extend(args_given.pop(0)[1:]) # command found if iterator didn't already stop - stop iterating else: diff --git a/tests/test_cli.py b/tests/test_cli.py index fdd768da..d1a3a7d8 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -13,6 +13,9 @@ class MyTests(unittest.TestCase): 'jc -p pip3 show jc': 'pip3 show jc | jc --pip-show -p', 'jc -prd last': 'last | jc --last -prd', 'jc -prd lastb': 'lastb | jc --last -prd', + 'jc -p airport -I': 'airport -I | jc --airport -p', + 'jc -p -r airport -I': 'airport -I | jc --airport -pr', + 'jc -prd airport -I': 'airport -I | jc --airport -prd', 'jc -p nonexistent command': 'nonexistent command', 'jc -ap': None } From dfc96181159748d019419a2cba7aa9cb3b7a2a2c Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Wed, 11 Mar 2020 12:20:58 -0700 Subject: [PATCH 18/21] add file parser for issue #41 --- README.md | 36 ++++++++++++ changelog.txt | 5 +- docgen.sh | 1 + docs/parsers/file.md | 90 ++++++++++++++++++++++++++++++ jc/cli.py | 1 + jc/parsers/file.py | 128 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 260 insertions(+), 1 deletion(-) create mode 100644 docs/parsers/file.md create mode 100644 jc/parsers/file.py diff --git a/README.md b/README.md index b9c39371..3a21f068 100755 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ The JSON output can be compact (default) or pretty formatted with the `-p` optio - `--dig` enables the `dig` command parser - `--du` enables the `du` command parser - `--env` enables the `env` command parser +- `--file` enables the `file` command parser - `--free` enables the `free` command parser - `--fstab` enables the `/etc/fstab` file parser - `--group` enables the `/etc/group` file parser @@ -761,6 +762,41 @@ $ env | jc --env -p # or: jc -p env ... ] ``` +### file +``` +$ file * | jc --file -p +[ + { + "filename": "Applications", + "type": "directory" + }, + { + "filename": "another file with spaces", + "type": "empty" + }, + { + "filename": "argstest.py", + "type": "Python script text executable, ASCII text" + }, + { + "filename": "blkid-p.out", + "type": "ASCII text" + }, + { + "filename": "blkid-pi.out", + "type": "ASCII text, with very long lines" + }, + { + "filename": "cd_catalog.xml", + "type": "XML 1.0 document text, ASCII text, with CRLF line terminators" + }, + { + "filename": "centosserial.sh", + "type": "Bourne-Again shell script text executable, UTF-8 Unicode text" + }, + ... +] +``` ### free ``` $ free | jc --free -p # or: jc -p free diff --git a/changelog.txt b/changelog.txt index b5e92fa0..7aadae4a 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,9 +4,12 @@ jc changelog - Added ntpq command parser - Added timedatectl status command parser - Added airport -I and airport -s command parser +- Added file command parser +- Optimized history command parser by https://github.com/philippeitis +- Magic syntax fix for certain edge cases 20200308 v1.8.1 -- CLI and history parser optimizations by https://github.com/philippeitis +- CLI optimizations by https://github.com/philippeitis - Refactored magic syntax function and added tests (https://github.com/philippeitis) - Github actions for CI testing on multiple platforms by https://github.com/philippeitis - Updated ls parser to fix parsing error in OSX with -lR when there are empty folders diff --git a/docgen.sh b/docgen.sh index cb682684..f09c7f85 100755 --- a/docgen.sh +++ b/docgen.sh @@ -15,6 +15,7 @@ pydocmd simple jc.parsers.df+ > ../docs/parsers/df.md pydocmd simple jc.parsers.dig+ > ../docs/parsers/dig.md pydocmd simple jc.parsers.du+ > ../docs/parsers/du.md pydocmd simple jc.parsers.env+ > ../docs/parsers/env.md +pydocmd simple jc.parsers.file+ > ../docs/parsers/file.md pydocmd simple jc.parsers.free+ > ../docs/parsers/free.md pydocmd simple jc.parsers.fstab+ > ../docs/parsers/fstab.md pydocmd simple jc.parsers.group+ > ../docs/parsers/group.md diff --git a/docs/parsers/file.md b/docs/parsers/file.md new file mode 100644 index 00000000..f7e4e7b1 --- /dev/null +++ b/docs/parsers/file.md @@ -0,0 +1,90 @@ +# jc.parsers.file +jc - JSON CLI output utility file command Parser + +Usage: + + specify --file as the first argument if the piped input is coming from file. + +Compatibility: + + 'linux', 'aix', 'freebsd', 'darwin' + +Examples: + + $ file * | jc --file -p + [ + { + "filename": "Applications", + "type": "directory" + }, + { + "filename": "another file with spaces", + "type": "empty" + }, + { + "filename": "argstest.py", + "type": "Python script text executable, ASCII text" + }, + { + "filename": "blkid-p.out", + "type": "ASCII text" + }, + { + "filename": "blkid-pi.out", + "type": "ASCII text, with very long lines" + }, + { + "filename": "cd_catalog.xml", + "type": "XML 1.0 document text, ASCII text, with CRLF line terminators" + }, + { + "filename": "centosserial.sh", + "type": "Bourne-Again shell script text executable, UTF-8 Unicode text" + }, + ... + ] + +## info +```python +info(self, /, *args, **kwargs) +``` + +## process +```python +process(proc_data) +``` + +Final processing to conform to the schema. + +Parameters: + + proc_data: (dictionary) raw structured data to process + +Returns: + + List of dictionaries. Structured data with the following schema: + + [ + { + "filename": string, + "type ": string + } + ] + +## parse +```python +parse(data, raw=False, quiet=False) +``` + +Main text parsing function + +Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + +Returns: + + List of dictionaries. Raw or processed structured data. + diff --git a/jc/cli.py b/jc/cli.py index c7bf174a..e3cd308c 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -33,6 +33,7 @@ parsers = [ 'dig', 'du', 'env', + 'file', 'free', 'fstab', 'group', diff --git a/jc/parsers/file.py b/jc/parsers/file.py new file mode 100644 index 00000000..e72b4d5e --- /dev/null +++ b/jc/parsers/file.py @@ -0,0 +1,128 @@ +"""jc - JSON CLI output utility file command Parser + +Usage: + + specify --file as the first argument if the piped input is coming from file. + +Compatibility: + + 'linux', 'aix', 'freebsd', 'darwin' + +Examples: + + $ file * | jc --file -p + [ + { + "filename": "Applications", + "type": "directory" + }, + { + "filename": "another file with spaces", + "type": "empty" + }, + { + "filename": "argstest.py", + "type": "Python script text executable, ASCII text" + }, + { + "filename": "blkid-p.out", + "type": "ASCII text" + }, + { + "filename": "blkid-pi.out", + "type": "ASCII text, with very long lines" + }, + { + "filename": "cd_catalog.xml", + "type": "XML 1.0 document text, ASCII text, with CRLF line terminators" + }, + { + "filename": "centosserial.sh", + "type": "Bourne-Again shell script text executable, UTF-8 Unicode text" + }, + ... + ] +""" +import jc.utils +import jc.parsers.universal + + +class info(): + version = '1.0' + description = 'file command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + + # compatible options: linux, darwin, cygwin, win32, aix, freebsd + compatible = ['linux', 'aix', 'freebsd', 'darwin'] + magic_commands = ['file'] + + +__version__ = info.version + + +def process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (dictionary) raw structured data to process + + Returns: + + List of dictionaries. Structured data with the following schema: + + [ + { + "filename": string, + "type ": string + } + ] + """ + # No further processing + return proc_data + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of dictionaries. Raw or processed structured data. + """ + if not quiet: + jc.utils.compatibility(__name__, info.compatible) + + raw_output = [] + + warned = False + for line in filter(None, data.splitlines()): + linedata = line.rsplit(':', maxsplit=1) + + try: + filename = linedata[0].strip() + filetype = linedata[1].strip() + + raw_output.append( + { + 'filename': filename, + 'type': filetype + } + ) + except IndexError: + if not warned: + jc.utils.warning_message('Filenames with newline characters detected. Some filenames may be truncated.') + warned = True + + if raw: + return raw_output + else: + return process(raw_output) From 59f19d33a5c6677ea756a9424fdb032b430511a2 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Wed, 11 Mar 2020 12:39:59 -0700 Subject: [PATCH 19/21] add file command tests for #41 --- tests/fixtures/centos-7.7/file.json | 1 + tests/fixtures/centos-7.7/file.out | 25 +++++++++++++ tests/fixtures/osx-10.14.6/file.json | 1 + tests/fixtures/osx-10.14.6/file.out | 53 +++++++++++++++++++++++++++ tests/fixtures/ubuntu-18.04/file.json | 1 + tests/fixtures/ubuntu-18.04/file.out | 20 ++++++++++ tests/test_file.py | 52 ++++++++++++++++++++++++++ 7 files changed, 153 insertions(+) create mode 100644 tests/fixtures/centos-7.7/file.json create mode 100644 tests/fixtures/centos-7.7/file.out create mode 100644 tests/fixtures/osx-10.14.6/file.json create mode 100644 tests/fixtures/osx-10.14.6/file.out create mode 100644 tests/fixtures/ubuntu-18.04/file.json create mode 100644 tests/fixtures/ubuntu-18.04/file.out create mode 100644 tests/test_file.py diff --git a/tests/fixtures/centos-7.7/file.json b/tests/fixtures/centos-7.7/file.json new file mode 100644 index 00000000..914ca697 --- /dev/null +++ b/tests/fixtures/centos-7.7/file.json @@ -0,0 +1 @@ +[{"filename": "bin", "type": "directory"}, {"filename": "digout", "type": "ASCII text"}, {"filename": "file with spaces in the name", "type": "empty"}, {"filename": "git", "type": "directory"}, {"filename": "id-centos.out", "type": "ASCII text"}, {"filename": "ifcfg.json", "type": "ASCII text, with very long lines"}, {"filename": "ifconfig.out", "type": "ASCII text"}, {"filename": "iptables-tests", "type": "directory"}, {"filename": "journaljson", "type": "ASCII text, with very long lines"}, {"filename": "jp", "type": "ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped"}, {"filename": "jp_1.1.12_linux_x86_64.zip", "type": "Zip archive data, at least v2.0 to extract"}, {"filename": "lastb.out", "type": "ASCII text"}, {"filename": "lsblk-cols", "type": "UTF-8 Unicode text"}, {"filename": "psfile.txt", "type": "ASCII text, with very long lines"}, {"filename": "resizeterm.sh", "type": "Bourne-Again shell script, ASCII text executable"}, {"filename": "routeout", "type": "ASCII text"}, {"filename": "ss-aeep.out", "type": "ASCII text"}, {"filename": "ssout", "type": "ASCII text"}, {"filename": "systemctl.out", "type": "UTF-8 Unicode text"}, {"filename": "testfiles", "type": "directory"}, {"filename": "tmp", "type": "directory"}, {"filename": "top.out", "type": "ASCII text, with escape sequences"}, {"filename": "who-aH.out", "type": "ASCII text"}, {"filename": "who.out", "type": "ASCII text"}, {"filename": "whotext", "type": "ASCII text"}] diff --git a/tests/fixtures/centos-7.7/file.out b/tests/fixtures/centos-7.7/file.out new file mode 100644 index 00000000..4c615195 --- /dev/null +++ b/tests/fixtures/centos-7.7/file.out @@ -0,0 +1,25 @@ +bin: directory +digout: ASCII text +file with spaces in the name: empty +git: directory +id-centos.out: ASCII text +ifcfg.json: ASCII text, with very long lines +ifconfig.out: ASCII text +iptables-tests: directory +journaljson: ASCII text, with very long lines +jp: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped +jp_1.1.12_linux_x86_64.zip: Zip archive data, at least v2.0 to extract +lastb.out: ASCII text +lsblk-cols: UTF-8 Unicode text +psfile.txt: ASCII text, with very long lines +resizeterm.sh: Bourne-Again shell script, ASCII text executable +routeout: ASCII text +ss-aeep.out: ASCII text +ssout: ASCII text +systemctl.out: UTF-8 Unicode text +testfiles: directory +tmp: directory +top.out: ASCII text, with escape sequences +who-aH.out: ASCII text +who.out: ASCII text +whotext: ASCII text diff --git a/tests/fixtures/osx-10.14.6/file.json b/tests/fixtures/osx-10.14.6/file.json new file mode 100644 index 00000000..c7836537 --- /dev/null +++ b/tests/fixtures/osx-10.14.6/file.json @@ -0,0 +1 @@ +[{"filename": "Applications", "type": "directory"}, {"filename": "Desktop", "type": "directory"}, {"filename": "Documents", "type": "directory"}, {"filename": "Downloads", "type": "directory"}, {"filename": "Library", "type": "directory"}, {"filename": "Movies", "type": "directory"}, {"filename": "Music", "type": "directory"}, {"filename": "Pictures", "type": "directory"}, {"filename": "Postman", "type": "directory"}, {"filename": "Public", "type": "directory"}, {"filename": "Virtual Machines.localized", "type": "directory"}, {"filename": "another file with spaces", "type": "empty"}, {"filename": "ansible", "type": "directory"}, {"filename": "api", "type": "directory"}, {"filename": "argstest.py", "type": "Python script text executable, ASCII text"}, {"filename": "blkid-p.out", "type": "ASCII text"}, {"filename": "blkid-pi.out", "type": "ASCII text, with very long lines"}, {"filename": "blkid-ubuntu.out", "type": "ASCII text"}, {"filename": "blkid-udev-mult.out", "type": "ASCII text"}, {"filename": "blkid-udev.out", "type": "ASCII text"}, {"filename": "blkid.out", "type": "ASCII text"}, {"filename": "cd_catalog.xml", "type": "XML 1.0 document text, ASCII text, with CRLF line terminators"}, {"filename": "centosserial.sh", "type": "Bourne-Again shell script text executable, UTF-8 Unicode text"}, {"filename": "coreosserial.sh", "type": "Bourne-Again shell script text executable, UTF-8 Unicode text"}, {"filename": "fazserial.sh", "type": "Bourne-Again shell script text executable, UTF-8 Unicode text"}, {"filename": "fgt1serial.sh", "type": "Bourne-Again shell script text executable, UTF-8 Unicode text"}, {"filename": "fgt2serial.sh", "type": "Bourne-Again shell script text executable, UTF-8 Unicode text"}, {"filename": "file with spaces", "type": "empty"}, {"filename": "fortiweb-docker.sh", "type": "Bourne-Again shell script text executable, ASCII text"}, {"filename": "ftmgr.sh", "type": "Bourne-Again shell script text executable, ASCII text"}, {"filename": "git", "type": "directory"}, {"filename": "google-cloud-sdk", "type": "directory"}, {"filename": "ifconfiglines.json", "type": "ASCII text, with very long lines"}, {"filename": "jc-jq-jp-ps-cpu.gif", "type": "GIF image data, version 89a, 1572 x 1212"}, {"filename": "jc-jq-jp-uptime-small.gif", "type": "GIF image data, version 89a, 800 x 617"}, {"filename": "jc-jq-jp-uptime.gif", "type": "GIF image data, version 89a, 1572 x 1212"}, {"filename": "jp", "type": "Mach-O 64-bit executable x86_64"}, {"filename": "jp_1.1.12_osx_x86_64.zip", "type": "Zip archive data, at least v2.0 to extract"}, {"filename": "jupyter.sh", "type": "ASCII text"}, {"filename": "kb.sh", "type": "Bourne-Again shell script text executable, ASCII text"}, {"filename": "kbtesting.txt", "type": "ASCII text"}, {"filename": "kelly-aws.pem", "type": "PEM RSA private key"}, {"filename": "kellyargtest.py", "type": "Python script text executable, ASCII text"}, {"filename": "loadplot.sh", "type": "Bourne-Again shell script text executable, ASCII text"}, {"filename": "ls-R.out", "type": "UTF-8 Unicode text"}, {"filename": "ls-lR-empty-folder.out", "type": "ASCII text"}, {"filename": "ls-lR.out", "type": "UTF-8 Unicode text"}, {"filename": "myrecording", "type": "data"}, {"filename": "smcclient.jnlp", "type": "XML 1.0 document text, ASCII text"}, {"filename": "table.py", "type": "Python script text executable, ASCII text"}, {"filename": "test.ini", "type": "ASCII text"}, {"filename": "ubuntuserial.sh", "type": "Bourne-Again shell script text executable, UTF-8 Unicode text"}, {"filename": "utils", "type": "directory"}] diff --git a/tests/fixtures/osx-10.14.6/file.out b/tests/fixtures/osx-10.14.6/file.out new file mode 100644 index 00000000..7b3b78bb --- /dev/null +++ b/tests/fixtures/osx-10.14.6/file.out @@ -0,0 +1,53 @@ +Applications: directory +Desktop: directory +Documents: directory +Downloads: directory +Library: directory +Movies: directory +Music: directory +Pictures: directory +Postman: directory +Public: directory +Virtual Machines.localized: directory +another file with spaces: empty +ansible: directory +api: directory +argstest.py: Python script text executable, ASCII text +blkid-p.out: ASCII text +blkid-pi.out: ASCII text, with very long lines +blkid-ubuntu.out: ASCII text +blkid-udev-mult.out: ASCII text +blkid-udev.out: ASCII text +blkid.out: ASCII text +cd_catalog.xml: XML 1.0 document text, ASCII text, with CRLF line terminators +centosserial.sh: Bourne-Again shell script text executable, UTF-8 Unicode text +coreosserial.sh: Bourne-Again shell script text executable, UTF-8 Unicode text +fazserial.sh: Bourne-Again shell script text executable, UTF-8 Unicode text +fgt1serial.sh: Bourne-Again shell script text executable, UTF-8 Unicode text +fgt2serial.sh: Bourne-Again shell script text executable, UTF-8 Unicode text +file with spaces: empty +fortiweb-docker.sh: Bourne-Again shell script text executable, ASCII text +ftmgr.sh: Bourne-Again shell script text executable, ASCII text +git: directory +google-cloud-sdk: directory +ifconfiglines.json: ASCII text, with very long lines +jc-jq-jp-ps-cpu.gif: GIF image data, version 89a, 1572 x 1212 +jc-jq-jp-uptime-small.gif: GIF image data, version 89a, 800 x 617 +jc-jq-jp-uptime.gif: GIF image data, version 89a, 1572 x 1212 +jp: Mach-O 64-bit executable x86_64 +jp_1.1.12_osx_x86_64.zip: Zip archive data, at least v2.0 to extract +jupyter.sh: ASCII text +kb.sh: Bourne-Again shell script text executable, ASCII text +kbtesting.txt: ASCII text +kelly-aws.pem: PEM RSA private key +kellyargtest.py: Python script text executable, ASCII text +loadplot.sh: Bourne-Again shell script text executable, ASCII text +ls-R.out: UTF-8 Unicode text +ls-lR-empty-folder.out: ASCII text +ls-lR.out: UTF-8 Unicode text +myrecording: data +smcclient.jnlp: XML 1.0 document text, ASCII text +table.py: Python script text executable, ASCII text +test.ini: ASCII text +ubuntuserial.sh: Bourne-Again shell script text executable, UTF-8 Unicode text +utils: directory diff --git a/tests/fixtures/ubuntu-18.04/file.json b/tests/fixtures/ubuntu-18.04/file.json new file mode 100644 index 00000000..d4fc3678 --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/file.json @@ -0,0 +1 @@ +[{"filename": "crayaml.yaml", "type": "ASCII text"}, {"filename": "git", "type": "directory"}, {"filename": "ifconfig-ubuntu.out", "type": "ASCII text"}, {"filename": "iptables-tests", "type": "directory"}, {"filename": "lastb.out", "type": "ASCII text"}, {"filename": "listjobs", "type": "ASCII text"}, {"filename": "ls-lR.out", "type": "ASCII text"}, {"filename": "ls-R.out", "type": "ASCII text"}, {"filename": "netstat-ae.out", "type": "ASCII text"}, {"filename": "netstat-aew.out", "type": "ASCII text"}, {"filename": "netstat-a.out", "type": "ASCII text"}, {"filename": "netstat-aw.out", "type": "ASCII text"}, {"filename": "resizeterm.sh", "type": "Bourne-Again shell script, ASCII text executable"}, {"filename": "snap", "type": "directory"}, {"filename": "ssout", "type": "ASCII text"}, {"filename": "sudofix", "type": "ASCII text"}, {"filename": "testfiles", "type": "directory"}, {"filename": "tmp", "type": "directory"}, {"filename": "trafficgen.sh", "type": "Bourne-Again shell script, ASCII text executable"}, {"filename": "xmltest.xml", "type": "XML 1.0 document, ASCII text"}] diff --git a/tests/fixtures/ubuntu-18.04/file.out b/tests/fixtures/ubuntu-18.04/file.out new file mode 100644 index 00000000..865b6cab --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/file.out @@ -0,0 +1,20 @@ +crayaml.yaml: ASCII text +git: directory +ifconfig-ubuntu.out: ASCII text +iptables-tests: directory +lastb.out: ASCII text +listjobs: ASCII text +ls-lR.out: ASCII text +ls-R.out: ASCII text +netstat-ae.out: ASCII text +netstat-aew.out: ASCII text +netstat-a.out: ASCII text +netstat-aw.out: ASCII text +resizeterm.sh: Bourne-Again shell script, ASCII text executable +snap: directory +ssout: ASCII text +sudofix: ASCII text +testfiles: directory +tmp: directory +trafficgen.sh: Bourne-Again shell script, ASCII text executable +xmltest.xml: XML 1.0 document, ASCII text diff --git a/tests/test_file.py b/tests/test_file.py new file mode 100644 index 00000000..67181854 --- /dev/null +++ b/tests/test_file.py @@ -0,0 +1,52 @@ +import os +import unittest +import json +import jc.parsers.file + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + + def setUp(self): + # input + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/file.out'), 'r', encoding='utf-8') as f: + self.centos_7_7_file = f.read() + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/file.out'), 'r', encoding='utf-8') as f: + self.ubuntu_18_4_file = f.read() + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/file.out'), 'r', encoding='utf-8') as f: + self.osx_10_14_6_file = f.read() + + # output + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/file.json'), 'r', encoding='utf-8') as f: + self.centos_7_7_file_json = json.loads(f.read()) + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/file.json'), 'r', encoding='utf-8') as f: + self.ubuntu_18_4_file_json = json.loads(f.read()) + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/file.json'), 'r', encoding='utf-8') as f: + self.osx_10_14_6_file_json = json.loads(f.read()) + + def test_file_centos_7_7(self): + """ + Test 'file *' on Centos 7.7 + """ + self.assertEqual(jc.parsers.file.parse(self.centos_7_7_file, quiet=True), self.centos_7_7_file_json) + + def test_file_ubuntu_18_4(self): + """ + Test 'file *' on Ubuntu 18.4 + """ + self.assertEqual(jc.parsers.file.parse(self.ubuntu_18_4_file, quiet=True), self.ubuntu_18_4_file_json) + + def test_file_osx_10_14_6(self): + """ + Test 'file *' on OSX 10.14.6 + """ + self.assertEqual(jc.parsers.file.parse(self.osx_10_14_6_file, quiet=True), self.osx_10_14_6_file_json) + + +if __name__ == '__main__': + unittest.main() From fc8ab27361df3359b706125531b2643612d6996a Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Wed, 11 Mar 2020 13:24:55 -0700 Subject: [PATCH 20/21] bugfix for misaligned columns and additional test for ntpq #31 --- README.md | 8 ++-- docs/parsers/ntpq.md | 40 ++++++++-------- jc/parsers/ntpq.py | 59 +++++++++++++----------- tests/fixtures/ubuntu-18.04/ntpq-p2.json | 1 + tests/fixtures/ubuntu-18.04/ntpq-p2.out | 24 ++++++++++ tests/test_ntpq.py | 12 ++++- 6 files changed, 93 insertions(+), 51 deletions(-) create mode 100644 tests/fixtures/ubuntu-18.04/ntpq-p2.json create mode 100644 tests/fixtures/ubuntu-18.04/ntpq-p2.out diff --git a/README.md b/README.md index 3a21f068..8bdbe1b6 100755 --- a/README.md +++ b/README.md @@ -1591,7 +1591,6 @@ $ sudo netstat -apee | jc --netstat -p # or: sudo jc -p netstat -apee $ ntpq -p | jc --ntpq -p # or: jc -p ntpq -p [ { - "state": null, "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, @@ -1601,10 +1600,10 @@ $ ntpq -p | jc --ntpq -p # or: jc -p ntpq -p "reach": 1, "delay": 23.399, "offset": -2.805, - "jitter": 2.131 + "jitter": 2.131, + "state": null }, { - "state": null, "remote": "mirror1.sjc02.s", "refid": "216.218.254.202", "st": 2, @@ -1614,7 +1613,8 @@ $ ntpq -p | jc --ntpq -p # or: jc -p ntpq -p "reach": 1, "delay": 29.325, "offset": 1.044, - "jitter": 4.069 + "jitter": 4.069, + "state": null } ] ``` diff --git a/docs/parsers/ntpq.md b/docs/parsers/ntpq.md index 343da1f5..fdd565ca 100644 --- a/docs/parsers/ntpq.md +++ b/docs/parsers/ntpq.md @@ -14,7 +14,6 @@ Examples: $ ntpq -p | jc --ntpq -p [ { - "state": null, "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, @@ -24,10 +23,10 @@ Examples: "reach": 1, "delay": 23.399, "offset": -2.805, - "jitter": 2.131 + "jitter": 2.131, + "state": null }, { - "state": null, "remote": "ntp.wdc1.us.lea", "refid": "130.133.1.10", "st": 2, @@ -37,10 +36,10 @@ Examples: "reach": 1, "delay": 93.053, "offset": -0.807, - "jitter": 2.839 + "jitter": 2.839, + "state": null }, { - "state": null, "remote": "clock.team-cymr", "refid": "204.9.54.119", "st": 2, @@ -50,10 +49,10 @@ Examples: "reach": 1, "delay": 70.337, "offset": -2.909, - "jitter": 2.6 + "jitter": 2.6, + "state": null }, { - "state": null, "remote": "mirror1.sjc02.s", "refid": "216.218.254.202", "st": 2, @@ -63,14 +62,14 @@ Examples: "reach": 1, "delay": 29.325, "offset": 1.044, - "jitter": 4.069 + "jitter": 4.069, + "state": null, } ] $ ntpq -pn| jc --ntpq -p [ { - "state": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, @@ -80,10 +79,10 @@ Examples: "reach": 377, "delay": 22.69, "offset": -0.392, - "jitter": 2.085 + "jitter": 2.085, + "state": "+" }, { - "state": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": 2, @@ -93,10 +92,10 @@ Examples: "reach": 377, "delay": 90.805, "offset": 2.84, - "jitter": 1.908 + "jitter": 1.908, + "state": "-" }, { - "state": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": 2, @@ -106,10 +105,10 @@ Examples: "reach": 377, "delay": 68.699, "offset": -0.61, - "jitter": 2.576 + "jitter": 2.576, + "state": "+" }, { - "state": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": 2, @@ -119,14 +118,15 @@ Examples: "reach": 377, "delay": 22.654, "offset": 0.231, - "jitter": 1.964 + "jitter": 1.964, + "state": "*" } ] $ ntpq -pn| jc --ntpq -p -r [ { - "state": "+", + "s": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": "2", @@ -139,7 +139,7 @@ Examples: "jitter": "2.085" }, { - "state": "-", + "s": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": "2", @@ -152,7 +152,7 @@ Examples: "jitter": "1.908" }, { - "state": "+", + "s": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": "2", @@ -165,7 +165,7 @@ Examples: "jitter": "2.576" }, { - "state": "*", + "s": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": "2", diff --git a/jc/parsers/ntpq.py b/jc/parsers/ntpq.py index cb41b626..2292f5e4 100644 --- a/jc/parsers/ntpq.py +++ b/jc/parsers/ntpq.py @@ -13,7 +13,6 @@ Examples: $ ntpq -p | jc --ntpq -p [ { - "state": null, "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, @@ -23,10 +22,10 @@ Examples: "reach": 1, "delay": 23.399, "offset": -2.805, - "jitter": 2.131 + "jitter": 2.131, + "state": null }, { - "state": null, "remote": "ntp.wdc1.us.lea", "refid": "130.133.1.10", "st": 2, @@ -36,10 +35,10 @@ Examples: "reach": 1, "delay": 93.053, "offset": -0.807, - "jitter": 2.839 + "jitter": 2.839, + "state": null }, { - "state": null, "remote": "clock.team-cymr", "refid": "204.9.54.119", "st": 2, @@ -49,10 +48,10 @@ Examples: "reach": 1, "delay": 70.337, "offset": -2.909, - "jitter": 2.6 + "jitter": 2.6, + "state": null }, { - "state": null, "remote": "mirror1.sjc02.s", "refid": "216.218.254.202", "st": 2, @@ -62,14 +61,14 @@ Examples: "reach": 1, "delay": 29.325, "offset": 1.044, - "jitter": 4.069 + "jitter": 4.069, + "state": null, } ] $ ntpq -pn| jc --ntpq -p [ { - "state": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": 2, @@ -79,10 +78,10 @@ Examples: "reach": 377, "delay": 22.69, "offset": -0.392, - "jitter": 2.085 + "jitter": 2.085, + "state": "+" }, { - "state": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": 2, @@ -92,10 +91,10 @@ Examples: "reach": 377, "delay": 90.805, "offset": 2.84, - "jitter": 1.908 + "jitter": 1.908, + "state": "-" }, { - "state": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": 2, @@ -105,10 +104,10 @@ Examples: "reach": 377, "delay": 68.699, "offset": -0.61, - "jitter": 2.576 + "jitter": 2.576, + "state": "+" }, { - "state": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": 2, @@ -118,14 +117,15 @@ Examples: "reach": 377, "delay": 22.654, "offset": 0.231, - "jitter": 1.964 + "jitter": 1.964, + "state": "*" } ] $ ntpq -pn| jc --ntpq -p -r [ { - "state": "+", + "s": "+", "remote": "44.190.6.254", "refid": "127.67.113.92", "st": "2", @@ -138,7 +138,7 @@ Examples: "jitter": "2.085" }, { - "state": "-", + "s": "-", "remote": "108.59.2.24", "refid": "130.133.1.10", "st": "2", @@ -151,7 +151,7 @@ Examples: "jitter": "1.908" }, { - "state": "+", + "s": "+", "remote": "38.229.71.1", "refid": "204.9.54.119", "st": "2", @@ -164,7 +164,7 @@ Examples: "jitter": "2.576" }, { - "state": "*", + "s": "*", "remote": "72.5.72.15", "refid": "216.218.254.202", "st": "2", @@ -227,8 +227,10 @@ def process(proc_data): """ for entry in proc_data: - if entry['state'] == '~': - entry['state'] = None + if entry['s'] == '~': + entry['s'] = None + + entry['state'] = entry.pop('s') int_list = ['st', 'when', 'poll', 'reach'] for key in int_list: @@ -269,18 +271,23 @@ def parse(data, raw=False, quiet=False): raw_output = [] cleandata = data.splitlines() - cleandata[0] = 'state ' + cleandata[0] + cleandata[0] = 's ' + cleandata[0] cleandata[0] = cleandata[0].lower() # delete header delimiter del cleandata[1] # separate first character with a space for easier parsing - for i, line in enumerate(cleandata[1:]): + for i, line in list(enumerate(cleandata[1:])): if line[0] == ' ': - cleandata[i + 1] = '~ ' + line[1:] + # fixup for no-state + cleandata[i + 1] = '~ ' + line[1:] else: - cleandata[i + 1] = line[:1] + ' ' + line[1:] + # fixup - realign columns since we added the 's' column + cleandata[i + 1] = line[:1] + ' ' + line[1:] + + # fixup for occaisional ip/hostname fields with a space + cleandata[i + 1] = cleandata[i + 1].replace(' (', '_(') raw_output = jc.parsers.universal.simple_table_parse(cleandata) diff --git a/tests/fixtures/ubuntu-18.04/ntpq-p2.json b/tests/fixtures/ubuntu-18.04/ntpq-p2.json new file mode 100644 index 00000000..03ac3755 --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/ntpq-p2.json @@ -0,0 +1 @@ +[{"remote": "0.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0, "state": null}, {"remote": "1.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0, "state": null}, {"remote": "2.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0, "state": null}, {"remote": "3.ubuntu.pool.n", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0, "state": null}, {"remote": "ntp.ubuntu.com", "refid": ".POOL.", "st": 16, "t": "p", "when": null, "poll": 64, "reach": 0, "delay": 0.0, "offset": 0.0, "jitter": 0.0, "state": null}, {"remote": "45.79.36.123_(t", "refid": "216.218.254.202", "st": 2, "t": "u", "when": 11, "poll": 64, "reach": 7, "delay": 68.172, "offset": 4.404, "jitter": 4.878, "state": "+"}, {"remote": "199.188.64.12_(", "refid": "173.161.33.165", "st": 2, "t": "u", "when": 9, "poll": 64, "reach": 7, "delay": 78.823, "offset": 2.148, "jitter": 8.739, "state": "+"}, {"remote": "ntp2.wiktel.com", "refid": ".GPS.", "st": 1, "t": "u", "when": 25, "poll": 64, "reach": 7, "delay": 76.857, "offset": 0.473, "jitter": 7.89, "state": "*"}, {"remote": "atl0.jane.mattn", "refid": "35.73.197.144", "st": 2, "t": "u", "when": 32, "poll": 64, "reach": 7, "delay": 87.536, "offset": 8.641, "jitter": 4.544, "state": "#"}, {"remote": "x.ns.gin.ntt.ne", "refid": "249.224.99.213", "st": 2, "t": "u", "when": 28, "poll": 64, "reach": 7, "delay": 25.021, "offset": 3.361, "jitter": 7.793, "state": "+"}, {"remote": "srcf-ntp.stanfo", "refid": "171.64.7.105", "st": 2, "t": "u", "when": 26, "poll": 64, "reach": 7, "delay": 28.053, "offset": 7.253, "jitter": 4.524, "state": "-"}, {"remote": "linode.ibendit.", "refid": "64.250.105.227", "st": 2, "t": "u", "when": 28, "poll": 64, "reach": 7, "delay": 56.841, "offset": -0.305, "jitter": 8.279, "state": "#"}, {"remote": "71.66.197.233", "refid": ".GPS.", "st": 1, "t": "u", "when": 26, "poll": 64, "reach": 7, "delay": 127.766, "offset": -0.769, "jitter": 3.465, "state": "+"}, {"remote": "time.aja.com", "refid": "198.169.208.144", "st": 2, "t": "u", "when": 32, "poll": 64, "reach": 7, "delay": 39.673, "offset": 4.684, "jitter": 4.916, "state": "+"}, {"remote": "23.152.160.126", "refid": "84.168.88.243", "st": 2, "t": "u", "when": 28, "poll": 64, "reach": 7, "delay": 21.553, "offset": 3.245, "jitter": 8.612, "state": "+"}, {"remote": "ntp.backplanedn", "refid": "152.2.133.52", "st": 2, "t": "u", "when": 30, "poll": 64, "reach": 7, "delay": 93.009, "offset": 9.337, "jitter": 10.03, "state": "#"}, {"remote": "chilipepper.can", "refid": "193.79.237.14", "st": 2, "t": "u", "when": 32, "poll": 64, "reach": 7, "delay": 165.084, "offset": 5.872, "jitter": 3.797, "state": null}, {"remote": "198.255.68.106", "refid": "192.168.1.193", "st": 2, "t": "u", "when": 26, "poll": 64, "reach": 7, "delay": 30.911, "offset": 3.034, "jitter": 8.201, "state": "+"}, {"remote": "golem.canonical", "refid": "17.253.34.251", "st": 2, "t": "u", "when": 28, "poll": 64, "reach": 7, "delay": 156.657, "offset": 3.767, "jitter": 5.448, "state": null}, {"remote": "vps.jan-jaap.ne", "refid": "132.163.96.1", "st": 2, "t": "u", "when": 23, "poll": 64, "reach": 7, "delay": 184.394, "offset": -7.118, "jitter": 4.585, "state": "#"}, {"remote": "vf2.bbnx.net", "refid": "252.74.143.178", "st": 2, "t": "u", "when": 28, "poll": 64, "reach": 7, "delay": 97.275, "offset": 0.824, "jitter": 18.08, "state": "+"}, {"remote": "pugot.canonical", "refid": "17.253.34.253", "st": 2, "t": "u", "when": 29, "poll": 64, "reach": 7, "delay": 157.069, "offset": 3.709, "jitter": 8.753, "state": null}] diff --git a/tests/fixtures/ubuntu-18.04/ntpq-p2.out b/tests/fixtures/ubuntu-18.04/ntpq-p2.out new file mode 100644 index 00000000..a2bd4033 --- /dev/null +++ b/tests/fixtures/ubuntu-18.04/ntpq-p2.out @@ -0,0 +1,24 @@ + remote refid st t when poll reach delay offset jitter +============================================================================== + 0.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 1.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 2.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + 3.ubuntu.pool.n .POOL. 16 p - 64 0 0.000 0.000 0.000 + ntp.ubuntu.com .POOL. 16 p - 64 0 0.000 0.000 0.000 ++45.79.36.123 (t 216.218.254.202 2 u 11 64 7 68.172 4.404 4.878 ++199.188.64.12 ( 173.161.33.165 2 u 9 64 7 78.823 2.148 8.739 +*ntp2.wiktel.com .GPS. 1 u 25 64 7 76.857 0.473 7.890 +#atl0.jane.mattn 35.73.197.144 2 u 32 64 7 87.536 8.641 4.544 ++x.ns.gin.ntt.ne 249.224.99.213 2 u 28 64 7 25.021 3.361 7.793 +-srcf-ntp.stanfo 171.64.7.105 2 u 26 64 7 28.053 7.253 4.524 +#linode.ibendit. 64.250.105.227 2 u 28 64 7 56.841 -0.305 8.279 ++71.66.197.233 .GPS. 1 u 26 64 7 127.766 -0.769 3.465 ++time.aja.com 198.169.208.144 2 u 32 64 7 39.673 4.684 4.916 ++23.152.160.126 84.168.88.243 2 u 28 64 7 21.553 3.245 8.612 +#ntp.backplanedn 152.2.133.52 2 u 30 64 7 93.009 9.337 10.030 + chilipepper.can 193.79.237.14 2 u 32 64 7 165.084 5.872 3.797 ++198.255.68.106 192.168.1.193 2 u 26 64 7 30.911 3.034 8.201 + golem.canonical 17.253.34.251 2 u 28 64 7 156.657 3.767 5.448 +#vps.jan-jaap.ne 132.163.96.1 2 u 23 64 7 184.394 -7.118 4.585 ++vf2.bbnx.net 252.74.143.178 2 u 28 64 7 97.275 0.824 18.080 + pugot.canonical 17.253.34.253 2 u 29 64 7 157.069 3.709 8.753 diff --git a/tests/test_ntpq.py b/tests/test_ntpq.py index 59843474..b9c2f23a 100644 --- a/tests/test_ntpq.py +++ b/tests/test_ntpq.py @@ -22,6 +22,9 @@ class MyTests(unittest.TestCase): with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ntpq-pn.out'), 'r', encoding='utf-8') as f: self.ubuntu_18_4_ntpq_pn = f.read() + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ntpq-p2.out'), 'r', encoding='utf-8') as f: + self.ubuntu_18_4_ntpq_p2 = f.read() + # output with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/ntpq-p.json'), 'r', encoding='utf-8') as f: self.centos_7_7_ntpq_p_json = json.loads(f.read()) @@ -35,6 +38,9 @@ class MyTests(unittest.TestCase): with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ntpq-pn.json'), 'r', encoding='utf-8') as f: self.ubuntu_18_4_ntpq_pn_json = json.loads(f.read()) + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/ntpq-p2.json'), 'r', encoding='utf-8') as f: + self.ubuntu_18_4_ntpq_p2_json = json.loads(f.read()) + def test_ntpq_p_centos_7_7(self): """ Test 'ntpq -p' on Centos 7.7 @@ -59,7 +65,11 @@ class MyTests(unittest.TestCase): """ self.assertEqual(jc.parsers.ntpq.parse(self.ubuntu_18_4_ntpq_pn, quiet=True), self.ubuntu_18_4_ntpq_pn_json) - + def test_ntpq_p2_ubuntu_18_4(self): + """ + Test 'ntpq -p' with ip data with spaces on Ubuntu 18.4 + """ + self.assertEqual(jc.parsers.ntpq.parse(self.ubuntu_18_4_ntpq_p2, quiet=True), self.ubuntu_18_4_ntpq_p2_json) if __name__ == '__main__': From a2ab5bab91ea980399df1afb9d8071fa3282d04e Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Wed, 11 Mar 2020 13:32:58 -0700 Subject: [PATCH 21/21] version bump to v1.9.0 --- changelog.txt | 2 +- jc/cli.py | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.txt b/changelog.txt index 7aadae4a..d8bff46b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,6 +1,6 @@ jc changelog -20200xxxx vX.X.X +20200311 v1.9.0 - Added ntpq command parser - Added timedatectl status command parser - Added airport -I and airport -s command parser diff --git a/jc/cli.py b/jc/cli.py index e3cd308c..1b443609 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -13,7 +13,7 @@ import jc.utils class info(): - version = '1.8.1' + version = '1.9.0' description = 'jc cli output JSON conversion tool' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/setup.py b/setup.py index 85fa9afb..16dfbee0 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open('README.md', 'r') as f: setuptools.setup( name='jc', - version='1.8.1', + version='1.9.0', author='Kelly Brazil', author_email='kellyjonbrazil@gmail.com', description='This tool serializes the output of popular command line tools and filetypes to structured JSON output.',