1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2026-04-03 17:44:07 +02:00

Compare commits

..

89 Commits

Author SHA1 Message Date
Kelly Brazil
0c40e3a3c9 Merge pull request #618 from kellyjonbrazil/dev
Dev v1.25.4
2024-11-25 20:39:34 -08:00
Kelly Brazil
025d00ecd2 Merge branch 'master' into dev 2024-11-25 20:33:20 -08:00
Kelly Brazil
a2b5d41308 minor fix to iw-scan to parse more fields 2024-11-25 20:19:40 -08:00
Kelly Brazil
366589268e update calculated fields 2024-11-25 17:16:30 -08:00
Kelly Brazil
53dd28b65e doc update 2024-11-24 18:59:30 -08:00
Kelly Brazil
0be6528aff convert size fields and fix issue for optional_deps without a description 2024-11-24 18:13:08 -08:00
Kelly Brazil
6ceaa7749e doc update 2024-11-24 17:30:49 -08:00
Kelly Brazil
6ae2e17ea1 add examples and tests 2024-11-24 17:30:03 -08:00
Kelly Brazil
ef9ca9322e Some processing completed 2024-11-24 16:02:59 -08:00
Kelly Brazil
95cba21d73 initial pacman parser 2024-11-24 14:57:55 -08:00
Kelly Brazil
be1dd031f1 doc update 2024-11-24 13:38:10 -08:00
Hamza Saht
7fbe1e9f21 Add WireGuard (wg) Command Output Parser (#606)
* feat: add parser to parse the output of wg

* fixup! feat: add parser to parse the output of wg

* feat: Add tests for windows 10

---------

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2024-11-24 13:34:58 -08:00
Hamza Saht
7d33850d43 fix: Edit pythonapp.yml workflow to run on non-draft PRs (#617) 2024-11-24 13:24:04 -08:00
Kelly Brazil
7887789d0d coerce non-json-serializable objects to strings 2024-11-20 13:49:27 -08:00
Kelly Brazil
7ddd2a4ce2 fix yaml parser to support values starting with an equal sign 2024-11-19 14:12:21 -08:00
Kelly Brazil
34ab34cc66 doc update 2024-11-19 12:15:18 -08:00
pettai
a8b231da81 nsd-control parser update, zone transfer status (#607)
* add more parsing functionality for transfering state

* add additional/conditional fields into the schema

---------

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2024-11-19 12:12:43 -08:00
Kelly Brazil
2278c7ecab doc update 2024-11-19 10:51:16 -08:00
Kelly Brazil
4cb88977cc add long ipv6 test 2024-11-19 10:51:10 -08:00
Aleksey Lobanov
0af4a3a5d7 fix: New RE_PROBE_IPV6_ONLY with better complexity in worst case (#609)
Example ':'*100

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2024-11-19 10:43:24 -08:00
Kelly Brazil
c0f9b705c6 doc update 2024-11-19 10:33:19 -08:00
Hamza Saht
fa416083f2 Fix/spaces in program name (#608)
* fix: enforce word boundaries while checking state presence

* fix: add tests for the special netstat case with space in process name

---------

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2024-11-19 10:28:32 -08:00
Kelly Brazil
e858faa746 fix for contiguous packages with the same name 2024-11-18 16:21:40 -08:00
Kelly Brazil
a8f769eea3 doc update 2024-10-20 09:50:36 -07:00
Kelly Brazil
26133261f9 use jc.utils.timestamp() for datetime conversions 2024-10-20 09:45:08 -07:00
Kelly Brazil
6072ea0ec7 doc update 2024-10-18 14:34:29 -07:00
Kelly Brazil
95672c23b7 doc update 2024-10-18 14:30:04 -07:00
Jose E. Rodriguez
a2e0e6d549 feat: Add Windows ipconfig parser to jc (#596)
* feat: Introduce ipconfig parser

* fix: add parsing support for "connection_specific_dns_suffix_search_list" and windows XP ipv4 addresses, remove dateutil dependency

* fix: introduce unit tests, correct import of datetime

* fix: changed preferred to status to account for other ip statuses, and parsed link local ipv6 prefix length

* fix: compress _parse_header_line and _parse_adapter_line + fix casing in unit test file

---------

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2024-10-18 14:17:14 -07:00
Kelly Brazil
2df5e79295 doc update 2024-10-18 09:56:32 -07:00
Jake Ob
c5e0642b0b Fix broken controller regexp scheme in the bluetoothctl parser (#599)
This commit fixes the controller parser scheme in order to take care
and extract some extra attributes, the manufacturer and version.
The order of the attributes appearing in the regexp scheme must follow
the order they appear in the `bluetoothctl show` outputs.

A new test has been added to test outputs with these extra attrs.

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2024-10-18 09:50:51 -07:00
Kelly Brazil
78150ded70 doc update 2024-10-17 14:05:06 -07:00
Kelly Brazil
05f3e4ea8a fixup tests 2024-10-17 13:53:20 -07:00
Kelly Brazil
b7cf0ca8d4 flip is_reserved to false for python ipv6 backports 2024-10-17 13:13:31 -07:00
Kelly Brazil
ba6e1e694e fix for python backports for ipv6 output 2024-10-17 13:06:17 -07:00
Kelly Brazil
aec3e3cd13 force github test 2024-10-17 09:15:29 -07:00
Kelly Brazil
d9363ae473 doc update 2024-09-26 09:49:06 -07:00
Kelly Brazil
0f367a435a formatting 2024-09-23 06:58:33 -07:00
Kelly Brazil
0955598b49 formatting 2024-09-23 06:55:17 -07:00
Kelly Brazil
f2a8e0087b add tests and doc update 2024-09-22 20:01:40 -07:00
Kelly Brazil
97e9798cef add posix_mode to utils/convert_size_to_int and use in df 2024-09-22 19:34:23 -07:00
Leonard Crestez
413519e02c #592: Handle df size suffixes as binary (#593)
* df: convert_size_to_int as binary

* tests: Update df -h expected output

---------

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2024-09-22 19:28:45 -07:00
Kelly Brazil
5c855e40c6 fix 6to4 test for python backports 2024-09-17 09:52:38 -07:00
Kelly Brazil
b66c5dbf55 doc update 2024-09-12 19:38:31 -07:00
Kelly Brazil
b054b1b782 doc update 2024-09-07 19:50:08 -07:00
Mabuchin
1593d0bf79 Add error type support for Linux Ping (#575)
* feat: add icmp error handle into linux_parse

* refactor: fixed timestamp offset logic(including error-response-type condition)
2024-09-07 19:46:43 -07:00
Kelly Brazil
8a22f8a468 add support for Link partner advertised link modes to ethtool parser 2024-09-07 18:43:41 -07:00
Kelly Brazil
c26f0641ff enhance ifconfig parser to support utun interfaces with assigned ipv4 addresses on macOS 2024-09-07 18:11:25 -07:00
Kelly Brazil
40eb2b7ef6 fix uptime for "user" instead of "users" 2024-09-07 17:24:21 -07:00
Kelly Brazil
71af0c5555 fix for IPv4 mapped IPv6 address changes in Python 3.13 2024-09-06 10:26:34 -07:00
Kelly Brazil
9f5532d91f formatting 2024-06-18 17:43:16 -07:00
Kelly Brazil
c68c919024 add mount test 2024-06-18 17:34:17 -07:00
Michał Górny
9eb4df34b1 adjust expected test_ip_address_ipv6_6to4 output for Python 3.12.4 (#573)
Adjust the expected output in `test_ip_address_ipv6_6to4` to account
for `is_global`/`is_private` changes for 6to4 addresses in Python 3.12.4
and Python 3.13.0.

Fixes #572

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2024-06-18 08:53:19 -07:00
Kelly Brazil
921133f3ac version bump 2024-06-18 08:47:24 -07:00
Kelly Brazil
0350607359 fix for space if filesystem name 2024-06-18 08:47:02 -07:00
Kelly Brazil
5f8c65cf5b Merge branch 'dev' 2024-06-09 19:00:37 -07:00
Kelly Brazil
ed048c7b2b doc update 2024-06-09 18:48:25 -07:00
Kelly Brazil
18dabd4b9f update var names 2024-06-09 10:35:42 -07:00
Kelly Brazil
0005945383 doc update 2024-06-09 10:31:06 -07:00
Kelly Brazil
e13605a6a9 add ss tests 2024-06-08 09:17:57 -07:00
Kelly Brazil
a92e318319 doc update 2024-06-07 17:42:30 -07:00
Kelly Brazil
7d8166326a fix for when recv-q is too close to state field 2024-06-07 17:06:40 -07:00
Kelly Brazil
d21e5f4f28 doc update 2024-05-14 18:20:14 -07:00
Kelly Brazil
f1bab336ee fix file_stats in git-log parsers 2024-05-14 17:38:39 -07:00
konstantin-shatalov
0faacb6d0d Added support for number of lines changed for git logs (#557)
Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
Co-authored-by: kshatalov <kshatalov@citco.com>
2024-05-14 15:05:22 -07:00
Kelly Brazil
6af82fb9ae doc update 2024-05-10 11:11:31 -07:00
Kelly Brazil
517137889c fix zpool-status parser when there are no columnar values 2024-05-10 11:06:05 -07:00
Kelly Brazil
8a3997cd0e fix for pip-show parser with multi-line fields starting with a blank line 2024-05-02 15:09:06 -07:00
Kelly Brazil
4f66faad0d do not convert physlot to int 2024-04-29 14:20:15 -07:00
Kelly Brazil
846f4fb691 doc update 2024-04-29 14:09:14 -07:00
Kelly Brazil
5bec53b3f8 feat: get correct probe IP when mutliple probe on hop (#562)
Co-authored-by: Pierre-Olivier Côté <pierre-olivier.cote@polymtl.ca>
2024-04-29 13:58:11 -07:00
Kelly Brazil
dfc942776b doc update 2024-04-28 13:10:38 -07:00
Kelly Brazil
18a00bc57a doc update 2024-04-28 13:01:07 -07:00
Sam SIU
e2b2406931 [Fix issue #558] Add a condition check before subvendor dict creation (#559)
- Updated jc/parsers/pci_ids.py adding a check to see if the subvendor dict is already created
- Updated test fixture

Signed-off-by: Sam SIU <23556929+ssiuhk@users.noreply.github.com>
Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2024-04-28 12:58:22 -07:00
Kelly Brazil
a9cfb4b13a force test 2024-04-28 12:49:27 -07:00
Kelly Brazil
3e57ecf6df remove verbose testing 2024-04-28 12:48:03 -07:00
Kelly Brazil
2ddf67ad0c make xrander test quiet 2024-04-28 12:44:40 -07:00
Kelly Brazil
137c59cea1 add quiet=True to uptime parsing 2024-04-28 12:40:52 -07:00
Kelly Brazil
8b2f57e8e7 force test 2024-04-28 12:14:10 -07:00
Kelly Brazil
01f5aa05b3 Update pythonapp.yml
fix unittest command
2024-04-28 12:13:30 -07:00
Kelly Brazil
6ae626ef9f Merge branch 'dev' of https://github.com/kellyjonbrazil/jc into dev 2024-04-28 12:10:27 -07:00
Kelly Brazil
dea7099183 doc update 2024-04-28 12:08:13 -07:00
Kelly Brazil
9c3e4f349c verbose tests
make tests verbose to troubleshoot uptime compatibility messages in tests
2024-04-28 12:07:21 -07:00
Kelly Brazil
d5398c7089 remove timezone updates within tests - should be done by tester before running tests 2024-04-28 12:01:50 -07:00
Kelly Brazil
2dcf0e2d1b test remove timezone setting within test for windows 2024-04-28 11:48:32 -07:00
Kelly Brazil
f45295eee4 doc update 2024-04-28 11:30:33 -07:00
Nicolas Mattelaer
4ef34f7849 feat: Add battery_percentage property to --bluetoothctl output (#564)
* Adds battery_percentage property to bluetoothctl output

* remove ending comma

---------

Co-authored-by: Kelly Brazil <kellyjonbrazil@gmail.com>
2024-04-28 11:27:31 -07:00
Kelly Brazil
a69e55cb1c version bump 2024-04-27 11:17:37 -07:00
Kelly Brazil
6563b9f073 split jobs for old python and latest python 2024-04-27 11:15:44 -07:00
Kelly Brazil
6e17ed6f62 version bump 2024-04-22 10:11:26 -07:00
181 changed files with 7067 additions and 416 deletions

View File

@@ -9,12 +9,41 @@ on:
- "**/*.py"
jobs:
build:
old_python:
if: github.event.pull_request.draft == false
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-20.04, windows-latest]
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
os: [macos-13, ubuntu-20.04, windows-2019]
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v3
- name: "Set up timezone to America/Los_Angeles"
uses: szenius/set-timezone@v1.2
with:
timezoneLinux: "America/Los_Angeles"
timezoneMacos: "America/Los_Angeles"
timezoneWindows: "Pacific Standard Time"
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Test with unittest
run: |
python -m unittest discover tests
latest_python:
if: github.event.pull_request.draft == false
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-latest, ubuntu-latest, windows-latest]
python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@v3

View File

@@ -1,5 +1,39 @@
jc changelog
20241125 v1.25.4
- Add `ipconfig` command parser (`ipconfig` for Windows)
- Add `pacman` command parser
- Add `wg show` command parser
- Enhance `ethtool` parser to support `link_partner_advertised_link_modes`
- Enhance `ifconfig` parser to support `utun` interfaces with assigned IPv4 addresses on macOS
- Enhance `nsd-control` parser with additional state fields
- Enhance `ping-s` streaming parser to support error replies
- Fix `bluetoothctl` parser when extra attributes like `manufacturer` and `version` exist
- Fix `df` parser to correctly output binary vs. decimal size outputs
- Fix `ip-address` parser for Python 3.13 changes to IPv4 mapped IPv6 addresses
- Fix `iw-scan` parser to output more fields (still beta quality)
- Fix `mount` parser for cases where there are spaces in the filesystem name
- Fix `netstat` parser for cases where there are spaces in the program name
- Fix `pkg-index-deb`, `apt-cache-show`, and `rpm-qi` parsers to correctly convert contiguous packages with the same name
- Fix `traceroute` parser to support extreme IPv6 cases
- Fix `uptime` parser for data that contains `user` instead of `users`
- Fix `yaml` parser to support values that start with an equal sign
- Enhance `jc.utils.convert_size_to_int()` to add `posix_mode` and `decimal_bias` parameters
- Enhance cli to coerce any non-JSON-serializable objects to a string
20240609 v1.25.3
- Enhance `bluetoothctl` parser with added `battery_percentage` field
- Enhance `git-log` standard and streaming parsers with added `lines_changed` field under `file_stats`
- Fix `lspci` parser to handle `physlot` fields with a range value
- Fix `pci-ids` parser to correctly handle multiple subdevices
- Fix `pip-show` parser to handle multi-line fields with a beginning blank line
- Fix `ss` parser to correctly handle the `Recv-Q` field being too close to the `Status` field
- Fix `top` parsers to quiet uptime info parsing
- Fix `traceroute` parser to correctly handle hops with multiple IPs
- Fix `zpool-status` parser for config items lacking data values
- Optimize some tests by removing timezone settings and using quiet=True
- Documentation updates
20240323 v1.25.2
- Add `apt-cache-show` command parser
- Add `apt-get-sqq` command parser

View File

@@ -218,6 +218,7 @@ option.
| `--iostat` | `iostat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat) |
| `--iostat-s` | `iostat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iostat_s) |
| `--ip-address` | IPv4 and IPv6 Address string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ip_address) |
| `--ipconfig` | `ipconfig` Windows command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ipconfig) |
| `--iptables` | `iptables` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iptables) |
| `--ip-route` | `ip route` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ip_route) |
| `--iw-scan` | `iw dev [device] scan` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iw_scan) |
@@ -250,6 +251,7 @@ option.
| `--openvpn` | openvpn-status.log file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/openvpn) |
| `--os-prober` | `os-prober` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/os_prober) |
| `--os-release` | `/etc/os-release` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/os_release) |
| `--pacman` | `pacman` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/pacman) |
| `--passwd` | `/etc/passwd` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/passwd) |
| `--path` | POSIX path string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/path) |
| `--path-list` | POSIX path list string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/path_list) |
@@ -316,6 +318,7 @@ option.
| `--vmstat-s` | `vmstat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/vmstat_s) |
| `--w` | `w` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/w) |
| `--wc` | `wc` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/wc) |
| `--wg-show` | `wg show` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/wg_show) |
| `--who` | `who` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/who) |
| `--x509-cert` | X.509 PEM and DER certificate file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/x509_cert) |
| `--x509-csr` | X.509 PEM and DER certificate request file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/x509_csr) |

View File

@@ -3,8 +3,8 @@ _jc()
local cur prev words cword jc_commands jc_parsers jc_options \
jc_about_options jc_about_mod_options jc_help_options jc_special_options
jc_commands=(acpi airport apt-cache apt-get arp blkid bluetoothctl cbt certbot chage cksum crontab curl date debconf-show df dig dmidecode dpkg du efibootmgr env ethtool file findmnt finger free git gpg hciconfig host id ifconfig iostat ip iptables iw iwconfig jobs last lastb ls lsattr lsb_release lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat needrestart netstat nmcli nsd-control ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum swapon sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 tune2fs udevadm ufw uname update-alternatives upower uptime vdir veracrypt vmstat w wc who xrandr zipinfo zpool)
jc_parsers=(--acpi --airport --airport-s --apt-cache-show --apt-get-sqq --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --certbot --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --curl-head --date --datetime-iso --debconf-show --df --dig --dir --dmidecode --dpkg-l --du --efibootmgr --email-address --env --ethtool --file --find --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --host --hosts --http-headers --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --ip-route --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --kv-dup --last --ls --ls-s --lsattr --lsb-release --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --needrestart --netstat --nmcli --nsd-control --ntpq --openvpn --os-prober --os-release --passwd --path --path-list --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --pkg-index-apk --pkg-index-deb --plist --postconf --proc --proc-buddyinfo --proc-cmdline --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-tcp --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --resolve-conf --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --srt --ss --ssh-conf --sshd-conf --stat --stat-s --swapon --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --tune2fs --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --veracrypt --vmstat --vmstat-s --w --wc --who --x509-cert --x509-csr --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status)
jc_commands=(acpi airport apt-cache apt-get arp blkid bluetoothctl cbt certbot chage cksum crontab curl date debconf-show df dig dmidecode dpkg du efibootmgr env ethtool file findmnt finger free git gpg hciconfig host id ifconfig iostat ip ipconfig iptables iw iwconfig jobs last lastb ls lsattr lsb_release lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat needrestart netstat nmcli nsd-control ntpq os-prober pacman pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum swapon sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 tune2fs udevadm ufw uname update-alternatives upower uptime vdir veracrypt vmstat w wc wg who xrandr zipinfo zpool)
jc_parsers=(--acpi --airport --airport-s --apt-cache-show --apt-get-sqq --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --certbot --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --curl-head --date --datetime-iso --debconf-show --df --dig --dir --dmidecode --dpkg-l --du --efibootmgr --email-address --env --ethtool --file --find --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --host --hosts --http-headers --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --ipconfig --iptables --ip-route --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --kv-dup --last --ls --ls-s --lsattr --lsb-release --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --needrestart --netstat --nmcli --nsd-control --ntpq --openvpn --os-prober --os-release --pacman --passwd --path --path-list --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --pkg-index-apk --pkg-index-deb --plist --postconf --proc --proc-buddyinfo --proc-cmdline --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-tcp --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --resolve-conf --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --srt --ss --ssh-conf --sshd-conf --stat --stat-s --swapon --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --tune2fs --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --veracrypt --vmstat --vmstat-s --w --wc --wg-show --who --x509-cert --x509-csr --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status)
jc_options=(--force-color -C --debug -d --monochrome -m --meta-out -M --pretty -p --quiet -q --raw -r --slurp -s --unbuffer -u --yaml-out -y)
jc_about_options=(--about -a)
jc_about_mod_options=(--pretty -p --yaml-out -y --monochrome -m --force-color -C)

View File

@@ -9,7 +9,7 @@ _jc() {
jc_help_options jc_help_options_describe \
jc_special_options jc_special_options_describe
jc_commands=(acpi airport apt-cache apt-get arp blkid bluetoothctl cbt certbot chage cksum crontab curl date debconf-show df dig dmidecode dpkg du efibootmgr env ethtool file findmnt finger free git gpg hciconfig host id ifconfig iostat ip iptables iw iwconfig jobs last lastb ls lsattr lsb_release lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat needrestart netstat nmcli nsd-control ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum swapon sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 tune2fs udevadm ufw uname update-alternatives upower uptime vdir veracrypt vmstat w wc who xrandr zipinfo zpool)
jc_commands=(acpi airport apt-cache apt-get arp blkid bluetoothctl cbt certbot chage cksum crontab curl date debconf-show df dig dmidecode dpkg du efibootmgr env ethtool file findmnt finger free git gpg hciconfig host id ifconfig iostat ip ipconfig iptables iw iwconfig jobs last lastb ls lsattr lsb_release lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat needrestart netstat nmcli nsd-control ntpq os-prober pacman pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss ssh sshd stat sum swapon sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 tune2fs udevadm ufw uname update-alternatives upower uptime vdir veracrypt vmstat w wc wg who xrandr zipinfo zpool)
jc_commands_describe=(
'acpi:run "acpi" command with magic syntax.'
'airport:run "airport" command with magic syntax.'
@@ -46,6 +46,7 @@ _jc() {
'ifconfig:run "ifconfig" command with magic syntax.'
'iostat:run "iostat" command with magic syntax.'
'ip:run "ip" command with magic syntax.'
'ipconfig:run "ipconfig" command with magic syntax.'
'iptables:run "iptables" command with magic syntax.'
'iw:run "iw" command with magic syntax.'
'iwconfig:run "iwconfig" command with magic syntax.'
@@ -71,6 +72,7 @@ _jc() {
'nsd-control:run "nsd-control" command with magic syntax.'
'ntpq:run "ntpq" command with magic syntax.'
'os-prober:run "os-prober" command with magic syntax.'
'pacman:run "pacman" command with magic syntax.'
'pidstat:run "pidstat" command with magic syntax.'
'ping:run "ping" command with magic syntax.'
'ping6:run "ping6" command with magic syntax.'
@@ -116,12 +118,13 @@ _jc() {
'vmstat:run "vmstat" command with magic syntax.'
'w:run "w" command with magic syntax.'
'wc:run "wc" command with magic syntax.'
'wg:run "wg" command with magic syntax.'
'who:run "who" command with magic syntax.'
'xrandr:run "xrandr" command with magic syntax.'
'zipinfo:run "zipinfo" command with magic syntax.'
'zpool:run "zpool" command with magic syntax.'
)
jc_parsers=(--acpi --airport --airport-s --apt-cache-show --apt-get-sqq --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --certbot --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --curl-head --date --datetime-iso --debconf-show --df --dig --dir --dmidecode --dpkg-l --du --efibootmgr --email-address --env --ethtool --file --find --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --host --hosts --http-headers --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --iptables --ip-route --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --kv-dup --last --ls --ls-s --lsattr --lsb-release --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --needrestart --netstat --nmcli --nsd-control --ntpq --openvpn --os-prober --os-release --passwd --path --path-list --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --pkg-index-apk --pkg-index-deb --plist --postconf --proc --proc-buddyinfo --proc-cmdline --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-tcp --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --resolve-conf --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --srt --ss --ssh-conf --sshd-conf --stat --stat-s --swapon --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --tune2fs --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --veracrypt --vmstat --vmstat-s --w --wc --who --x509-cert --x509-csr --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status)
jc_parsers=(--acpi --airport --airport-s --apt-cache-show --apt-get-sqq --arp --asciitable --asciitable-m --blkid --bluetoothctl --cbt --cef --cef-s --certbot --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --curl-head --date --datetime-iso --debconf-show --df --dig --dir --dmidecode --dpkg-l --du --efibootmgr --email-address --env --ethtool --file --find --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --host --hosts --http-headers --id --ifconfig --ini --ini-dup --iostat --iostat-s --ip-address --ipconfig --iptables --ip-route --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --kv-dup --last --ls --ls-s --lsattr --lsb-release --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --needrestart --netstat --nmcli --nsd-control --ntpq --openvpn --os-prober --os-release --pacman --passwd --path --path-list --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --pkg-index-apk --pkg-index-deb --plist --postconf --proc --proc-buddyinfo --proc-cmdline --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-tcp --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --resolve-conf --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --srt --ss --ssh-conf --sshd-conf --stat --stat-s --swapon --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --toml --top --top-s --tracepath --traceroute --tune2fs --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --ver --veracrypt --vmstat --vmstat-s --w --wc --wg-show --who --x509-cert --x509-csr --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status)
jc_parsers_describe=(
'--acpi:`acpi` command parser'
'--airport:`airport -I` command parser'
@@ -185,6 +188,7 @@ _jc() {
'--iostat:`iostat` command parser'
'--iostat-s:`iostat` command streaming parser'
'--ip-address:IPv4 and IPv6 Address string parser'
'--ipconfig:`ipconfig` Windows command parser'
'--iptables:`iptables` command parser'
'--ip-route:`ip route` command parser'
'--iw-scan:`iw dev [device] scan` command parser'
@@ -217,6 +221,7 @@ _jc() {
'--openvpn:openvpn-status.log file parser'
'--os-prober:`os-prober` command parser'
'--os-release:`/etc/os-release` file parser'
'--pacman:`pacman` command parser'
'--passwd:`/etc/passwd` file parser'
'--path:POSIX path string parser'
'--path-list:POSIX path list string parser'
@@ -334,6 +339,7 @@ _jc() {
'--vmstat-s:`vmstat` command streaming parser'
'--w:`w` command parser'
'--wc:`wc` command parser'
'--wg-show:`wg show` command parser'
'--who:`who` command parser'
'--x509-cert:X.509 PEM and DER certificate file parser'
'--x509-csr:X.509 PEM and DER certificate request file parser'

View File

@@ -9,11 +9,11 @@ Requires the `-sqq` options in `apt-get`.
Usage (cli):
$ apt-get -sqq | jc --apt-get-sqq
$ apt-get -sqq upgrade | jc --apt-get-sqq
or
$ jc apt-get -sqq
$ jc apt-get -sqq full-upgrade
Usage (module):
@@ -35,7 +35,7 @@ Schema:
Examples:
$ apt-get -sqq | jc --apt-get-sqq -p
$ apt-get -sqq upgrade | jc --apt-get-sqq -p
[
{
"operation": "unpack",
@@ -103,7 +103,7 @@ Examples:
}
]
$ apt-get -sqq | jc --apt-get-sqq -p -r
$ apt-get -sqq upgrade | jc --apt-get-sqq -p -r
[
{
"operation": "Inst",
@@ -198,4 +198,4 @@ Compatibility: linux
Source: [`jc/parsers/apt_get_sqq.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/apt_get_sqq.py)
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -33,6 +33,8 @@ a controller and a device but there might be fields corresponding to one entity.
Controller:
[
{
"manufacturer": string,
"version": string,
"name": string,
"is_default": boolean,
"is_public": boolean,
@@ -70,7 +72,8 @@ a controller and a device but there might be fields corresponding to one entity.
"rssi": int,
"txpower": int,
"uuids": array,
"modalias": string
"modalias": string,
"battery_percentage": int
}
]
@@ -101,7 +104,8 @@ Examples:
"Headset HS (00001831-0000-1000-8000-00805f9b34fb)"
],
"rssi": -52,
"txpower": 4
"txpower": 4,
"battery_percentage": 70
}
]
@@ -132,4 +136,4 @@ Compatibility: linux
Source: [`jc/parsers/bluetoothctl.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/bluetoothctl.py)
Version 1.1 by Jake Ob (iakopap at gmail.com)
Version 1.3 by Jake Ob (iakopap at gmail.com)

View File

@@ -124,4 +124,4 @@ Compatibility: linux, darwin, freebsd
Source: [`jc/parsers/df.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/df.py)
Version 2.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 2.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -194,4 +194,4 @@ Compatibility: linux
Source: [`jc/parsers/ethtool.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/ethtool.py)
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -55,6 +55,12 @@ Schema:
"deletions": integer,
"files": [
string
],
"file_stats": [
{
"name": string,
"lines_changed": integer
}
]
}
}
@@ -79,6 +85,16 @@ Examples:
"files": [
"docs/parsers/git_log.md",
"jc/parsers/git_log.py"
],
"file_stats": [
{
"name": "docs/parsers/git_log.md",
"lines_changed": 3
},
{
"name": "jc/parsers/git_log.py",
"lines_changed": 7
}
]
},
"message": "add timestamp docs and examples",
@@ -100,6 +116,28 @@ Examples:
"jc/parsers/git_log.py",
"jc/utils.py",
"man/jc.1"
],
"file_stats": [
{
"name": "docs/parsers/git_log.md",
"lines_changed": 3
},
{
"name": "docs/utils.md",
"lines_changed": 7
},
{
"name": "jc/parsers/git_log.py",
"lines_changed": 1
},
{
"name": "jc/utils.py",
"lines_changed": 12
},
{
"name": "man/jc.1",
"lines_changed": 14
}
]
},
"message": "add calculated timestamp",
@@ -123,6 +161,16 @@ Examples:
"files": [
"docs/parsers/git_log.md",
"jc/parsers/git_log.py"
],
"file_stats": [
{
"name": "docs/parsers/git_log.md",
"lines_changed": "3"
},
{
"name": "jc/parsers/git_log.py",
"lines_changed": "7"
}
]
},
"message": "add timestamp docs and examples"
@@ -142,6 +190,28 @@ Examples:
"jc/parsers/git_log.py",
"jc/utils.py",
"man/jc.1"
],
"file_stats": [
{
"name": "docs/parsers/git_log.md",
"lines_changed": "3"
},
{
"name": "docs/utils.md",
"lines_changed": "7"
},
{
"name": "jc/parsers/git_log.py",
"lines_changed": "1"
},
{
"name": "jc/utils.py",
"lines_changed": "12"
},
{
"name": "man/jc.1",
"lines_changed": "14"
}
]
},
"message": "add calculated timestamp"
@@ -174,4 +244,4 @@ Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Source: [`jc/parsers/git_log.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/git_log.py)
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.5 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -56,6 +56,12 @@ Schema:
"deletions": integer,
"files": [
string
],
"file_stats": [
{
"name": string,
"lines_changed": integer
}
]
}
@@ -109,4 +115,4 @@ Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Source: [`jc/parsers/git_log_s.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/git_log_s.py)
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.5 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -242,4 +242,4 @@ Compatibility: linux, aix, freebsd, darwin
Source: [`jc/parsers/ifconfig.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/ifconfig.py)
Version 2.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 2.4 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -556,4 +556,4 @@ Source: [`jc/parsers/ip_address.py`](https://github.com/kellyjonbrazil/jc/blob/m
This parser can be used with the `--slurp` command-line option.
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.5 by Kelly Brazil (kellyjonbrazil@gmail.com)

461
docs/parsers/ipconfig.md Normal file
View File

@@ -0,0 +1,461 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.ipconfig"></a>
# jc.parsers.ipconfig
jc - JSON Convert `ipconfig` Windows command output parser
Usage (cli):
$ ipconfig /all | jc --ipconfig
$ ipconfig | jc --ipconfig
$ jc ipconfig /all
Usage (module):
import jc
result = jc.parse('ipconfig', ipconfig_command_output)
Schema:
{
"host_name": string,
"primary_dns_suffix": string,
"node_type": string,
"ip_routing_enabled": boolean,
"wins_proxy_enabled": boolean,
"dns_suffix_search_list": [
string
],
"adapters": [
{
"name_long": string,
"name": string,
"type": string,
"connection_specific_dns_suffix": string,
"connection_specific_dns_suffix_search_list": [
string
]
"description": string,
"physical_address": string,
"dhcp_enabled": boolean,
"autoconfiguration_enabled": boolean,
"ipv6_addresses": [
{
"address": string,
"status": string,
},
],
"temporary_ipv6_addresses": [
{
"address": string,
"status": string,
},
],
"link_local_ipv6_addresses": [
{
"address": string,
"status": string,
"prefix_length": integer,
}
],
"ipv4_addresses": [
{
"address": string, # [2]
"subnet_mask": string,
"status": string,
"autoconfigured": boolean # [1]
}
],
"default_gateways": [
string
],
"dhcp_server": null,
"dhcpv6_iaid": string,
"dhcpv6_client_duid": string,
"dns_servers": [
string
],
"primary_wins_server": string,
"lease_expires": string,
"lease_expires_epoch": integer, # [0]
"lease_expires_iso": string,
"lease_obtained": string,
"lease_obtained_epoch": integer, # [0]
"lease_obtained_iso": string,
"netbios_over_tcpip": boolean,
"media_state": string,
"extras": [
<string>: string
]
}
],
"extras": []
}
Notes:
[0] - The epoch calculated timestamp field is naive. (i.e. based on
the local time of the system the parser is run on)
[1] - 'autoconfigured' under 'ipv4_address' is only providing
indication if the ipv4 address was labeled as "Autoconfiguration
IPv4 Address" vs "IPv4 Address". It does not infer any
information from other fields
[2] - Windows XP uses 'IP Address' instead of 'IPv4 Address'. Both
values are parsed to the 'ipv4_address' object for consistency
Examples:
$ ipconfig /all | jc --ipconfig -p
{
"host_name": "DESKTOP-WIN11-HOME",
"primary_dns_suffix": null,
"node_type": "Hybrid",
"ip_routing_enabled": false,
"wins_proxy_enabled": false,
"dns_suffix_search_list": [
"localdomain"
],
"adapters": [
{
"name_long": "Ethernet adapter Ethernet",
"name": "Ethernet",
"type": "Ethernet",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "Intel(R) I211 Gigabit Network Connection",
"physical_address": "24-4B-FE-AB-43-C3",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Ethernet adapter Ethernet 2",
"name": "Ethernet 2",
"type": "Ethernet",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "Realtek PCIe 2.5GbE Family Controller",
"physical_address": "24-4B-FE-57-3D-F2",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Unknown adapter OpenVPN Data Channel Offload for NordVPN",
"name": "OpenVPN Data Channel Offload for NordVPN",
"type": "Unknown",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "OpenVPN Data Channel Offload",
"physical_address": null,
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Unknown adapter Local Area Connection",
"name": "Local Area Connection",
"type": "Unknown",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "TAP-NordVPN Windows Adapter V9",
"physical_address": "00-FF-4C-F4-5E-49",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Wireless LAN adapter Local Area Connection* 1",
"name": "Local Area Connection* 1",
"type": "Wireless LAN",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "Microsoft Wi-Fi Direct Virtual Adapter",
"physical_address": "A8-7E-EA-5A-7F-DE",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Wireless LAN adapter Local Area Connection* 2",
"name": "Local Area Connection* 2",
"type": "Wireless LAN",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "Microsoft Wi-Fi Direct Virtual Adapter #2",
"physical_address": "AA-7E-EA-F3-64-C3",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Ethernet adapter VMware Network Adapter VMnet1",
"name": "VMware Network Adapter VMnet1",
"type": "Ethernet",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "VMware Virtual Ethernet Adapter for VMnet1",
"physical_address": "00-50-56-CC-27-73",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [
{
"address": "fe80::f47d:9c7f:69dc:591e",
"prefix_length": 8,
"status": "Preferred"
}
],
"ipv4_addresses": [
{
"address": "192.168.181.1",
"subnet_mask": "255.255.255.0",
"status": "Preferred",
"autoconfigured": false
}
],
"default_gateways": [],
"dhcp_server": "192.168.181.254",
"dhcpv6_iaid": "771772502",
"dhcpv6_client_duid": "00-01-00-01-2C-CF-19-EB-24-4B-FE-5B-9B-E6",
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": "2024-09-19T18:01:29",
"lease_obtained": "2024-09-19T08:31:29",
"netbios_over_tcpip": true,
"media_state": null,
"extras": []
},
{
"name_long": "Ethernet adapter VMware Network Adapter VMnet8",
"name": "VMware Network Adapter VMnet8",
"type": "Ethernet",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "VMware Virtual Ethernet Adapter for VMnet8",
"physical_address": "00-50-56-C9-A3-78",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [
{
"address": "fe80::4551:bf0d:59dd:a4f0",
"prefix_length": 10,
"status": "Preferred"
}
],
"ipv4_addresses": [
{
"address": "192.168.213.1",
"subnet_mask": "255.255.255.0",
"status": "Preferred",
"autoconfigured": false
}
],
"default_gateways": [],
"dhcp_server": "192.168.213.254",
"dhcpv6_iaid": "788549718",
"dhcpv6_client_duid": "00-01-00-01-2C-CF-19-EB-24-4B-FE-5B-9B-E6",
"dns_servers": [],
"primary_wins_server": "192.168.213.2",
"lease_expires": "2024-09-19T18:01:29",
"lease_obtained": "2024-09-19T08:31:29",
"netbios_over_tcpip": true,
"media_state": null,
"extras": []
},
{
"name_long": "Wireless LAN adapter Wi-Fi",
"name": "Wi-Fi",
"type": "Wireless LAN",
"connection_specific_dns_suffix": "localdomain",
"connection_specific_dns_suffix_search_list": [],
"description": "Intel(R) Wi-Fi 6 AX200 160MHz",
"physical_address": "A8-7E-EA-55-26-B0",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [
{
"address": "fd63:cc9c:65eb:3f95:57c2:aa:10d8:db08",
"status": "Preferred"
}
],
"temporary_ipv6_addresses": [
{
"address": "fd63:cc9c:65eb:3f95:8928:348e:d692:b7ef",
"status": "Preferred"
}
],
"link_local_ipv6_addresses": [
{
"address": "fe80::4fae:1380:5a1b:8b6b",
"prefix_length": 11,
"status": "Preferred"
}
],
"ipv4_addresses": [
{
"address": "192.168.1.169",
"subnet_mask": "255.255.255.0",
"status": "Preferred",
"autoconfigured": false
}
],
"default_gateways": [
"192.168.1.1"
],
"dhcp_server": "192.168.1.1",
"dhcpv6_iaid": "162037482",
"dhcpv6_client_duid": "00-01-00-01-2C-CF-19-EB-24-4B-FE-5B-9B-E6",
"dns_servers": [
"192.168.1.1"
],
"primary_wins_server": null,
"lease_expires": "2024-09-20T08:31:30",
"lease_obtained": "2024-09-19T08:31:30",
"netbios_over_tcpip": true,
"media_state": null,
"extras": []
},
{
"name_long": "Ethernet adapter Bluetooth Network Connection",
"name": "Bluetooth Network Connection",
"type": "Ethernet",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "Bluetooth Device (Personal Area Network)",
"physical_address": "A8-7E-EA-43-23-14",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
}
],
"extras": []
}
<a id="jc.parsers.ipconfig.parse"></a>
### parse
```python
def parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Parsed dictionary. The raw and processed data structures are the same.
### Parser Information
Compatibility: windows
Source: [`jc/parsers/ipconfig.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/ipconfig.py)
Version 1.0 by joehacksalot (joehacksalot@gmail.com)

View File

@@ -146,4 +146,4 @@ Compatibility: linux
Source: [`jc/parsers/iw_scan.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/iw_scan.py)
Version 0.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 0.75 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -53,7 +53,6 @@ Schema:
"sdevice_id_int": integer,
"rev": string,
"physlot": string,
"physlot_int": integer,
"progif": string,
"progif_int": integer
}
@@ -89,7 +88,6 @@ Examples:
"sdevice_id": "07e0",
"sdevice_id_int": 2016,
"physlot": "37",
"physlot_int": 55,
"progif": "01",
"progif_int": 1
},
@@ -147,4 +145,4 @@ Compatibility: linux
Source: [`jc/parsers/lspci.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/lspci.py)
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -100,4 +100,4 @@ Compatibility: linux, darwin, freebsd, aix
Source: [`jc/parsers/mount.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/mount.py)
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.10 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -380,4 +380,4 @@ Compatibility: linux, darwin, freebsd, win32
Source: [`jc/parsers/netstat.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/netstat.py)
Version 1.15 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.16 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -22,20 +22,24 @@ Schema:
[
{
"version": string,
"verbosity": integer,
"ratelimit": integer
"version": string,
"verbosity": integer,
"ratelimit": integer
}
]
[
{
"zone": string
"zone": string
"status": {
"state": string,
"served-serial": string,
"commit-serial": string,
"wait": string
"state": string,
"pattern": string, # Additional
"catalog-member-id": string, # Additional
"served-serial": string,
"commit-serial": string,
"notified-serial": string, # Conditional
"wait": string,
"transfer": string # Conditional
}
}
]
@@ -89,4 +93,4 @@ Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Source: [`jc/parsers/nsd_control.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/nsd_control.py)
Version 1.1 by Pettai (pettai@sunet.se)
Version 1.2 by Pettai (pettai@sunet.se)

208
docs/parsers/pacman.md Normal file
View File

@@ -0,0 +1,208 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.pacman"></a>
# jc.parsers.pacman
jc - JSON Convert `pacman` command output parser
Supports the following `pacman` arguments:
- `-Si`
- `-Sii`
- `-Qi`
- `-Qii`
The `*_epoch` calculated timestamp fields are naive. (i.e. based on the
local time of the system the parser is run on)
Usage (cli):
$ pacman -Si <package> | jc --pacman
or
$ jc pacman -Si <package>
Usage (module):
import jc
result = jc.parse('pacman', pacman_command_output)
Schema:
[
{
"repository": string,
"name": string,
"version": string,
"description": string,
"architecture": string,
"url": string,
"licenses": [
string
],
"groups": [
string
],
"provides": [
string
],
"depends_on": [
string
],
"optional_deps": [
{
"name": string,
"description": string
}
],
"optional_for": [
string
],
"conflicts_with": [
string
],
"replaces": [
string
],
"download_size": string,
"download_size_bytes": integer [0]
"installed_size": string,
"installed_size_bytes": integer, [0]
"packager": string,
"build_date": string,
"build_date_epoch": integer, [0]
"install_date": string,
"install_date_epoch": integer, [0]
"validated_by": [
string
],
"backup_files": [
string
]
}
]
[0] Field exists if conversion successful
Examples:
$ pacman -qii zstd | jc --pacman -p
[
{
"name": "zstd",
"version": "1.5.6-1",
"description": "Zstandard - Fast real-time compression algorithm",
"architecture": "x86_64",
"url": "https://facebook.github.io/zstd/",
"licenses": [
"BSD-3-Clause",
"GPL-2.0-only"
],
"groups": [],
"provides": [
"libzstd.so=1-64"
],
"depends_on": [
"glibc",
"gcc-libs",
"zlib",
"xz",
"lz4"
],
"required_by": [
"android-tools",
"appstream",
...
"tiled",
"vulkan-radeon",
"wireshark-cli"
],
"optional_for": [
"xarchiver"
],
"conflicts_with": [],
"replaces": [],
"installed_size": "1527.00 KiB",
"installed_size_bytes": 1563648,
"packager": "Levente Polyak <anthraxx@archlinux.org>",
"build_date": "Sat 11 May 2024 06:14:19 AM +08",
"build_date_epoch": 1715433259,
"install_date": "Fri 24 May 2024 09:50:31 AM +08",
"install_date_epoch": 1715663342,
"install_reason": "Installed as a dependency for another package",
"install_script": "No",
"validated_by": [
"Signature"
],
"extended_data": "pkgtype=pkg"
}
]
$ pacman -qii zstd | jc --pacman -p -r
[
{
"name": "zstd",
"version": "1.5.6-1",
"description": "Zstandard - Fast real-time compression algorithm",
"architecture": "x86_64",
"url": "https://facebook.github.io/zstd/",
"licenses": "BSD-3-Clause GPL-2.0-only",
"groups": null,
"provides": "libzstd.so=1-64",
"depends_on": "glibc gcc-libs zlib xz lz4",
"required_by": [
"android-tools appstream avr-gcc binutils blender blosc",
"boost-libs btrfs-progs cloudflare-warp-bin comgr curl",
"dolphin-emu file flatpak gcc gdal gnutls karchive",
"karchive5 kmod lib32-zstd libarchive libelf libtiff",
"libva-mesa-driver libxmlb libzip lld llvm-libs mariadb-libs",
"mesa mesa-vdpau minizip-ng mkinitcpio mold netcdf",
"opencl-clover-mesa opencl-rusticl-mesa openucx postgresql",
"postgresql-libs ppsspp qemu-img qemu-system-riscv",
"qemu-system-x86 qgis qt6-base qt6-tools rsync rustup",
"squashfs-tools squashfuse systemd-libs tiled vulkan-radeon",
"wireshark-cli"
],
"optional_for": "xarchiver",
"conflicts_with": null,
"replaces": null,
"installed_size": "1527.00 KiB",
"packager": "Levente Polyak <anthraxx@archlinux.org>",
"build_date": "Sat 11 May 2024 06:14:19 AM +08",
"install_date": "Fri 24 May 2024 09:50:31 AM +08",
"install_reason": "Installed as a dependency for another package",
"install_script": "No",
"validated_by": "Signature",
"extended_data": "pkgtype=pkg"
}
]
<a id="jc.parsers.pacman.parse"></a>
### parse
```python
def parse(data: str,
raw: bool = False,
quiet: bool = False) -> List[Dict[str, Any]]
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Source: [`jc/parsers/pacman.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/pacman.py)
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -98,4 +98,4 @@ Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Source: [`jc/parsers/pci_ids.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/pci_ids.py)
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -109,4 +109,4 @@ Compatibility: linux, darwin, freebsd
Source: [`jc/parsers/ping_s.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/ping_s.py)
Version 1.5 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -90,4 +90,4 @@ Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Source: [`jc/parsers/pip_show.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/pip_show.py)
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.5 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -31,7 +31,7 @@ or
or
$ cat /proc/meminfo | jc --proc-memifno
$ cat /proc/meminfo | jc --proc-meminfo
Usage (module):

View File

@@ -210,4 +210,4 @@ Compatibility: linux
Source: [`jc/parsers/rpm_qi.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/rpm_qi.py)
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -47,6 +47,13 @@ field names
"file_descriptor": string
}
}
"inode_number": string,
"cookie": string,
"cgroup": string,
"v6only": string,
"timer_name": string,
"expire_time": string,
"retrans": string
}
}
]
@@ -310,4 +317,4 @@ Compatibility: linux
Source: [`jc/parsers/ss.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/ss.py)
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -338,4 +338,4 @@ Compatibility: linux
Source: [`jc/parsers/top.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/top.py)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -178,4 +178,4 @@ Compatibility: linux
Source: [`jc/parsers/top_s.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/top_s.py)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -145,4 +145,4 @@ Compatibility: linux, darwin, freebsd
Source: [`jc/parsers/traceroute.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/traceroute.py)
Version 1.6 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -92,4 +92,4 @@ Source: [`jc/parsers/uptime.py`](https://github.com/kellyjonbrazil/jc/blob/maste
This parser can be used with the `--slurp` command-line option.
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.10 by Kelly Brazil (kellyjonbrazil@gmail.com)

172
docs/parsers/wg_show.md Normal file
View File

@@ -0,0 +1,172 @@
[Home](https://kellyjonbrazil.github.io/jc/)
<a id="jc.parsers.wg_show"></a>
# jc.parsers.wg_show
jc - JSON Convert `wg show` command output parser
Parses the output of the `wg show all dump` command, providing structured JSON output for easy integration and analysis.
Usage (cli):
$ wg show all dump | jc --wg-show
or
$ jc wg show all dump
Usage (module):
import jc
result = jc.parse('wg-show', wg_command_output)
Schema:
[
{
"device": string,
"private_key": string,
"public_key": string,
"listen_port": integer,
"fwmark": integer,
"peers": [
{
"public_key": string,
"preshared_key": string,
"endpoint": string,
"latest_handshake": integer,
"transfer_rx": integer,
"transfer_sx": integer,
"persistent_keepalive": integer,
"allowed_ips": [
string
]
}
]
}
]
Examples:
$ wg show all dump | jc --wg-show -p
[
{
"device": "wg0",
"private_key": "aEbVdvHSEp3oofHDNVCsUoaRSxk1Og8/pTLof5yF+1M=",
"public_key": "OIxbQszw1chdO5uigAxpsl4fc/h04yMYafl72gUbakM=",
"listen_port": 51820,
"fwmark": null,
"peers": [
{
"public_key": "sQFGAhSdx0aC7DmTFojzBOW8Ccjv1XV5+N9FnkZu5zc=",
"preshared_key": null,
"endpoint": "79.134.136.199:40036",
"latest_handshake": 1728809756,
"transfer_rx": 1378724,
"transfer_sx": 406524,
"persistent_keepalive": null,
"allowed_ips": ["10.10.0.2/32"]
},
{
"public_key": "B9csmpvrv4Q7gpjc6zAbNNO8hIOYfpBqxmik2aNpwwE=",
"preshared_key": null,
"endpoint": "79.134.136.199:35946",
"latest_handshake": 1728809756,
"transfer_rx": 4884248,
"transfer_sx": 3544596,
"persistent_keepalive": null,
"allowed_ips": ["10.10.0.3/32"]
},
{
"public_key": "miiSYR5UdevREhlWpmnci+vv/dEGLHbNtKu7u1CuOD4=",
"preshared_key": null,
"allowed_ips": ["10.10.0.4/32"]
},
{
"public_key": "gx9+JHLHJvOfBNjTmZ8KQAnThFFiZMQrX1kRaYcIYzw=",
"preshared_key": null,
"endpoint": "173.244.225.194:45014",
"latest_handshake": 1728809827,
"transfer_rx": 1363652,
"transfer_sx": 458252,
"persistent_keepalive": null,
"allowed_ips": ["10.10.0.5/32"]
}
]
}
]
$ wg show all dump | jc --wg-show -p -r
[
{
"device": "wg0",
"private_key": "aEbVdvHSEp3oofHDNVCsUoaRSxk1Og8/pTLof5yF+1M=",
"public_key": "OIxbQszw1chdO5uigAxpsl4fc/h04yMYafl72gUbakM=",
"listen_port": 51820,
"fwmark": null,
"peers": {
"sQFGAhSdx0aC7DmTFojzBOW8Ccjv1XV5+N9FnkZu5zc=": {
"preshared_key": null,
"endpoint": "79.134.136.199:40036",
"latest_handshake": 1728809756,
"transfer_rx": 1378724,
"transfer_sx": 406524,
"persistent_keepalive": -1,
"allowed_ips": ["10.10.0.2/32"]
},
"B9csmpvrv4Q7gpjc6zAbNNO8hIOYfpBqxmik2aNpwwE=": {
"preshared_key": null,
"endpoint": "79.134.136.199:35946",
"latest_handshake": 1728809756,
"transfer_rx": 4884248,
"transfer_sx": 3544596,
"persistent_keepalive": -1,
"allowed_ips": ["10.10.0.3/32"]
},
"miiSYR5UdevREhlWpmnci+vv/dEGLHbNtKu7u1CuOD4=": {
"preshared_key": null,
"allowed_ips": ["10.10.0.4/32"]
},
"gx9+JHLHJvOfBNjTmZ8KQAnThFFiZMQrX1kRaYcIYzw=": {
"preshared_key": null,
"endpoint": "173.244.225.194:45014",
"latest_handshake": 1728809827,
"transfer_rx": 1363652,
"transfer_sx": 458252,
"persistent_keepalive": -1,
"allowed_ips": ["10.10.0.5/32"]
}
}
}
]
<a id="jc.parsers.wg_show.parse"></a>
### parse
```python
def parse(data: str,
raw: bool = False,
quiet: bool = False) -> List[Dict[str, Any]]
```
Main text parsing function.
Parses the output of the `wg` command, specifically `wg show all dump`, into structured JSON format.
Parameters:
data: (str) Text data to parse, typically the output from `wg show all dump`
raw: (bool) If True, returns unprocessed output
quiet: (bool) Suppress warning messages if True
Returns:
List[Dict]: Parsed data in JSON-friendly format, either raw or processed.
### Parser Information
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Source: [`jc/parsers/wg_show.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/wg_show.py)
Version 1.0 by Hamza Saht (hamzasaht01@gmail.com)

View File

@@ -111,4 +111,4 @@ Compatibility: linux, darwin, cygwin, win32, aix, freebsd
Source: [`jc/parsers/yaml.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/yaml.py)
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -34,11 +34,11 @@ Schema:
"config": [
{
"name": string,
"state": string,
"read": integer,
"write": integer,
"checksum": integer,
"errors": string,
"state": string/null,
"read": integer/null,
"write": integer/null,
"checksum": integer/null,
"errors": string/null,
}
],
"errors": string
@@ -162,4 +162,4 @@ Compatibility: linux, darwin, freebsd
Source: [`jc/parsers/zpool_status.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/zpool_status.py)
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)

View File

@@ -55,7 +55,10 @@ Returns:
### convert_size_to_int
```python
def convert_size_to_int(size: str, binary: bool = False) -> Optional[int]
def convert_size_to_int(size: str,
binary: bool = False,
posix_mode: bool = False,
decimal_bias: bool = False) -> Optional[int]
```
Parse a human readable data size and return the number of bytes.
@@ -66,6 +69,12 @@ Parameters:
binary: (boolean) `True` to use binary multiples of bytes
(base-2) for ambiguous unit symbols and names,
`False` to use decimal multiples of bytes (base-10).
posix_mode: (boolean) Treat one-letter units (k, m, g, etc.) as
binary.
decimal_bias: (boolean) `True` to treat slightly ambiguous two-
letter unit symbols ending in "i" (e.g. Ki, Gi) to
use decimal multiples of bytes (base-10). `False`
(default) to use binary multiples of bytes.
Returns:
integer/None Integer if successful conversion, otherwise None
@@ -85,6 +94,10 @@ gigabytes, terabytes and petabytes. Some examples:
1000
>>> convert_size_to_int('1 KiB')
1024
>>> convert_size_to_int('1 Ki')
1024
>>> convert_size_to_int('1 Ki', decimal_bias=True)
1000
>>> convert_size_to_int('1 KB', binary=True)
1024
>>> convert_size_to_int('1.5 GB')

View File

@@ -401,11 +401,16 @@ class JcCli():
self.json_indent = 2
self.json_separators = None
# Convert any non-serializable object to a string
def string_serializer(data):
return str(data)
j_string = json.dumps(
self.data_out,
indent=self.json_indent,
separators=self.json_separators,
ensure_ascii=self.ascii_only
ensure_ascii=self.ascii_only,
default=string_serializer
)
if not self.mono and PYGMENTS_INSTALLED:

View File

@@ -3,6 +3,7 @@
import sys
from typing import Any, Dict, List, Tuple, Iterator, Optional, Union
CustomColorType = Dict[Any, str]
JSONDictType = Dict[str, Any]
StreamingOutputType = Iterator[Union[JSONDictType, Tuple[BaseException, str]]]
@@ -42,11 +43,3 @@ if sys.version_info >= (3, 8):
else:
ParserInfoType = Dict
TimeStampFormatType = Dict
try:
from pygments.token import (Name, Number, String, Keyword)
CustomColorType = Dict[Union[Name.Tag, Number, String, Keyword], str]
except Exception:
CustomColorType = Dict # type: ignore

View File

@@ -10,7 +10,7 @@ from jc import appdirs
from jc import utils
__version__ = '1.25.2'
__version__ = '1.25.4'
parsers: List[str] = [
'acpi',
@@ -75,6 +75,7 @@ parsers: List[str] = [
'iostat',
'iostat-s',
'ip-address',
'ipconfig',
'iptables',
'ip-route',
'iw-scan',
@@ -107,6 +108,7 @@ parsers: List[str] = [
'openvpn',
'os-prober',
'os-release',
'pacman',
'passwd',
'path',
'path-list',
@@ -224,6 +226,7 @@ parsers: List[str] = [
'vmstat-s',
'w',
'wc',
'wg-show',
'who',
'x509-cert',
'x509-csr',

View File

@@ -4,11 +4,11 @@ Requires the `-sqq` options in `apt-get`.
Usage (cli):
$ apt-get -sqq | jc --apt-get-sqq
$ apt-get -sqq upgrade | jc --apt-get-sqq
or
$ jc apt-get -sqq
$ jc apt-get -sqq full-upgrade
Usage (module):
@@ -30,7 +30,7 @@ Schema:
Examples:
$ apt-get -sqq | jc --apt-get-sqq -p
$ apt-get -sqq upgrade | jc --apt-get-sqq -p
[
{
"operation": "unpack",
@@ -98,7 +98,7 @@ Examples:
}
]
$ apt-get -sqq | jc --apt-get-sqq -p -r
$ apt-get -sqq upgrade | jc --apt-get-sqq -p -r
[
{
"operation": "Inst",
@@ -174,7 +174,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
version = '1.1'
description = '`apt-get -sqq` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'

View File

@@ -28,6 +28,8 @@ a controller and a device but there might be fields corresponding to one entity.
Controller:
[
{
"manufacturer": string,
"version": string,
"name": string,
"is_default": boolean,
"is_public": boolean,
@@ -65,7 +67,8 @@ a controller and a device but there might be fields corresponding to one entity.
"rssi": int,
"txpower": int,
"uuids": array,
"modalias": string
"modalias": string,
"battery_percentage": int
}
]
@@ -96,7 +99,8 @@ Examples:
"Headset HS (00001831-0000-1000-8000-00805f9b34fb)"
],
"rssi": -52,
"txpower": 4
"txpower": 4,
"battery_percentage": 70
}
]
"""
@@ -108,7 +112,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
version = '1.3'
description = '`bluetoothctl` command parser'
author = 'Jake Ob'
author_email = 'iakopap at gmail.com'
@@ -125,6 +129,8 @@ try:
Controller = TypedDict(
"Controller",
{
"manufacturer": str,
"version": str,
"name": str,
"is_default": bool,
"is_public": bool,
@@ -161,7 +167,8 @@ try:
"rssi": int,
"txpower": int,
"uuids": List[str],
"modalias": str
"modalias": str,
"battery_percentage": int
},
)
except ImportError:
@@ -172,7 +179,9 @@ except ImportError:
_controller_head_pattern = r"Controller (?P<address>([0-9A-F]{2}:){5}[0-9A-F]{2}) (?P<name>.+)"
_controller_line_pattern = (
r"(\s*Name:\s*(?P<name>.+)"
r"(\s*Manufacturer:\s*(?P<manufacturer>.+)"
+ r"|\s*Version:\s*(?P<version>.+)"
+ r"|\s*Name:\s*(?P<name>.+)"
+ r"|\s*Alias:\s*(?P<alias>.+)"
+ r"|\s*Class:\s*(?P<class>.+)"
+ r"|\s*Powered:\s*(?P<powered>.+)"
@@ -200,6 +209,8 @@ def _parse_controller(next_lines: List[str]) -> Optional[Controller]:
return None
controller: Controller = {
"manufacturer": '',
"version": '',
"name": '',
"is_default": False,
"is_public": False,
@@ -238,7 +249,11 @@ def _parse_controller(next_lines: List[str]) -> Optional[Controller]:
matches = result.groupdict()
if matches["name"]:
if matches["manufacturer"]:
controller["manufacturer"] = matches["manufacturer"]
elif matches["version"]:
controller["version"] = matches["version"]
elif matches["name"]:
controller["name"] = matches["name"]
elif matches["alias"]:
controller["alias"] = matches["alias"]
@@ -280,6 +295,7 @@ _device_line_pattern = (
+ r"|\s*Modalias:\s*(?P<modalias>.+)"
+ r"|\s*RSSI:\s*(?P<rssi>.+)"
+ r"|\s*TxPower:\s*(?P<txpower>.+)"
+ r"|\s*Battery\sPercentage:\s*0[xX][0-9a-fA-F]*\s*\((?P<battery_percentage>[0-9]+)\)"
+ r"|\s*UUID:\s*(?P<uuid>.+))"
)
@@ -317,7 +333,8 @@ def _parse_device(next_lines: List[str], quiet: bool) -> Optional[Device]:
"rssi": 0,
"txpower": 0,
"uuids": [],
"modalias": ''
"modalias": '',
"battery_percentage": 0
}
if name.endswith("(public)"):
@@ -381,6 +398,13 @@ def _parse_device(next_lines: List[str], quiet: bool) -> Optional[Device]:
device["uuids"].append(matches["uuid"])
elif matches["modalias"]:
device["modalias"] = matches["modalias"]
elif matches["battery_percentage"]:
battery_percentage = matches["battery_percentage"]
try:
device["battery_percentage"] = int(battery_percentage)
except ValueError:
if not quiet:
jc.utils.warning_message([f"{next_line} : battery_percentage - {battery_percentage} is not int-able"])
return device

View File

@@ -101,7 +101,7 @@ import jc.parsers.universal
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '2.0'
version = '2.1'
description = '`df` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -127,6 +127,7 @@ def _process(proc_data):
"""
int_list = {'use_percent', 'capacity_percent', 'ifree', 'iused', 'iused_percent'}
size_list = {'size', 'used', 'available'}
posix_mode = False
for entry in proc_data:
if 'avail' in entry:
@@ -134,6 +135,7 @@ def _process(proc_data):
if 'use%' in entry:
entry['use_percent'] = entry.pop('use%')
posix_mode = True
if 'capacity' in entry:
entry['capacity_percent'] = entry.pop('capacity')
@@ -159,7 +161,7 @@ def _process(proc_data):
# parse the size, used, and available fields to bytes
for key in entry:
if key in size_list:
entry[key] = jc.utils.convert_size_to_int(entry[key])
entry[key] = jc.utils.convert_size_to_int(entry[key], posix_mode=posix_mode)
# convert integers
for key in entry:

View File

@@ -172,7 +172,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
version = '1.1'
description = '`ethtool` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -248,10 +248,11 @@ def _parse_default(data: str) -> JSONDictType:
supported_link_modes: List[str] = []
supported_fec_modes: List[str] = []
advertised_link_modes: List[str] = []
link_partner_advertised_link_modes: List[str] = []
advertised_fec_modes: List[str] = []
current_message_level: List[str] = []
mode: str = '' # supported_link_modes, supported_fec_modes, advertised_link_modes,
# advertised_fec_modes, current_message_level
# link_partner_advertised_link_modes, advertised_fec_modes, current_message_level
for line in filter(None, data.splitlines()):
@@ -294,6 +295,14 @@ def _parse_default(data: str) -> JSONDictType:
mode = 'advertised_link_modes'
continue
if 'Link partner advertised link modes:' in line and 'Not reported' not in line:
_, val = line.split(':', maxsplit=1)
val = val.strip()
val_list = val.split()
link_partner_advertised_link_modes.extend(val_list)
mode = 'link_partner_advertised_link_modes'
continue
if 'Advertised FEC modes:' in line and 'Not reported' not in line:
_, val = line.split(':', maxsplit=1)
val = val.strip()
@@ -326,6 +335,12 @@ def _parse_default(data: str) -> JSONDictType:
advertised_link_modes.extend(val_list)
continue
if mode == 'link_partner_advertised_link_modes':
val = line.strip()
val_list = val.split()
link_partner_advertised_link_modes.extend(val_list)
continue
if mode == 'advertised_fec_modes':
val = line.strip()
val_list = val.split()
@@ -346,6 +361,7 @@ def _parse_default(data: str) -> JSONDictType:
(supported_link_modes, 'supported_link_modes'),
(supported_fec_modes, 'supported_fec_modes'),
(advertised_link_modes, 'advertised_link_modes'),
(link_partner_advertised_link_modes, 'link_partner_advertised_link_modes'),
(advertised_fec_modes, 'advertised_fec_modes'),
(current_message_level, 'current_message_level')
]

View File

@@ -50,6 +50,12 @@ Schema:
"deletions": integer,
"files": [
string
],
"file_stats": [
{
"name": string,
"lines_changed": integer
}
]
}
}
@@ -74,6 +80,16 @@ Examples:
"files": [
"docs/parsers/git_log.md",
"jc/parsers/git_log.py"
],
"file_stats": [
{
"name": "docs/parsers/git_log.md",
"lines_changed": 3
},
{
"name": "jc/parsers/git_log.py",
"lines_changed": 7
}
]
},
"message": "add timestamp docs and examples",
@@ -95,6 +111,28 @@ Examples:
"jc/parsers/git_log.py",
"jc/utils.py",
"man/jc.1"
],
"file_stats": [
{
"name": "docs/parsers/git_log.md",
"lines_changed": 3
},
{
"name": "docs/utils.md",
"lines_changed": 7
},
{
"name": "jc/parsers/git_log.py",
"lines_changed": 1
},
{
"name": "jc/utils.py",
"lines_changed": 12
},
{
"name": "man/jc.1",
"lines_changed": 14
}
]
},
"message": "add calculated timestamp",
@@ -118,6 +156,16 @@ Examples:
"files": [
"docs/parsers/git_log.md",
"jc/parsers/git_log.py"
],
"file_stats": [
{
"name": "docs/parsers/git_log.md",
"lines_changed": "3"
},
{
"name": "jc/parsers/git_log.py",
"lines_changed": "7"
}
]
},
"message": "add timestamp docs and examples"
@@ -137,6 +185,28 @@ Examples:
"jc/parsers/git_log.py",
"jc/utils.py",
"man/jc.1"
],
"file_stats": [
{
"name": "docs/parsers/git_log.md",
"lines_changed": "3"
},
{
"name": "docs/utils.md",
"lines_changed": "7"
},
{
"name": "jc/parsers/git_log.py",
"lines_changed": "1"
},
{
"name": "jc/utils.py",
"lines_changed": "12"
},
{
"name": "man/jc.1",
"lines_changed": "14"
}
]
},
"message": "add calculated timestamp"
@@ -145,7 +215,7 @@ Examples:
]
"""
import re
from typing import List, Dict
from typing import List, Dict, Any
import jc.utils
hash_pattern = re.compile(r'(?:[0-9]|[a-f]){40}')
@@ -153,7 +223,7 @@ changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed)(?:,\s+(?P<ins
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.4'
version = '1.5'
description = '`git log` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -177,7 +247,7 @@ def _process(proc_data: List[Dict]) -> List[Dict]:
List of Dictionaries. Structured to conform to the schema.
"""
int_list = {'files_changed', 'insertions', 'deletions'}
int_list = {'files_changed', 'insertions', 'deletions', 'lines_changed'}
for entry in proc_data:
if 'date' in entry:
@@ -190,6 +260,13 @@ def _process(proc_data: List[Dict]) -> List[Dict]:
if key in int_list:
entry['stats'][key] = jc.utils.convert_to_int(entry['stats'][key])
if 'file_stats' in entry['stats']:
file_stats = entry['stats']['file_stats']
for file_entry in file_stats:
for key in file_entry:
if key in int_list:
file_entry[key] = jc.utils.convert_to_int(file_entry[key])
return proc_data
@@ -251,6 +328,7 @@ def parse(
output_line: Dict = {}
message_lines: List[str] = []
file_list: List[str] = []
file_stats_list: List[Dict[str, Any]] = []
if jc.utils.has_data(data):
@@ -263,10 +341,14 @@ def parse(
if file_list:
output_line['stats']['files'] = file_list
if file_stats_list:
output_line['stats']['file_stats'] = file_stats_list
raw_output.append(output_line)
output_line = {}
message_lines = []
file_list = []
file_stats_list = []
output_line = {
'commit': line_list[0],
'message': line_list[1]
@@ -282,10 +364,14 @@ def parse(
if file_list:
output_line['stats']['files'] = file_list
if file_stats_list:
output_line['stats']['file_stats'] = file_stats_list
raw_output.append(output_line)
output_line = {}
message_lines = []
file_list = []
file_stats_list = []
output_line['commit'] = line_list[1]
continue
@@ -319,8 +405,19 @@ def parse(
if line.startswith(' ') and 'changed, ' not in line:
# this is a file name
file_name = line.split('|')[0].strip()
file_line_split = line.split('|')
file_name = file_line_split[0].strip()
file_list.append(file_name)
if len(file_line_split) > 1:
file_stats = file_line_split[1].strip()
lines_changed_str = file_stats.split(' ')
lines_changed_count_str = lines_changed_str[0].strip()
file_stat = {}
file_stat["name"] = file_name
file_stat["lines_changed"] = lines_changed_count_str
file_stats_list.append(file_stat)
continue
if line.startswith(' ') and 'changed, ' in line:
@@ -344,6 +441,9 @@ def parse(
if file_list:
output_line['stats']['files'] = file_list
if file_stats_list:
output_line['stats']['file_stats'] = file_stats_list
raw_output.append(output_line)
return raw_output if raw else _process(raw_output)

View File

@@ -51,6 +51,12 @@ Schema:
"deletions": integer,
"files": [
string
],
"file_stats": [
{
"name": string,
"lines_changed": integer
}
]
}
@@ -73,7 +79,7 @@ Examples:
...
"""
import re
from typing import List, Dict, Iterable, Union
from typing import List, Dict, Any, Iterable, Union
import jc.utils
from jc.parsers.git_log import _parse_name_email
from jc.streaming import (
@@ -88,7 +94,7 @@ changes_pattern = re.compile(r'\s(?P<files>\d+)\s+(files? changed)(?:,\s+(?P<ins
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.4'
version = '1.5'
description = '`git log` command streaming parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -112,7 +118,7 @@ def _process(proc_data: Dict) -> Dict:
Dictionary. Structured data to conform to the schema.
"""
int_list = {'files_changed', 'insertions', 'deletions'}
int_list = {'files_changed', 'insertions', 'deletions', 'lines_changed'}
if 'date' in proc_data:
ts = jc.utils.timestamp(proc_data['date'], format_hint=(1100,))
@@ -124,6 +130,13 @@ def _process(proc_data: Dict) -> Dict:
if key in int_list:
proc_data['stats'][key] = jc.utils.convert_to_int(proc_data['stats'][key])
if 'file_stats' in proc_data['stats']:
file_stats = proc_data['stats']['file_stats']
for file_entry in file_stats:
for key in file_entry:
if key in int_list:
file_entry[key] = jc.utils.convert_to_int(file_entry[key])
return proc_data
@@ -168,6 +181,7 @@ def parse(
output_line: Dict = {}
message_lines: List[str] = []
file_list: List[str] = []
file_stats_list: List[Dict[str, Any]] = []
for line in data:
try:
@@ -184,11 +198,15 @@ def parse(
if file_list:
output_line['stats']['files'] = file_list
if file_stats_list:
output_line['stats']['file_stats'] = file_stats_list
yield output_line if raw else _process(output_line)
output_line = {}
message_lines = []
file_list = []
file_stats_list = []
output_line = {
'commit': line_list[0],
'message': line_list[1]
@@ -204,11 +222,15 @@ def parse(
if file_list:
output_line['stats']['files'] = file_list
if file_stats_list:
output_line['stats']['file_stats'] = file_stats_list
yield output_line if raw else _process(output_line)
output_line = {}
message_lines = []
file_list = []
file_stats_list = []
output_line['commit'] = line_list[1]
continue
@@ -242,8 +264,19 @@ def parse(
if line.startswith(' ') and 'changed, ' not in line:
# this is a file name
file_name = line.split('|')[0].strip()
file_line_split = line.split('|')
file_name = file_line_split[0].strip()
file_list.append(file_name)
if len(file_line_split) > 1:
file_stats = file_line_split[1].strip()
lines_changed_str = file_stats.split(' ')
lines_changed_count_str = lines_changed_str[0].strip()
file_stat = {}
file_stat["name"] = file_name
file_stat["lines_changed"] = lines_changed_count_str
file_stats_list.append(file_stat)
continue
if line.startswith(' ') and 'changed, ' in line:
@@ -274,6 +307,9 @@ def parse(
if file_list:
output_line['stats']['files'] = file_list
if file_stats_list:
output_line['stats']['file_stats'] = file_stats_list
yield output_line if raw else _process(output_line)
except Exception as e:

View File

@@ -212,14 +212,14 @@ Examples:
"""
import re
from ipaddress import IPv4Network
from typing import List, Dict, Optional
from typing import List, Dict
from jc.jc_types import JSONDictType
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '2.3'
version = '2.4'
description = '`ifconfig` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -548,6 +548,11 @@ def parse(
broadcast\s+(?P<broadcast>(?:[0-9]{1,3}\.){3}[0-9]{1,3}))?
''', re.IGNORECASE | re.VERBOSE
)
re_freebsd_ipv4_utun = re.compile(r'''
inet\s(?P<address>(?:[0-9]{1,3}\.){3}[0-9]{1,3})\s-->\s(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3})\s
netmask\s(?P<mask>\S*)
''', re.IGNORECASE | re.VERBOSE
)
re_freebsd_ipv6 = re.compile(r'''
\s?inet6\s(?P<address>.*?)
(?:\%(?P<scope_id>\w+\d+))?\s
@@ -624,14 +629,14 @@ def parse(
re_openbsd_rx_stats, re_openbsd_tx, re_openbsd_tx_stats
]
re_freebsd = [
re_freebsd_interface, re_freebsd_ipv4, re_freebsd_ipv4_v2, re_freebsd_ipv6,
re_freebsd_interface, re_freebsd_ipv4, re_freebsd_ipv4_v2, re_freebsd_ipv4_utun, re_freebsd_ipv6,
re_freebsd_details, re_freebsd_status, re_freebsd_nd6_options, re_freebsd_plugged,
re_freebsd_vendor_pn_sn_date, re_freebsd_temp_volts, re_freebsd_hwaddr, re_freebsd_media,
re_freebsd_tx_rx_power, re_freebsd_options
]
interface_patterns = [re_linux_interface, re_openbsd_interface, re_freebsd_interface]
ipv4_patterns = [re_linux_ipv4, re_openbsd_ipv4, re_freebsd_ipv4, re_freebsd_ipv4_v2]
ipv4_patterns = [re_linux_ipv4, re_openbsd_ipv4, re_freebsd_ipv4, re_freebsd_ipv4_v2, re_freebsd_ipv4_utun]
ipv6_patterns = [re_linux_ipv6, re_openbsd_ipv6, re_freebsd_ipv6]
if jc.utils.has_data(data):

View File

@@ -533,7 +533,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.4'
version = '1.5'
description = 'IPv4 and IPv6 Address string parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -661,8 +661,18 @@ def parse(
if interface.version == 4:
ip_split = ip_exploded.split('.')
else:
ip_split = ip_exploded.split(':')
# regular IPv6
if not '.' in ip_exploded:
ip_split = ip_exploded.split(':')
# IPv4 mapped IPv6
else:
ip_split_v6 = ip_exploded.split(':')[:-1]
ip_split_v4_mapped_str = ip_exploded.split(':')[-1]
ip_split_v4_mapped = ip_split_v4_mapped_str.split('.')
ip_split = ip_split_v6 + ip_split_v4_mapped
# fix for ipv6-only attributes
scope_id = None

809
jc/parsers/ipconfig.py Normal file
View File

@@ -0,0 +1,809 @@
r"""jc - JSON Convert `ipconfig` Windows command output parser
Usage (cli):
$ ipconfig /all | jc --ipconfig
$ ipconfig | jc --ipconfig
$ jc ipconfig /all
Usage (module):
import jc
result = jc.parse('ipconfig', ipconfig_command_output)
Schema:
{
"host_name": string,
"primary_dns_suffix": string,
"node_type": string,
"ip_routing_enabled": boolean,
"wins_proxy_enabled": boolean,
"dns_suffix_search_list": [
string
],
"adapters": [
{
"name_long": string,
"name": string,
"type": string,
"connection_specific_dns_suffix": string,
"connection_specific_dns_suffix_search_list": [
string
]
"description": string,
"physical_address": string,
"dhcp_enabled": boolean,
"autoconfiguration_enabled": boolean,
"ipv6_addresses": [
{
"address": string,
"status": string,
},
],
"temporary_ipv6_addresses": [
{
"address": string,
"status": string,
},
],
"link_local_ipv6_addresses": [
{
"address": string,
"status": string,
"prefix_length": integer,
}
],
"ipv4_addresses": [
{
"address": string, # [2]
"subnet_mask": string,
"status": string,
"autoconfigured": boolean # [1]
}
],
"default_gateways": [
string
],
"dhcp_server": null,
"dhcpv6_iaid": string,
"dhcpv6_client_duid": string,
"dns_servers": [
string
],
"primary_wins_server": string,
"lease_expires": string,
"lease_expires_epoch": integer, # [0]
"lease_expires_iso": string,
"lease_obtained": string,
"lease_obtained_epoch": integer, # [0]
"lease_obtained_iso": string,
"netbios_over_tcpip": boolean,
"media_state": string,
"extras": [
<string>: string
]
}
],
"extras": []
}
Notes:
[0] - The epoch calculated timestamp field is naive. (i.e. based on
the local time of the system the parser is run on)
[1] - 'autoconfigured' under 'ipv4_address' is only providing
indication if the ipv4 address was labeled as "Autoconfiguration
IPv4 Address" vs "IPv4 Address". It does not infer any
information from other fields
[2] - Windows XP uses 'IP Address' instead of 'IPv4 Address'. Both
values are parsed to the 'ipv4_address' object for consistency
Examples:
$ ipconfig /all | jc --ipconfig -p
{
"host_name": "DESKTOP-WIN11-HOME",
"primary_dns_suffix": null,
"node_type": "Hybrid",
"ip_routing_enabled": false,
"wins_proxy_enabled": false,
"dns_suffix_search_list": [
"localdomain"
],
"adapters": [
{
"name_long": "Ethernet adapter Ethernet",
"name": "Ethernet",
"type": "Ethernet",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "Intel(R) I211 Gigabit Network Connection",
"physical_address": "24-4B-FE-AB-43-C3",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Ethernet adapter Ethernet 2",
"name": "Ethernet 2",
"type": "Ethernet",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "Realtek PCIe 2.5GbE Family Controller",
"physical_address": "24-4B-FE-57-3D-F2",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Unknown adapter OpenVPN Data Channel Offload for NordVPN",
"name": "OpenVPN Data Channel Offload for NordVPN",
"type": "Unknown",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "OpenVPN Data Channel Offload",
"physical_address": null,
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Unknown adapter Local Area Connection",
"name": "Local Area Connection",
"type": "Unknown",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "TAP-NordVPN Windows Adapter V9",
"physical_address": "00-FF-4C-F4-5E-49",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Wireless LAN adapter Local Area Connection* 1",
"name": "Local Area Connection* 1",
"type": "Wireless LAN",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "Microsoft Wi-Fi Direct Virtual Adapter",
"physical_address": "A8-7E-EA-5A-7F-DE",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Wireless LAN adapter Local Area Connection* 2",
"name": "Local Area Connection* 2",
"type": "Wireless LAN",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "Microsoft Wi-Fi Direct Virtual Adapter #2",
"physical_address": "AA-7E-EA-F3-64-C3",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
},
{
"name_long": "Ethernet adapter VMware Network Adapter VMnet1",
"name": "VMware Network Adapter VMnet1",
"type": "Ethernet",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "VMware Virtual Ethernet Adapter for VMnet1",
"physical_address": "00-50-56-CC-27-73",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [
{
"address": "fe80::f47d:9c7f:69dc:591e",
"prefix_length": 8,
"status": "Preferred"
}
],
"ipv4_addresses": [
{
"address": "192.168.181.1",
"subnet_mask": "255.255.255.0",
"status": "Preferred",
"autoconfigured": false
}
],
"default_gateways": [],
"dhcp_server": "192.168.181.254",
"dhcpv6_iaid": "771772502",
"dhcpv6_client_duid": "00-01-00-01-2C-CF-19-EB-24-4B-FE-5B-9B-E6",
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": "2024-09-19T18:01:29",
"lease_obtained": "2024-09-19T08:31:29",
"netbios_over_tcpip": true,
"media_state": null,
"extras": []
},
{
"name_long": "Ethernet adapter VMware Network Adapter VMnet8",
"name": "VMware Network Adapter VMnet8",
"type": "Ethernet",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "VMware Virtual Ethernet Adapter for VMnet8",
"physical_address": "00-50-56-C9-A3-78",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [
{
"address": "fe80::4551:bf0d:59dd:a4f0",
"prefix_length": 10,
"status": "Preferred"
}
],
"ipv4_addresses": [
{
"address": "192.168.213.1",
"subnet_mask": "255.255.255.0",
"status": "Preferred",
"autoconfigured": false
}
],
"default_gateways": [],
"dhcp_server": "192.168.213.254",
"dhcpv6_iaid": "788549718",
"dhcpv6_client_duid": "00-01-00-01-2C-CF-19-EB-24-4B-FE-5B-9B-E6",
"dns_servers": [],
"primary_wins_server": "192.168.213.2",
"lease_expires": "2024-09-19T18:01:29",
"lease_obtained": "2024-09-19T08:31:29",
"netbios_over_tcpip": true,
"media_state": null,
"extras": []
},
{
"name_long": "Wireless LAN adapter Wi-Fi",
"name": "Wi-Fi",
"type": "Wireless LAN",
"connection_specific_dns_suffix": "localdomain",
"connection_specific_dns_suffix_search_list": [],
"description": "Intel(R) Wi-Fi 6 AX200 160MHz",
"physical_address": "A8-7E-EA-55-26-B0",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [
{
"address": "fd63:cc9c:65eb:3f95:57c2:aa:10d8:db08",
"status": "Preferred"
}
],
"temporary_ipv6_addresses": [
{
"address": "fd63:cc9c:65eb:3f95:8928:348e:d692:b7ef",
"status": "Preferred"
}
],
"link_local_ipv6_addresses": [
{
"address": "fe80::4fae:1380:5a1b:8b6b",
"prefix_length": 11,
"status": "Preferred"
}
],
"ipv4_addresses": [
{
"address": "192.168.1.169",
"subnet_mask": "255.255.255.0",
"status": "Preferred",
"autoconfigured": false
}
],
"default_gateways": [
"192.168.1.1"
],
"dhcp_server": "192.168.1.1",
"dhcpv6_iaid": "162037482",
"dhcpv6_client_duid": "00-01-00-01-2C-CF-19-EB-24-4B-FE-5B-9B-E6",
"dns_servers": [
"192.168.1.1"
],
"primary_wins_server": null,
"lease_expires": "2024-09-20T08:31:30",
"lease_obtained": "2024-09-19T08:31:30",
"netbios_over_tcpip": true,
"media_state": null,
"extras": []
},
{
"name_long": "Ethernet adapter Bluetooth Network Connection",
"name": "Bluetooth Network Connection",
"type": "Ethernet",
"connection_specific_dns_suffix": null,
"connection_specific_dns_suffix_search_list": [],
"description": "Bluetooth Device (Personal Area Network)",
"physical_address": "A8-7E-EA-43-23-14",
"dhcp_enabled": true,
"autoconfiguration_enabled": true,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": null,
"dhcpv6_iaid": null,
"dhcpv6_client_duid": null,
"dns_servers": [],
"primary_wins_server": null,
"lease_expires": null,
"lease_obtained": null,
"netbios_over_tcpip": null,
"media_state": "Media disconnected",
"extras": []
}
],
"extras": []
}
"""
from datetime import datetime
import re
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`ipconfig` Windows command parser'
author = 'joehacksalot'
author_email = 'joehacksalot@gmail.com'
compatible = ['windows']
magic_commands = ['ipconfig']
tags = ['command']
__version__ = info.version
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
Parsed dictionary. The raw and processed data structures are the same.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output = {}
if jc.utils.has_data(data):
# Initialize the parsed output dictionary with all fields set to None or empty lists
raw_output = _parse(data)
return raw_output if raw else _process(raw_output)
def _process_ipv6_address(ip_address):
address_split = ip_address["address"].split('%')
try:
if len(address_split) > 1:
address = address_split[0]
prefix_length = int(address_split[1])
else:
address = ip_address["address"]
prefix_length = None
except:
address = ip_address["address"]
prefix_length = None
return {
"address": address,
"prefix_length": prefix_length,
"status": ip_address["status"]
}
def _process_ipv4_address(ip_address):
autoconfigured = True if ip_address.get("autoconfigured","") is not None and 'autoconfigured' in ip_address.get("autoconfigured","") else False
subnet_mask = ip_address["subnet_mask"]
return {
"address": ip_address["address"],
"subnet_mask": subnet_mask,
"status": ip_address["status"],
"autoconfigured": autoconfigured
}
def _process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (Dictionary) raw structured data to process
Returns:
Processed Dictionary. Structured data to conform to the schema.
"""
processed = proc_data
if "ip_routing_enabled" in processed and processed["ip_routing_enabled"] is not None:
processed["ip_routing_enabled"] = (processed["ip_routing_enabled"].lower() == "yes")
if "wins_proxy_enabled" in processed and processed["wins_proxy_enabled"] is not None:
processed["wins_proxy_enabled"] = (processed["wins_proxy_enabled"].lower() == "yes")
for adapter in processed["adapters"]:
if "dhcp_enabled" in adapter and adapter["dhcp_enabled"] is not None:
adapter["dhcp_enabled"] = (adapter["dhcp_enabled"].lower() == "yes")
if "autoconfiguration_enabled" in adapter and adapter["autoconfiguration_enabled"] is not None:
adapter["autoconfiguration_enabled"] = (adapter["autoconfiguration_enabled"].lower() == "yes")
if "netbios_over_tcpip" in adapter and adapter["netbios_over_tcpip"] is not None:
adapter["netbios_over_tcpip"] = (adapter["netbios_over_tcpip"].lower() == "enabled")
if "lease_expires" in adapter and adapter["lease_expires"]:
ts = jc.utils.timestamp(adapter['lease_expires'], format_hint=(1720,))
adapter["lease_expires_epoch"] = ts.naive
adapter["lease_expires_iso"] = ts.iso
if "lease_obtained" in adapter and adapter["lease_obtained"]:
ts = jc.utils.timestamp(adapter['lease_obtained'], format_hint=(1720,))
adapter["lease_obtained_epoch"] = ts.naive
adapter["lease_obtained_iso"] = ts.iso
adapter["link_local_ipv6_addresses"] = [_process_ipv6_address(address) for address in adapter.get("link_local_ipv6_addresses", [])]
adapter["ipv4_addresses"] = [_process_ipv4_address(address) for address in adapter.get("ipv4_addresses", [])]
return processed
class _PushbackIterator:
def __init__(self, iterator):
self.iterator = iterator
self.pushback_stack = []
def __iter__(self):
return self
def __next__(self):
if self.pushback_stack:
return self.pushback_stack.pop()
else:
return next(self.iterator)
def pushback(self, value):
self.pushback_stack.append(value)
def _parse(data):
# Initialize the parsed output dictionary with all fields set to None or empty lists
parse_output = {
"host_name": None,
"primary_dns_suffix": None,
"node_type": None,
"ip_routing_enabled": None,
"wins_proxy_enabled": None,
"dns_suffix_search_list": [],
"adapters": [],
"extras": [] # To store unrecognized fields
}
lines = data.splitlines()
lines = [line.rstrip() for line in lines if line.strip() != ""]
line_iter = _PushbackIterator(iter(lines))
adapter = None
in_adapter_section = False
for line in line_iter:
line = line.rstrip()
# Skip empty lines
if not line.strip():
continue
# Header Section
if not in_adapter_section:
if "Windows IP Configuration" in line:
continue
elif _is_adapter_start_line(line):
# Start of Adapter Section
in_adapter_section = True
adapter_name = line.strip(":").strip()
adapter = _initialize_adapter(adapter_name)
parse_output["adapters"].append(adapter)
elif line.startswith(" "):
key, value = _parse_line(line)
if key:
_parse_header_line(parse_output, key, value, line_iter)
else:
continue
else:
# Adapter Sections
if _is_adapter_start_line(line):
# Start of new adapter
adapter_name = line.strip(":").strip()
adapter = _initialize_adapter(adapter_name)
parse_output["adapters"].append(adapter)
elif line.startswith(" "):
key, value = _parse_line(line)
if key:
_parse_adapter_line(adapter, key, value, line_iter)
else:
continue
return parse_output
def _is_adapter_start_line(line):
# Detect adapter start lines, e.g., "Ethernet adapter Ethernet:"
return re.match(r"^[^\s].*adapter.*:", line, re.IGNORECASE)
def _initialize_adapter(adapter_name):
adapter_name_split = adapter_name.split(" adapter ", 1)
if len(adapter_name_split) > 1:
adapter_type = adapter_name_split[0]
adapter_short_name = adapter_name_split[1]
else:
adapter_type = None
adapter_short_name = adapter_name
# Initialize adapter dictionary with all fields set to None or empty lists
return {
"name_long": adapter_name,
"name": adapter_short_name,
"type": adapter_type,
"connection_specific_dns_suffix": None,
"connection_specific_dns_suffix_search_list": [],
"description": None,
"physical_address": None,
"dhcp_enabled": None,
"autoconfiguration_enabled": None,
"ipv6_addresses": [],
"temporary_ipv6_addresses": [],
"link_local_ipv6_addresses": [],
"ipv4_addresses": [],
"default_gateways": [],
"dhcp_server": None,
"dhcpv6_iaid": None,
"dhcpv6_client_duid": None,
"dns_servers": [],
"primary_wins_server": None,
"lease_expires": None,
"lease_obtained": None,
"netbios_over_tcpip": None,
"media_state": None,
"extras": [] # To store unrecognized fields
}
def _parse_line(line):
# Split the line into key and value using ':' or multiple spaces
key_value = re.split(r":", line.strip(), 1)
if len(key_value) == 2:
key, value = key_value
key = key.strip().rstrip('. ')
key = re.sub(r'[^\w]+', '_', key.lower())
value = value.strip() if value.strip() != "" else None
return key, value
else:
return None, None
def _parse_header_line(result, key, value, line_iter):
if key in ["host_name", "primary_dns_suffix", "node_type", "ip_routing_enabled", "wins_proxy_enabled"]:
result[key] = value
elif key == "dns_suffix_search_list":
if value:
result["dns_suffix_search_list"].append(value)
# Process additional entries
_parse_additional_entries(result["dns_suffix_search_list"], line_iter)
else:
# Store unrecognized fields in extras
result["extras"].append({key: value})
def _parse_adapter_line(adapter, key, value, line_iter):
if key in ["connection_specific_dns_suffix","media_state", "description", "physical_address", "dhcp_enabled",
"autoconfiguration_enabled", "dhcpv6_iaid", "dhcpv6_client_duid", "netbios_over_tcpip", "dhcp_server",
"lease_obtained", "lease_expires", "primary_wins_server"]:
adapter[key] = value
elif key in ["ipv6_address", "temporary_ipv6_address", "link_local_ipv6_address"]:
address_dict = _parse_ipv6_address(value)
if key == "ipv6_address":
adapter["ipv6_addresses"].append(address_dict)
elif key == "temporary_ipv6_address":
adapter["temporary_ipv6_addresses"].append(address_dict)
elif key == "link_local_ipv6_address":
adapter["link_local_ipv6_addresses"].append(address_dict)
elif key in ["ipv4_address", "autoconfiguration_ipv4_address", "ip_address", "autoconfiguration_ip_address"]:
ipv4_address_dict = _parse_ipv4_address(value, key, line_iter)
adapter["ipv4_addresses"].append(ipv4_address_dict)
elif key == "connection_specific_dns_suffix_search_list":
if value:
adapter["connection_specific_dns_suffix_search_list"].append(value)
# Process additional connection specific dns suffix search list entries
_parse_additional_entries(adapter["connection_specific_dns_suffix_search_list"], line_iter)
elif key == "default_gateway":
if value:
adapter["default_gateways"].append(value)
# Process additional gateways
_parse_additional_entries(adapter["default_gateways"], line_iter)
elif key == "dns_servers":
if value:
adapter["dns_servers"].append(value)
# Process additional DNS servers
_parse_additional_entries(adapter["dns_servers"], line_iter)
elif key == "subnet_mask":
# Subnet Mask should be associated with the last IPv4 address
if adapter["ipv4_addresses"]:
adapter["ipv4_addresses"][-1]["subnet_mask"] = value
else:
# Store unrecognized fields in extras
adapter["extras"].append({key: value})
def _parse_ipv6_address(value):
# Handle multiple status indicators
match = re.match(r"([^\(]+)\((.*)\)", value) if value else None
if match:
address = match.group(1).strip()
status = match.group(2).strip('()')
else:
address = value
status = None
return {
"address": address,
"status": status
}
def _parse_ipv4_address(value, key, line_iter):
# Handle autoconfigured status
match = re.match(r"([^\(]+)\((.*)\)", value) if value else None
if match:
address = match.group(1).strip()
status = match.group(2).strip('()')
autoconfigured = 'autoconfigured' if 'autoconfiguration' in key or 'autoconfigured' in status else None
else:
address = value
status = None
autoconfigured = 'autoconfigured' if 'autoconfiguration' in key else None
# Get subnet mask
subnet_mask = None
# Peek ahead for "Subnet Mask" line
try:
next_line = next(line_iter)
next_key, next_value = _parse_line(next_line)
if next_key == "subnet_mask":
subnet_mask = next_value
else:
# If it's not "Subnet Mask", put it back into the iterator
line_iter.pushback(next_line)
except StopIteration:
pass
return {
"address": address,
"subnet_mask": subnet_mask,
"autoconfigured": autoconfigured,
"status": status
}
def _parse_additional_entries(entry_list, line_iter):
# Process additional lines that belong to the current entry (e.g., additional DNS servers, DNS Suffix Search List)
while True:
try:
next_line = next(line_iter)
if not next_line.strip():
continue # Skip empty lines
# Check if the line is indented (starts with whitespace)
if re.match(r"^\s\s\s\s", next_line):
# It's an indented line; append the stripped line to entry_list
entry_list.append(next_line.strip())
else:
# Not an indented line; push it back and exit
line_iter.pushback(next_line)
break
except StopIteration:
break

View File

@@ -122,7 +122,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '0.7'
version = '0.75'
description = '`iw dev [device] scan` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -298,6 +298,7 @@ def parse(data, raw=False, quiet=False):
raw_output = []
section = {}
header = ''
if jc.utils.has_data(data):
@@ -323,7 +324,9 @@ def parse(data, raw=False, quiet=False):
split_line[0] = split_line[0].lower().replace('*', '').replace('(', '')\
.replace(')', '').replace(',', '').replace('-', '_')\
.strip().replace(' ', '_')
section[split_line[0]] = split_line[1].strip()
if split_line[1] == '':
header = split_line[0] + '_'
section[header + split_line[0]] = split_line[1].strip()
continue

View File

@@ -48,7 +48,6 @@ Schema:
"sdevice_id_int": integer,
"rev": string,
"physlot": string,
"physlot_int": integer,
"progif": string,
"progif_int": integer
}
@@ -84,7 +83,6 @@ Examples:
"sdevice_id": "07e0",
"sdevice_id_int": 2016,
"physlot": "37",
"physlot_int": 55,
"progif": "01",
"progif_int": 1
},
@@ -123,7 +121,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
version = '1.1'
description = '`lspci -mmv` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -149,7 +147,7 @@ def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]:
"""
int_list: set[str] = {
'domain', 'bus', 'dev', 'function', 'class_id', 'vendor_id', 'device_id',
'svendor_id', 'sdevice_id', 'physlot', 'progif'
'svendor_id', 'sdevice_id', 'progif'
}
new_list: List[JSONDictType] = []

View File

@@ -77,7 +77,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.9'
version = '1.10'
description = '`mount` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -138,16 +138,19 @@ def _linux_parse(data):
pattern = re.compile(
r'''
(?P<filesystem>\S+)\s+
on\s+
(?P<mount_point>.*?)\s+
type\s+
(?P<type>\S+)\s+
\((?P<options>.*?)\)\s*''',
re.VERBOSE)
(?P<filesystem>.*)
\son\s
(?P<mount_point>.*?)
\stype\s
(?P<type>\S+)
\s+
\((?P<options>.*?)\)
\s*
''', re.VERBOSE
)
match = pattern.match(entry)
groups = match.groupdict()
mymatch = pattern.match(entry)
groups = mymatch.groupdict()
if groups:
output_line['filesystem'] = groups["filesystem"]
@@ -223,7 +226,4 @@ def parse(data, raw=False, quiet=False):
else:
raw_output = _linux_parse(cleandata)
if raw:
return raw_output
else:
return _process(raw_output)
return raw_output if raw else _process(raw_output)

View File

@@ -355,7 +355,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.15'
version = '1.16'
description = '`netstat` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'

View File

@@ -1,5 +1,6 @@
r"""jc - JSON Convert Linux netstat Parser"""
import string
import re
def normalize_headers(header):
@@ -38,7 +39,7 @@ def parse_network(headers, entry):
]
# split entry based on presence of value in "State" column
contains_state = any(state in entry for state in LIST_OF_STATES)
contains_state = any(re.search(rf"\b{re.escape(state)}\b", entry) for state in LIST_OF_STATES)
split_modifier = 1 if contains_state else 2
entry = entry.split(maxsplit=len(headers) - split_modifier)

View File

@@ -17,20 +17,24 @@ Schema:
[
{
"version": string,
"verbosity": integer,
"ratelimit": integer
"version": string,
"verbosity": integer,
"ratelimit": integer
}
]
[
{
"zone": string
"zone": string
"status": {
"state": string,
"served-serial": string,
"commit-serial": string,
"wait": string
"state": string,
"pattern": string, # Additional
"catalog-member-id": string, # Additional
"served-serial": string,
"commit-serial": string,
"notified-serial": string, # Conditional
"wait": string,
"transfer": string # Conditional
}
}
]
@@ -65,7 +69,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
version = '1.2'
description = '`nsd-control` command parser'
author = 'Pettai'
author_email = 'pettai@sunet.se'
@@ -89,7 +93,7 @@ def _process(proc_data):
List of Dictionaries. Structured to conform to the schema.
"""
int_list = {'verbosity', 'ratelimit', 'wait'}
int_list = {'verbosity', 'ratelimit', 'wait', 'transfer'}
for entry in proc_data:
for key in entry:
@@ -229,6 +233,20 @@ def parse(data: str, raw: bool = False, quiet: bool = False):
raw_output.append(zonename)
continue
if line.startswith('notified-serial:'):
linedata = line.split(': ', maxsplit=1)
notified = linedata[1].strip('"').rstrip('"')
zstatus.update({'notified-serial': notified})
continue
if line.startswith('transfer:'):
linedata = line.split(': ', maxsplit=1)
transfer = linedata[1].strip('"').rstrip('"')
zstatus.update({'transfer': transfer})
zonename.update({'status': zstatus})
raw_output.append(zonename)
continue
# stats
if line.startswith('server') or line.startswith('num.') or line.startswith('size.') or line.startswith('time.') or line.startswith('zone.'):
itrparse = True

348
jc/parsers/pacman.py Normal file
View File

@@ -0,0 +1,348 @@
r"""jc - JSON Convert `pacman` command output parser
Supports the following `pacman` arguments:
- `-Si`
- `-Sii`
- `-Qi`
- `-Qii`
The `*_epoch` calculated timestamp fields are naive. (i.e. based on the
local time of the system the parser is run on)
Usage (cli):
$ pacman -Si <package> | jc --pacman
or
$ jc pacman -Si <package>
Usage (module):
import jc
result = jc.parse('pacman', pacman_command_output)
Schema:
[
{
"repository": string,
"name": string,
"version": string,
"description": string,
"architecture": string,
"url": string,
"licenses": [
string
],
"groups": [
string
],
"provides": [
string
],
"depends_on": [
string
],
"optional_deps": [
{
"name": string,
"description": string
}
],
"optional_for": [
string
],
"conflicts_with": [
string
],
"replaces": [
string
],
"download_size": string,
"download_size_bytes": integer [0]
"installed_size": string,
"installed_size_bytes": integer, [0]
"packager": string,
"build_date": string,
"build_date_epoch": integer, [0]
"install_date": string,
"install_date_epoch": integer, [0]
"validated_by": [
string
],
"backup_files": [
string
]
}
]
[0] Field exists if conversion successful
Examples:
$ pacman -qii zstd | jc --pacman -p
[
{
"name": "zstd",
"version": "1.5.6-1",
"description": "Zstandard - Fast real-time compression algorithm",
"architecture": "x86_64",
"url": "https://facebook.github.io/zstd/",
"licenses": [
"BSD-3-Clause",
"GPL-2.0-only"
],
"groups": [],
"provides": [
"libzstd.so=1-64"
],
"depends_on": [
"glibc",
"gcc-libs",
"zlib",
"xz",
"lz4"
],
"required_by": [
"android-tools",
"appstream",
...
"tiled",
"vulkan-radeon",
"wireshark-cli"
],
"optional_for": [
"xarchiver"
],
"conflicts_with": [],
"replaces": [],
"installed_size": "1527.00 KiB",
"installed_size_bytes": 1563648,
"packager": "Levente Polyak <anthraxx@archlinux.org>",
"build_date": "Sat 11 May 2024 06:14:19 AM +08",
"build_date_epoch": 1715433259,
"install_date": "Fri 24 May 2024 09:50:31 AM +08",
"install_date_epoch": 1715663342,
"install_reason": "Installed as a dependency for another package",
"install_script": "No",
"validated_by": [
"Signature"
],
"extended_data": "pkgtype=pkg"
}
]
$ pacman -qii zstd | jc --pacman -p -r
[
{
"name": "zstd",
"version": "1.5.6-1",
"description": "Zstandard - Fast real-time compression algorithm",
"architecture": "x86_64",
"url": "https://facebook.github.io/zstd/",
"licenses": "BSD-3-Clause GPL-2.0-only",
"groups": null,
"provides": "libzstd.so=1-64",
"depends_on": "glibc gcc-libs zlib xz lz4",
"required_by": [
"android-tools appstream avr-gcc binutils blender blosc",
"boost-libs btrfs-progs cloudflare-warp-bin comgr curl",
"dolphin-emu file flatpak gcc gdal gnutls karchive",
"karchive5 kmod lib32-zstd libarchive libelf libtiff",
"libva-mesa-driver libxmlb libzip lld llvm-libs mariadb-libs",
"mesa mesa-vdpau minizip-ng mkinitcpio mold netcdf",
"opencl-clover-mesa opencl-rusticl-mesa openucx postgresql",
"postgresql-libs ppsspp qemu-img qemu-system-riscv",
"qemu-system-x86 qgis qt6-base qt6-tools rsync rustup",
"squashfs-tools squashfuse systemd-libs tiled vulkan-radeon",
"wireshark-cli"
],
"optional_for": "xarchiver",
"conflicts_with": null,
"replaces": null,
"installed_size": "1527.00 KiB",
"packager": "Levente Polyak <anthraxx@archlinux.org>",
"build_date": "Sat 11 May 2024 06:14:19 AM +08",
"install_date": "Fri 24 May 2024 09:50:31 AM +08",
"install_reason": "Installed as a dependency for another package",
"install_script": "No",
"validated_by": "Signature",
"extended_data": "pkgtype=pkg"
}
]
"""
from typing import List, Dict
from jc.jc_types import JSONDictType
import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
description = '`pacman` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
tags = ['command', 'file']
magic_commands = ['pacman']
__version__ = info.version
def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List of Dictionaries) raw structured data to process
Returns:
List of Dictionaries. Structured to conform to the schema.
"""
split_fields = {
'licenses', 'groups', 'provides', 'depends_on', 'conflicts_with',
'replaces', 'optional_for'
}
space_split_fields = {
'required_by', 'groups', 'provides', 'depends_on',
'conflicts_with', 'replaces', 'validated_by'
}
two_space_fields = {'licenses', 'validated_by'}
name_description_fields = {'optional_deps'}
size_fields = {'download_size', 'installed_size'}
date_fields = {'build_date', 'install_date'}
# initial split for field lists
for item in proc_data:
for key, val in item.copy().items():
if key in split_fields:
if val is None:
item[key] = []
else:
item[key] = val.split()
# fixup for specific lists
if key in space_split_fields and isinstance(val, List):
val_list = [x.split() for x in val]
item[key] = [x for xs in val_list for x in xs] # flatten the list
if key in two_space_fields and isinstance(val, str):
item[key] = val.split(' ')
if key in name_description_fields and isinstance(val, list):
new_list = []
for name_desc in val:
n, *d = name_desc.split(': ')
if d == []:
d = ''
else:
d = d[0]
new_obj = {'name': n, 'description': d}
new_list.append(new_obj)
item[key] = new_list
if key in size_fields:
bts = jc.utils.convert_size_to_int(val)
if bts:
item[key + '_bytes'] = bts
if key in date_fields:
# need to append '00' to date for conversion
ts = jc.utils.timestamp(val + '00', format_hint=(3100,))
if ts.naive:
item[key + '_epoch'] = ts.naive
else:
# try taking off the text TZ identifier
ts = jc.utils.timestamp(val[:-4], format_hint=(3000,))
if ts.naive:
item[key + '_epoch'] = ts.naive
return proc_data
def parse(
data: str,
raw: bool = False,
quiet: bool = False
) -> List[JSONDictType]:
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) unprocessed output if True
quiet: (boolean) suppress warning messages if True
Returns:
List of Dictionaries. Raw or processed structured data.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: List[Dict] = []
entry_obj: Dict = {}
multiline_fields = {'required_by', 'optional_deps', 'backup_files'}
multiline_list: List = []
multiline_key = ''
if jc.utils.has_data(data):
for line in filter(None, data.splitlines()):
splitline = line.split(' : ', maxsplit=1)
if len(splitline) == 2:
# this is a key/value pair
key, val = splitline
key = key.strip()
key = jc.utils.normalize_key(key)
val = val.strip()
# new entries can start with "Repository" or "Name"
if (key == 'name' or key == 'repository') and len(entry_obj) > 2:
if multiline_list:
entry_obj[multiline_key] = multiline_list
multiline_list = []
multiline_key = ''
if entry_obj:
raw_output.append(entry_obj)
entry_obj = {}
entry_obj[key] = val
continue
if key in multiline_fields:
if multiline_list:
entry_obj[multiline_key] = multiline_list
multiline_list = []
if val != 'None':
multiline_list.append(val)
multiline_key = key
continue
if key not in multiline_fields:
if multiline_list:
entry_obj[multiline_key] = multiline_list
multiline_list = []
multiline_key = ''
entry_obj[key] = val if val != 'None' else None
continue
# multiline field continuation lines
multiline_list.append(line.strip())
continue
# grab the last entry
if entry_obj:
if multiline_list:
entry_obj[multiline_key] = multiline_list
multiline_list = []
multiline_key = ''
raw_output.append(entry_obj)
return raw_output if raw else _process(raw_output)

View File

@@ -76,7 +76,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.0'
version = '1.1'
description = '`pci.ids` file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -182,7 +182,8 @@ def parse(
if vdc_subvendor:
subvendor = '_' + vdc_subvendor.groupdict()['subvendor']
subdevice = '_' + vdc_subvendor.groupdict()['subdevice']
vdc_obj[vendor_id][device_id][subvendor] = {}
if not vdc_obj[vendor_id][device_id].get(subvendor) or not isinstance(vdc_obj[vendor_id][device_id][subvendor], dict):
vdc_obj[vendor_id][device_id][subvendor] = {}
vdc_obj[vendor_id][device_id][subvendor][subdevice] = {}
vdc_obj[vendor_id][device_id][subvendor][subdevice]['subsystem_name'] = vdc_subvendor.groupdict()['subsystem_name']
continue

View File

@@ -88,7 +88,7 @@ from jc.exceptions import ParseError
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.5'
version = '1.6'
description = '`ping` and `ping6` command streaming parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -169,7 +169,7 @@ def _ipv6_in(line):
return ipv6
def _error_type(line):
def _error_type_v4(line):
# from https://github.com/dgibson/iputils/blob/master/ping.c
# https://android.googlesource.com/platform/external/ping/+/8fc3c91cf9e7f87bc20b9e6d3ea2982d87b70d9a/ping.c
# https://opensource.apple.com/source/network_cmds/network_cmds-328/ping.tproj/ping.c
@@ -207,6 +207,37 @@ def _error_type(line):
return None
def _error_type_v6(line):
type_map = {
'Destination unreachable': 'destination_unreachable',
'Packet too big': 'packet_too_big',
'Time exceeded:': 'time_exceeded',
'Parameter problem:': 'parameter_problem',
}
code_map = {
'destination_unreachable': {
'No route': 'no_route',
'Administratively prohibited': 'administratively_prohibited',
"Beyond scope of source address": 'beyond_scope_of_source_address',
'Address unreachable': 'address_unreachable',
'Port unreachable': 'port_unreachable',
},
'time_exceeded': {
'Hop limit': 'hop_limit',
'Fragment reassembly time exceeded': 'fragment_reassembly_time_exceeded',
},
}
return_code = None
for err_type, code in type_map.items():
if err_type in line:
return_code = code
for err_code, code_name in code_map[code].items():
if err_code in line:
return_code += '_' + code_name
return return_code
def _bsd_parse(line, s):
output_line = {}
@@ -263,6 +294,24 @@ def _bsd_parse(line, s):
# ping response lines
err = None
if s.ipv4:
err = _error_type_v4(line)
else:
err = _error_type_v6(line)
if err:
output_line = {
'type': err
}
try:
output_line['sent_bytes'] = line.split()[0]
output_line['destination_ip'] = s.destination_ip
output_line['response_ip'] = line.split()[4].strip(':').strip('(').strip(')')
except Exception:
pass
return output_line
# ipv4 lines
if not _ipv6_in(line):
@@ -279,7 +328,7 @@ def _bsd_parse(line, s):
return output_line
# catch error responses
err = _error_type(line)
err = _error_type_v4(line)
if err:
output_line = {
'type': err
@@ -444,25 +493,40 @@ def _linux_parse(line, s):
}
return output_line
# if timestamp option is specified, then shift icmp sequence field right by one
timestamp = False
if line[0] == '[':
timestamp = True
timestamp_offset = 1 if timestamp else 0
# ping response lines
err = None
if s.ipv4:
err = _error_type_v4(line)
else:
err = _error_type_v6(line)
if err:
output_line = {
'type': err,
'destination_ip': s.destination_ip or None,
'sent_bytes': s.sent_bytes or None,
'response_ip': line.split()[timestamp_offset + 1] if type != 'timeout' else None,
'icmp_seq': line.replace('=', ' ').split()[timestamp_offset + 3],
'timestamp': line.split()[0].lstrip('[').rstrip(']') if timestamp else None,
}
return output_line
# request timeout
if 'no answer yet for icmp_seq=' in line:
timestamp = False
isequence = 5
# if timestamp option is specified, then shift icmp sequence field right by one
if line[0] == '[':
timestamp = True
isequence = 6
output_line = {
'type': 'timeout',
'destination_ip': s.destination_ip or None,
'sent_bytes': s.sent_bytes or None,
'pattern': s.pattern or None,
'timestamp': line.split()[0].lstrip('[').rstrip(']') if timestamp else None,
'icmp_seq': line.replace('=', ' ').split()[isequence]
'icmp_seq': line.replace('=', ' ').split()[timestamp_offset + 5]
}
return output_line
@@ -473,20 +537,16 @@ def _linux_parse(line, s):
line = line.replace('(', ' ').replace(')', ' ').replace('=', ' ')
# positions of items depend on whether ipv4/ipv6 and/or ip/hostname is used
param_positions = None
if s.ipv4 and not s.hostname:
bts, rip, iseq, t2l, tms = (0, 3, 5, 7, 9)
param_positions = (0, 3, 5, 7, 9)
elif s.ipv4 and s.hostname:
bts, rip, iseq, t2l, tms = (0, 4, 7, 9, 11)
param_positions = (0, 4, 7, 9, 11)
elif not s.ipv4 and not s.hostname:
bts, rip, iseq, t2l, tms = (0, 3, 5, 7, 9)
param_positions = (0, 3, 5, 7, 9)
elif not s.ipv4 and s.hostname:
bts, rip, iseq, t2l, tms = (0, 4, 7, 9, 11)
# if timestamp option is specified, then shift everything right by one
timestamp = False
if line[0] == '[':
timestamp = True
bts, rip, iseq, t2l, tms = (bts + 1, rip + 1, iseq + 1, t2l + 1, tms + 1)
param_positions = (0, 4, 7, 9, 11)
bts, rip, iseq, t2l, tms = (x + timestamp_offset for x in param_positions)
output_line = {
'type': 'reply',

View File

@@ -66,7 +66,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.4'
version = '1.5'
description = '`pip show` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -144,6 +144,8 @@ def parse(
item_value = None
if last_key_data and last_key != item_key:
if not isinstance(package[last_key], str):
package[last_key] = ''
package[last_key] = package[last_key] + '\n' + '\n'.join(last_key_data)
last_key_data = []

View File

@@ -26,7 +26,7 @@ or
or
$ cat /proc/meminfo | jc --proc-memifno
$ cat /proc/meminfo | jc --proc-meminfo
Usage (module):

View File

@@ -185,7 +185,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.8'
version = '1.9'
description = '`rpm -qi` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -256,8 +256,6 @@ def parse(data, raw=False, quiet=False):
raw_output = []
entry_obj = {}
last_entry = None
this_entry = None
desc_entry = False
desc_en_entry = False
description = []
@@ -268,17 +266,13 @@ def parse(data, raw=False, quiet=False):
split_line = line.split(': ', maxsplit=1)
if (split_line[0].startswith('Name') or split_line[0] == 'Package') and len(split_line) == 2:
this_entry = split_line[1].strip()
if this_entry != last_entry:
if entry_obj:
if description:
entry_obj['description'] = ' '.join(description)
raw_output.append(entry_obj)
entry_obj = {}
last_entry = this_entry
desc_entry = False
desc_en_entry = False
if entry_obj:
if description:
entry_obj['description'] = ' '.join(description)
raw_output.append(entry_obj)
entry_obj = {}
desc_entry = False
desc_en_entry = False
if line.startswith('Description :'):
desc_entry = True

View File

@@ -42,6 +42,13 @@ field names
"file_descriptor": string
}
}
"inode_number": string,
"cookie": string,
"cgroup": string,
"v6only": string,
"timer_name": string,
"expire_time": string,
"retrans": string
}
}
]
@@ -288,7 +295,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.7'
version = '1.8'
description = '`ss` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -344,13 +351,16 @@ def _parse_opts(proc_data):
"""
o_field = proc_data.split(' ')
opts = {}
for item in o_field:
# -e option:
item = re.sub(
'uid', 'uid_number',
re.sub('sk', 'cookie', re.sub('ino', 'inode_number', item)))
if ":" in item:
key, val = item.split(':')
# -o option
if key == "timer":
val = val.replace('(', '[').replace(')', ']')
@@ -361,6 +371,7 @@ def _parse_opts(proc_data):
'retrans': val[2]
}
opts[key] = val
# -p option
if key == "users":
key = 'process_id'
@@ -380,7 +391,9 @@ def _parse_opts(proc_data):
}
})
val = data
opts[key] = val
return opts
def parse(data, raw=False, quiet=False):
@@ -402,16 +415,24 @@ def parse(data, raw=False, quiet=False):
contains_colon = ['nl', 'p_raw', 'raw', 'udp', 'tcp', 'v_str', 'icmp6']
raw_output = []
ONE_OR_MORE_SPACE_PATTERN = r'[ ]{1,}'
TWO_OR_MORE_SPACES_PATTERN = r'[ ]{2,}'
# Clear any blank lines
cleandata = list(filter(None, data.splitlines()))
if jc.utils.has_data(data):
header_text = cleandata[0].lower()
# get the position of Recv-Q since sometimes it doesn't leave enough space
# to parse. need at least two spaces between main fields to differentiate
# from opt fields, which are only separated by one space
recv_q_position = header_text.find('recv-q')
header_text = header_text.replace('netidstate', 'netid state')
header_text = header_text.replace('local address:port', 'local_address local_port')
header_text = header_text.replace('peer address:port', 'peer_address peer_port')
header_text = header_text.replace('portprocess', 'port') # don't support process info today
header_text = header_text.replace('-', '_')
header_list = header_text.split()
@@ -420,14 +441,16 @@ def parse(data, raw=False, quiet=False):
for entry in cleandata[1:]:
output_line = {}
if entry[0] not in string.whitespace:
# fix issue where recv-q can be too close to state
entry = entry[:recv_q_position] + ' ' + entry[recv_q_position:]
# fix weird ss bug where first two columns have no space between them sometimes
entry = entry[:5] + ' ' + entry[5:]
entry_list = re.split(r'[ ]{1,}',entry.strip())
entry_list = re.split(ONE_OR_MORE_SPACE_PATTERN, entry.strip())
if len(entry_list) > len(header_list) or extra_opts == True:
entry_list = re.split(r'[ ]{2,}',entry.strip())
entry_list = re.split(TWO_OR_MORE_SPACES_PATTERN, entry.strip())
extra_opts = True
if entry_list[0] in contains_colon and ':' in entry_list[4]:
@@ -444,8 +467,9 @@ def parse(data, raw=False, quiet=False):
entry_list[6] = p_address
entry_list.insert(7, p_port)
if re.search(r'ino:|uid:|sk:|users:|timer:',entry_list[-1]):
header_list.append('opts')
if re.search(r'ino:|uid:|sk:|users:|timer:|cgroup:|v6only:', entry_list[-1]):
if header_list[-1] != 'opts':
header_list.append('opts')
entry_list[-1] = _parse_opts(entry_list[-1])
output_line = dict(zip(header_list, entry_list))
@@ -476,7 +500,4 @@ def parse(data, raw=False, quiet=False):
raw_output.append(output_line)
if raw:
return raw_output
else:
return _process(raw_output)
return raw_output if raw else _process(raw_output)

View File

@@ -316,7 +316,7 @@ from jc.parsers.universal import sparse_table_parse as parse_table
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
version = '1.2'
description = '`top -b` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -565,7 +565,7 @@ def parse(
item_obj = {}
uptime_str = line[6:]
item_obj.update(parse_uptime(uptime_str, raw=True))
item_obj.update(parse_uptime(uptime_str, raw=True, quiet=True))
continue
if line.startswith('Tasks:'):

View File

@@ -153,7 +153,7 @@ from jc.parsers.universal import sparse_table_parse as parse_table
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
version = '1.2'
description = '`top -b` command streaming parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -409,7 +409,7 @@ def parse(
idx += 1
uptime_str = line[6:]
output_line.update(parse_uptime(uptime_str, raw=True))
output_line.update(parse_uptime(uptime_str, raw=True, quiet=True))
continue
if line.startswith('Tasks:'):

View File

@@ -118,11 +118,12 @@ Examples:
import re
from decimal import Decimal
import jc.utils
from copy import deepcopy
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.6'
version = '1.8'
description = '`traceroute` and `traceroute6` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -166,7 +167,7 @@ SOFTWARE.
RE_HEADER = re.compile(r'(\S+)\s+\((\d+\.\d+\.\d+\.\d+|[0-9a-fA-F:]+)\)')
RE_PROBE_NAME_IP = re.compile(r'(\S+)\s+\((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}|[0-9a-fA-F:]+)\)+')
RE_PROBE_IP_ONLY = re.compile(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+([^\(])')
RE_PROBE_IPV6_ONLY = re.compile(r'(([a-f0-9:]+:+)+[a-f0-9]+)')
RE_PROBE_IPV6_ONLY = re.compile(r'(([a-f0-9]*:)+[a-f0-9]+)')
RE_PROBE_BSD_IPV6 = re.compile(r'\b(?:[A-Fa-f0-9]{1,4}:){7}[A-Fa-f0-9]{1,4}\b')
RE_HOP = re.compile(r'^\s*(\d+)?\s+(.+)$')
RE_PROBE_ASN = re.compile(r'\[AS(\d+)\]')
@@ -238,6 +239,58 @@ class _Probe(object):
return text
def _get_probes(hop_string: str):
probes = []
probe_asn_match = [ (match, "ASN") for match in RE_PROBE_ASN.finditer(hop_string)]
probe_name_ip_match = [(match, "NAME_IP") for match in RE_PROBE_NAME_IP.finditer(hop_string)]
probe_ip_only_match = [(match, "IP_ONLY") for match in RE_PROBE_IP_ONLY.finditer(hop_string)]
probe_bsd_ipv6_match = [(match, "IP_IPV6") for match in RE_PROBE_BSD_IPV6.finditer(hop_string)]
probe_ipv6_only_match = [(match, "IP_IPV6_ONLY") for match in RE_PROBE_IPV6_ONLY.finditer(hop_string)]
probe_rtt_annotations = [(match, "RTT") for match in RE_PROBE_RTT_ANNOTATION.finditer(hop_string)]
matches = sorted(probe_asn_match + probe_name_ip_match + probe_ip_only_match + probe_bsd_ipv6_match + probe_ipv6_only_match + probe_rtt_annotations, key=lambda x: x[0].start(0))
probe, is_last_match_rtt = _Probe(), False
for match, match_type in matches:
if match_type == "ASN":
probe.asn = int(match.group(1))
elif match_type == "NAME_IP":
probe.name = match.group(1)
probe.ip = match.group(2)
elif match_type == "IP_ONLY":
probe.ip = match.group(1)
elif match_type == "IP_IPV6":
probe.ip = match.group(0)
elif match_type == "IP_IPV6_ONLY":
probe.ip = match.group(1)
elif match_type == "RTT":
if match.groups()[0]:
probe_rtt = Decimal(match.groups()[0])
elif match.groups()[1]:
probe_rtt = None
else:
message = f"Expected probe RTT or *. Got: '{match.group(0)}'"
raise ParseError(message)
# If the last match is a RTT, then copy all probe values and replace RTT field
if is_last_match_rtt:
probe = deepcopy(last_probe) # type: ignore
# Set RTT values
probe.rtt = probe_rtt
probe.annotation = match.groups()[2] or None
# RTT is the last value shown for a hop
if any([probe.ip, probe.asn, probe.annotation, probe.rtt, probe.name]):
probes.append(probe)
last_probe = probe
probe = _Probe()
if match_type == "RTT":
is_last_match_rtt = True
else:
is_last_match_rtt = False
return probes
def _loads(data):
lines = data.splitlines()
@@ -270,56 +323,9 @@ def _loads(data):
hop_string = hop_match.group(2)
probe_asn_match = RE_PROBE_ASN.search(hop_string)
if probe_asn_match:
probe_asn = int(probe_asn_match.group(1))
else:
probe_asn = None
probe_name_ip_match = RE_PROBE_NAME_IP.search(hop_string)
probe_ip_only_match = RE_PROBE_IP_ONLY.search(hop_string)
probe_bsd_ipv6_match = RE_PROBE_BSD_IPV6.search(hop_string)
probe_ipv6_only_match = RE_PROBE_IPV6_ONLY.search(hop_string)
if probe_ip_only_match:
probe_name = None
probe_ip = probe_ip_only_match.group(1)
elif probe_name_ip_match:
probe_name = probe_name_ip_match.group(1)
probe_ip = probe_name_ip_match.group(2)
elif probe_bsd_ipv6_match:
probe_name = None
probe_ip = probe_bsd_ipv6_match.group(0)
elif probe_ipv6_only_match:
probe_name = None
probe_ip = probe_ipv6_only_match.group(1)
else:
probe_name = None
probe_ip = None
probe_rtt_annotations = RE_PROBE_RTT_ANNOTATION.findall(hop_string)
for probe_rtt_annotation in probe_rtt_annotations:
if probe_rtt_annotation[0]:
probe_rtt = Decimal(probe_rtt_annotation[0])
elif probe_rtt_annotation[1]:
probe_rtt = None
else:
message = f"Expected probe RTT or *. Got: '{probe_rtt_annotation[0]}'"
raise ParseError(message)
probe_annotation = probe_rtt_annotation[2] or None
probe = _Probe(
name=probe_name,
ip=probe_ip,
asn=probe_asn,
rtt=probe_rtt,
annotation=probe_annotation
)
# only add probe if there is data
if any([probe_name, probe_ip, probe_asn, probe_rtt, probe_annotation]):
hop.add_probe(probe)
probes = _get_probes(hop_string)
for probe in probes:
hop.add_probe(probe)
return traceroute
@@ -330,6 +336,7 @@ class ParseError(Exception):
########################################################################################
def _process(proc_data):
"""
Final processing to conform to the schema.
@@ -435,7 +442,4 @@ def parse(data, raw=False, quiet=False):
'hops': hops_list
}
if raw:
return raw_output
else:
return _process(raw_output)
return raw_output if raw else _process(raw_output)

View File

@@ -65,7 +65,7 @@ import jc.utils
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.9'
version = '1.10'
description = '`uptime` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -162,7 +162,7 @@ def parse(data, raw=False, quiet=False):
raw_output = {}
if jc.utils.has_data(data):
if 'users' in data:
if 'user' in data:
# standard uptime output
time, _, *uptime, users, _, _, _, load_1m, load_5m, load_15m = data.split()

273
jc/parsers/wg_show.py Normal file
View File

@@ -0,0 +1,273 @@
r"""jc - JSON Convert `wg show` command output parser
Parses the output of the `wg show all dump` command, providing structured JSON output for easy integration and analysis.
Usage (cli):
$ wg show all dump | jc --wg-show
or
$ jc wg show all dump
Usage (module):
import jc
result = jc.parse('wg-show', wg_command_output)
Schema:
[
{
"device": string,
"private_key": string,
"public_key": string,
"listen_port": integer,
"fwmark": integer,
"peers": [
{
"public_key": string,
"preshared_key": string,
"endpoint": string,
"latest_handshake": integer,
"transfer_rx": integer,
"transfer_sx": integer,
"persistent_keepalive": integer,
"allowed_ips": [
string
]
}
]
}
]
Examples:
$ wg show all dump | jc --wg-show -p
[
{
"device": "wg0",
"private_key": "aEbVdvHSEp3oofHDNVCsUoaRSxk1Og8/pTLof5yF+1M=",
"public_key": "OIxbQszw1chdO5uigAxpsl4fc/h04yMYafl72gUbakM=",
"listen_port": 51820,
"fwmark": null,
"peers": [
{
"public_key": "sQFGAhSdx0aC7DmTFojzBOW8Ccjv1XV5+N9FnkZu5zc=",
"preshared_key": null,
"endpoint": "79.134.136.199:40036",
"latest_handshake": 1728809756,
"transfer_rx": 1378724,
"transfer_sx": 406524,
"persistent_keepalive": null,
"allowed_ips": ["10.10.0.2/32"]
},
{
"public_key": "B9csmpvrv4Q7gpjc6zAbNNO8hIOYfpBqxmik2aNpwwE=",
"preshared_key": null,
"endpoint": "79.134.136.199:35946",
"latest_handshake": 1728809756,
"transfer_rx": 4884248,
"transfer_sx": 3544596,
"persistent_keepalive": null,
"allowed_ips": ["10.10.0.3/32"]
},
{
"public_key": "miiSYR5UdevREhlWpmnci+vv/dEGLHbNtKu7u1CuOD4=",
"preshared_key": null,
"allowed_ips": ["10.10.0.4/32"]
},
{
"public_key": "gx9+JHLHJvOfBNjTmZ8KQAnThFFiZMQrX1kRaYcIYzw=",
"preshared_key": null,
"endpoint": "173.244.225.194:45014",
"latest_handshake": 1728809827,
"transfer_rx": 1363652,
"transfer_sx": 458252,
"persistent_keepalive": null,
"allowed_ips": ["10.10.0.5/32"]
}
]
}
]
$ wg show all dump | jc --wg-show -p -r
[
{
"device": "wg0",
"private_key": "aEbVdvHSEp3oofHDNVCsUoaRSxk1Og8/pTLof5yF+1M=",
"public_key": "OIxbQszw1chdO5uigAxpsl4fc/h04yMYafl72gUbakM=",
"listen_port": 51820,
"fwmark": null,
"peers": {
"sQFGAhSdx0aC7DmTFojzBOW8Ccjv1XV5+N9FnkZu5zc=": {
"preshared_key": null,
"endpoint": "79.134.136.199:40036",
"latest_handshake": 1728809756,
"transfer_rx": 1378724,
"transfer_sx": 406524,
"persistent_keepalive": -1,
"allowed_ips": ["10.10.0.2/32"]
},
"B9csmpvrv4Q7gpjc6zAbNNO8hIOYfpBqxmik2aNpwwE=": {
"preshared_key": null,
"endpoint": "79.134.136.199:35946",
"latest_handshake": 1728809756,
"transfer_rx": 4884248,
"transfer_sx": 3544596,
"persistent_keepalive": -1,
"allowed_ips": ["10.10.0.3/32"]
},
"miiSYR5UdevREhlWpmnci+vv/dEGLHbNtKu7u1CuOD4=": {
"preshared_key": null,
"allowed_ips": ["10.10.0.4/32"]
},
"gx9+JHLHJvOfBNjTmZ8KQAnThFFiZMQrX1kRaYcIYzw=": {
"preshared_key": null,
"endpoint": "173.244.225.194:45014",
"latest_handshake": 1728809827,
"transfer_rx": 1363652,
"transfer_sx": 458252,
"persistent_keepalive": -1,
"allowed_ips": ["10.10.0.5/32"]
}
}
}
]
"""
from typing import List, Dict, Optional, Union
from jc.jc_types import JSONDictType
import jc.utils
import re
PeerData = Dict[str, Union[Optional[str], Optional[int], List[str]]]
DeviceData = Dict[str, Union[Optional[str], Optional[int], Dict[str, PeerData]]]
class info:
"""Provides parser metadata (version, author, etc.)"""
version = "1.0"
description = "`wg show` command parser"
author = "Hamza Saht"
author_email = "hamzasaht01@gmail.com"
compatible = ["linux", "darwin", "cygwin", "win32", "aix", "freebsd"]
tags = ["command"]
magic_commands = ["wg show"]
__version__ = info.version
def _process(proc_data: List[DeviceData]) -> List[JSONDictType]:
"""
Final processing to conform to the schema.
Parameters:
proc_data: (List[Dict]) Raw structured data to process
Returns:
List[Dict]: Structured data that conforms to the schema
"""
processed_data: List[JSONDictType] = []
for device in proc_data:
processed_device = {
"device": device["device"],
"private_key": device.get("private_key"),
"public_key": device.get("public_key"),
"listen_port": device.get("listen_port"),
"fwmark": device.get("fwmark"),
"peers": [
{
"public_key": peer_key,
"preshared_key": peer_data.get("preshared_key"),
"endpoint": peer_data.get("endpoint"),
"latest_handshake": peer_data.get("latest_handshake", 0),
"transfer_rx": peer_data.get("transfer_rx", 0),
"transfer_sx": peer_data.get("transfer_sx", 0),
"persistent_keepalive": peer_data.get("persistent_keepalive", -1),
"allowed_ips": peer_data.get("allowed_ips", []),
}
for peer_key, peer_data in device.get("peers", {}).items()
],
}
processed_data.append(processed_device)
return processed_data
def parse(data: str, raw: bool = False, quiet: bool = False) -> List[JSONDictType]:
"""
Main text parsing function.
Parses the output of the `wg` command, specifically `wg show all dump`, into structured JSON format.
Parameters:
data: (str) Text data to parse, typically the output from `wg show all dump`
raw: (bool) If True, returns unprocessed output
quiet: (bool) Suppress warning messages if True
Returns:
List[Dict]: Parsed data in JSON-friendly format, either raw or processed.
"""
jc.utils.compatibility(__name__, info.compatible, quiet)
jc.utils.input_type_check(data)
raw_output: List[DeviceData] = []
current_device: Optional[str] = None
device_data: DeviceData = {}
if jc.utils.has_data(data):
for line in filter(None, data.splitlines()):
fields = re.split(r"\s+", line.strip())
if len(fields) == 5:
device, private_key, public_key, listen_port, fwmark = fields
if current_device:
raw_output.append({"device": current_device, **device_data})
current_device = device
device_data = {
"private_key": private_key if private_key != "(none)" else None,
"public_key": public_key if public_key != "(none)" else None,
"listen_port": int(listen_port) if listen_port != "0" else None,
"fwmark": int(fwmark) if fwmark != "off" else None,
"peers": {},
}
elif len(fields) == 9:
(
interface,
public_key,
preshared_key,
endpoint,
allowed_ips,
latest_handshake,
transfer_rx,
transfer_tx,
persistent_keepalive,
) = fields
peer_data: PeerData = {
"preshared_key": preshared_key
if preshared_key != "(none)"
else None,
"endpoint": endpoint if endpoint != "(none)" else None,
"latest_handshake": int(latest_handshake),
"transfer_rx": int(transfer_rx),
"transfer_sx": int(transfer_tx),
"persistent_keepalive": int(persistent_keepalive)
if persistent_keepalive != "off"
else -1,
"allowed_ips": allowed_ips.split(",")
if allowed_ips != "(none)"
else [],
}
device_data["peers"][public_key] = {
k: v for k, v in peer_data.items() if v is not None
}
if current_device:
raw_output.append({"device": current_device, **device_data})
return raw_output if raw else _process(raw_output)

View File

@@ -87,7 +87,7 @@ from jc.exceptions import LibraryNotInstalled
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.7'
version = '1.8'
description = 'YAML file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -111,7 +111,6 @@ def _process(proc_data):
List of Dictionaries. Each dictionary represents a YAML document.
"""
# No further processing
return proc_data
@@ -148,17 +147,20 @@ def parse(data, raw=False, quiet=False):
# plugin code is incompatible with the pyoxidizer packager
YAML.official_plug_ins = lambda a: []
yaml = YAML(typ='safe')
# use the default `typ` to correctly load values that start with a literal "="
yaml = YAML(typ=None)
# modify the timestamp constructor to output datetime objects as
# strings since JSON does not support datetime objects
yaml.constructor.yaml_constructors['tag:yaml.org,2002:timestamp'] = \
yaml.constructor.yaml_constructors['tag:yaml.org,2002:str']
# modify the value constructor to output values starting with a
# literal "=" as a string.
yaml.constructor.yaml_constructors['tag:yaml.org,2002:value'] = \
yaml.constructor.yaml_constructors['tag:yaml.org,2002:str']
for document in yaml.load_all(data):
raw_output.append(document)
if raw:
return raw_output
else:
return _process(raw_output)
return raw_output if raw else _process(raw_output)

View File

@@ -29,11 +29,11 @@ Schema:
"config": [
{
"name": string,
"state": string,
"read": integer,
"write": integer,
"checksum": integer,
"errors": string,
"state": string/null,
"read": integer/null,
"write": integer/null,
"checksum": integer/null,
"errors": string/null,
}
],
"errors": string
@@ -138,7 +138,7 @@ from jc.parsers.kv import parse as kv_parse
class info():
"""Provides parser metadata (version, author, etc.)"""
version = '1.1'
version = '1.2'
description = '`zpool status` command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -182,11 +182,11 @@ def _build_config_list(string: str) -> List[Dict]:
line_list = line.strip().split(maxsplit=5)
config_obj: Dict = {}
config_obj['name'] = line_list[0]
config_obj['state'] = line_list[1]
config_obj['read'] = line_list[2]
config_obj['write'] = line_list[3]
config_obj['checksum'] = line_list[4]
config_obj['name'] = line_list[0] if len(line_list) > 0 else None
config_obj['state'] = line_list[1] if len(line_list) > 1 else None
config_obj['read'] = line_list[2] if len(line_list) > 2 else None
config_obj['write'] = line_list[3] if len(line_list) > 3 else None
config_obj['checksum'] = line_list[4] if len(line_list) > 4 else None
if len(line_list) == 6:
config_obj['errors'] = line_list[5]
config_list.append(config_obj)

View File

@@ -365,7 +365,11 @@ def convert_to_bool(value: object) -> bool:
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
def convert_size_to_int(size: str, binary: bool = False) -> Optional[int]:
def convert_size_to_int(
size: str,
binary: bool = False,
posix_mode: bool = False,
decimal_bias: bool = False) -> Optional[int]:
"""
Parse a human readable data size and return the number of bytes.
@@ -375,6 +379,12 @@ def convert_size_to_int(size: str, binary: bool = False) -> Optional[int]:
binary: (boolean) `True` to use binary multiples of bytes
(base-2) for ambiguous unit symbols and names,
`False` to use decimal multiples of bytes (base-10).
posix_mode: (boolean) Treat one-letter units (k, m, g, etc.) as
binary.
decimal_bias: (boolean) `True` to treat slightly ambiguous two-
letter unit symbols ending in "i" (e.g. Ki, Gi) to
use decimal multiples of bytes (base-10). `False`
(default) to use binary multiples of bytes.
Returns:
integer/None Integer if successful conversion, otherwise None
@@ -394,6 +404,10 @@ def convert_size_to_int(size: str, binary: bool = False) -> Optional[int]:
1000
>>> convert_size_to_int('1 KiB')
1024
>>> convert_size_to_int('1 Ki')
1024
>>> convert_size_to_int('1 Ki', decimal_bias=True)
1000
>>> convert_size_to_int('1 KB', binary=True)
1024
>>> convert_size_to_int('1.5 GB')
@@ -441,17 +455,33 @@ def convert_size_to_int(size: str, binary: bool = False) -> Optional[int]:
# Convert plural units to singular units, for details:
# https://github.com/xolox/python-humanfriendly/issues/26
normalized_unit = normalized_unit.rstrip('s')
# Handle POSIX mode units where `k`, `m`, etc. are treated as binary
# https://www.gnu.org/software/coreutils/manual/html_node/Block-size.html
if len(normalized_unit) == 1 and posix_mode:
normalized_unit = normalized_unit + 'ib'
# Handle two-letter units (Ki, Gi, etc.) These are somewhat
# ambiguous, but are treated as binary by default. This can be
# changed with the `decimal_bias` parameter
if len(normalized_unit) == 2 and normalized_unit[1].lower() == 'i':
if decimal_bias:
normalized_unit = normalized_unit[0]
else:
normalized_unit = normalized_unit + 'b'
for unit in disk_size_units:
# First we check for unambiguous symbols (KiB, MiB, GiB, etc)
# and names (kibibyte, mebibyte, gibibyte, etc) because their
# handling is always the same.
if normalized_unit in (unit.binary.symbol.lower(), unit.binary.name.lower()):
return int(tokens[0] * unit.binary.divider)
# Now we will deal with ambiguous prefixes (K, M, G, etc),
# symbols (KB, MB, GB, etc) and names (kilobyte, megabyte,
# gigabyte, etc) according to the caller's preference.
if (normalized_unit in (unit.decimal.symbol.lower(), unit.decimal.name.lower()) or
normalized_unit.startswith(unit.decimal.symbol[0].lower())):
if (normalized_unit in (unit.decimal.symbol.lower(), unit.decimal.name.lower())
or normalized_unit.startswith(unit.decimal.symbol[0].lower())):
return int(tokens[0] * (unit.binary.divider if binary else unit.decimal.divider))
# We failed to parse the size specification.
return None
@@ -666,12 +696,14 @@ class timestamp:
{'id': 1700, 'format': '%m/%d/%Y, %I:%M:%S %p', 'locale': None}, # Windows english format wint non-UTC tz (found in systeminfo cli output): 3/22/2021, 1:15:51 PM (UTC-0600)
{'id': 1705, 'format': '%m/%d/%Y, %I:%M:%S %p %Z', 'locale': None}, # Windows english format with UTC tz (found in systeminfo cli output): 3/22/2021, 1:15:51 PM (UTC)
{'id': 1710, 'format': '%m/%d/%Y, %I:%M:%S %p UTC%z', 'locale': None}, # Windows english format with UTC tz (found in systeminfo cli output): 3/22/2021, 1:15:51 PM (UTC+0000)
{'id': 1720, 'format': '%A, %B %d, %Y %I:%M:%S %p', 'locale': None}, # ipconfig cli output format: Thursday, June 22, 2023 10:39:04 AM
{'id': 1750, 'format': '%Y/%m/%d-%H:%M:%S.%f', 'locale': None}, # Google Big Table format with no timezone: 1970/01/01-01:00:00.000000
{'id': 1755, 'format': '%Y/%m/%d-%H:%M:%S.%f%z', 'locale': None}, # Google Big Table format with timezone: 1970/01/01-01:00:00.000000+00:00
{'id': 1760, 'format': '%Y-%m-%d %H:%M:%S%z', 'locale': None}, # certbot format with timezone: 2023-06-12 01:35:30+00:00
{'id': 1800, 'format': '%d/%b/%Y:%H:%M:%S %z', 'locale': None}, # Common Log Format: 10/Oct/2000:13:55:36 -0700
{'id': 2000, 'format': '%a %d %b %Y %I:%M:%S %p %Z', 'locale': None}, # en_US.UTF-8 local format (found in upower cli output): Tue 23 Mar 2021 04:12:11 PM UTC
{'id': 3000, 'format': '%a %d %b %Y %I:%M:%S %p', 'locale': None}, # en_US.UTF-8 local format with non-UTC tz (found in upower cli output): Tue 23 Mar 2021 04:12:11 PM IST
{'id': 3100, 'format': '%a %d %b %Y %I:%M:%S %p %z', 'locale': None}, # pacman format - append 00 to end to make it work: # Sat 11 May 2024 06:14:19 AM +0800
{'id': 3500, 'format': '%a, %d %b %Y %H:%M:%S %Z', 'locale': None}, # HTTP header time format (always GMT so assume UTC): Wed, 31 Jan 2024 00:39:28 GMT
{'id': 4000, 'format': '%A %d %B %Y %I:%M:%S %p %Z', 'locale': None}, # European-style local format (found in upower cli output): Tuesday 01 October 2019 12:50:41 PM UTC
{'id': 5000, 'format': '%A %d %B %Y %I:%M:%S %p', 'locale': None}, # European-style local format with non-UTC tz (found in upower cli output): Tuesday 01 October 2019 12:50:41 PM IST

View File

@@ -1,4 +1,4 @@
.TH jc 1 2024-03-23 1.25.2 "JSON Convert"
.TH jc 1 2024-11-25 1.25.4 "JSON Convert"
.SH NAME
\fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types,
and strings
@@ -347,6 +347,11 @@ INI with duplicate key file parser
\fB--ip-address\fP
IPv4 and IPv6 Address string parser
.TP
.B
\fB--ipconfig\fP
`ipconfig` Windows command parser
.TP
.B
\fB--iptables\fP
@@ -507,6 +512,11 @@ openvpn-status.log file parser
\fB--os-release\fP
`/etc/os-release` file parser
.TP
.B
\fB--pacman\fP
`pacman` command parser
.TP
.B
\fB--passwd\fP
@@ -1092,6 +1102,11 @@ Version string parser
\fB--wc\fP
`wc` command parser
.TP
.B
\fB--wg-show\fP
`wg show` command parser
.TP
.B
\fB--who\fP
@@ -1289,7 +1304,7 @@ lines from the beginning through \fBSTOP\fP lines from the end
\fB:\fP
all lines
.SH SlURP
.SH SLURP
Some parsers support multi-item input and can output an array of results in a
single pass. Slurping works for string parsers that accept a single line of
input. (e.g. \fBurl\fP and \fBip-address\fP) To see a list of parsers that support

View File

@@ -5,7 +5,7 @@ with open('README.md', 'r') as f:
setuptools.setup(
name='jc',
version='1.25.2',
version='1.25.4',
author='Kelly Brazil',
author_email='kellyjonbrazil@gmail.com',
description='Converts the output of popular command-line tools and file-types to JSON.',

View File

@@ -194,7 +194,7 @@ lines from the beginning through \fBSTOP\fP lines from the end
\fB:\fP
all lines
.SH SlURP
.SH SLURP
Some parsers support multi-item input and can output an array of results in a
single pass. Slurping works for string parsers that accept a single line of
input. (e.g. \fBurl\fP and \fBip-address\fP) To see a list of parsers that support

View File

@@ -1 +1 @@
[{"filesystem":"devtmpfs","size":1900000000,"used":0,"mounted_on":"/dev","available":1900000000,"use_percent":0},{"filesystem":"tmpfs","size":1900000000,"used":0,"mounted_on":"/dev/shm","available":1900000000,"use_percent":0},{"filesystem":"tmpfs","size":1900000000,"used":12000000,"mounted_on":"/run","available":1900000000,"use_percent":1},{"filesystem":"tmpfs","size":1900000000,"used":0,"mounted_on":"/sys/fs/cgroup","available":1900000000,"use_percent":0},{"filesystem":"/dev/mapper/centos-root","size":17000000000,"used":1800000000,"mounted_on":"/","available":16000000000,"use_percent":11},{"filesystem":"/dev/sda1","size":1014000000,"used":233000000,"mounted_on":"/boot","available":782000000,"use_percent":23},{"filesystem":"tmpfs","size":378000000,"used":0,"mounted_on":"/run/user/1000","available":378000000,"use_percent":0}]
[{"filesystem":"devtmpfs","size":2040109465,"used":0,"mounted_on":"/dev","available":2040109465,"use_percent":0},{"filesystem":"tmpfs","size":2040109465,"used":0,"mounted_on":"/dev/shm","available":2040109465,"use_percent":0},{"filesystem":"tmpfs","size":2040109465,"used":12582912,"mounted_on":"/run","available":2040109465,"use_percent":1},{"filesystem":"tmpfs","size":2040109465,"used":0,"mounted_on":"/sys/fs/cgroup","available":2040109465,"use_percent":0},{"filesystem":"/dev/mapper/centos-root","size":18253611008,"used":1932735283,"mounted_on":"/","available":17179869184,"use_percent":11},{"filesystem":"/dev/sda1","size":1063256064,"used":244318208,"mounted_on":"/boot","available":819986432,"use_percent":23},{"filesystem":"tmpfs","size":396361728,"used":0,"mounted_on":"/run/user/1000","available":396361728,"use_percent":0}]

View File

@@ -1 +1 @@
[{"bssid": "00:19:a9:cd:c6:80", "interface": "wlan0", "freq": 2412, "capability": "ESS ShortPreamble ShortSlotTime (0x0421)", "ssid": "Cisco1240", "supported_rates": [1.0, 2.0, 5.5, 6.0, 9.0, 11.0, 12.0, 18.0], "erp": "<no flags>", "extended_supported_rates": [24.0, 36.0, 48.0, 54.0], "wmm": "Parameter version 1", "be": "CW 15-1023, AIFSN 3", "bk": "CW 15-1023, AIFSN 7", "vi": "CW 7-15, AIFSN 2, TXOP 3008 usec", "vo": "CW 3-7, AIFSN 2, TXOP 1504 usec", "tsf_usec": 2984923701, "beacon_interval_tus": 100, "signal_dbm": -45.0, "last_seen_ms": 429, "selected_rates": [1.0, 2.0, 5.5, 11.0], "ds_parameter_set_channel": 1}, {"bssid": "d0:d0:fd:69:ca:70", "interface": "wlan0", "freq": 2462, "capability": "ESS ShortPreamble ShortSlotTime (0x0421)", "ssid": "Cisco1250", "supported_rates": [1.0, 2.0, 5.5, 6.0, 9.0, 11.0, 12.0, 18.0], "erp": "<no flags>", "extended_supported_rates": [24.0, 36.0, 48.0, 54.0], "wmm": "Parameter version 1", "be": "CW 15-1023, AIFSN 3", "bk": "CW 15-1023, AIFSN 7", "vi": "CW 7-15, AIFSN 2, TXOP 3008 usec", "vo": "acm CW 3-7, AIFSN 2, TXOP 1504 usec", "tsf_usec": 2968648942, "beacon_interval_tus": 102, "signal_dbm": -70.0, "last_seen_ms": 328, "selected_rates": [1.0, 2.0, 5.5, 11.0], "ds_parameter_set_channel": 11}]
[{"bssid":"00:19:a9:cd:c6:80","interface":"wlan0","freq":2412,"capability":"ESS ShortPreamble ShortSlotTime (0x0421)","ssid":"Cisco1240","supported_rates":[1.0,2.0,5.5,6.0,9.0,11.0,12.0,18.0],"erp":"<no flags>","extended_supported_rates":[24.0,36.0,48.0,54.0],"wmm":"Parameter version 1","be":"CW 15-1023, AIFSN 3","bk":"CW 15-1023, AIFSN 7","vi":"CW 7-15, AIFSN 2, TXOP 3008 usec","vo":"CW 3-7, AIFSN 2, TXOP 1504 usec","tsf_usec":2984923701,"beacon_interval_tus":100,"signal_dbm":-45.0,"last_seen_ms":429,"selected_rates":[1.0,2.0,5.5,11.0],"ds_parameter_set_channel":1},{"bssid":"d0:d0:fd:69:ca:70","interface":"wlan0","freq":2462,"capability":"ESS ShortPreamble ShortSlotTime (0x0421)","ssid":"Cisco1250","supported_rates":[1.0,2.0,5.5,6.0,9.0,11.0,12.0,18.0],"erp":"<no flags>","extended_supported_rates":[24.0,36.0,48.0,54.0],"wmm":"Parameter version 1","be":"CW 15-1023, AIFSN 3","bk":"CW 15-1023, AIFSN 7","vi":"CW 7-15, AIFSN 2, TXOP 3008 usec","vo":"acm CW 3-7, AIFSN 2, TXOP 1504 usec","tsf_usec":2968648942,"beacon_interval_tus":102,"signal_dbm":-70.0,"last_seen_ms":328,"selected_rates":[1.0,2.0,5.5,11.0],"ds_parameter_set_channel":11}]

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
[{"bssid":"xx:xx:xx:xx:3e:41","interface":"wlan0-1","freq":2412,"capability":"ESS (0x1431)","information_elements_from_probe_response_frame_ssid":"Troubleshooting","information_elements_from_probe_response_frame_rsn":"Version: 1","information_elements_from_probe_response_frame_group_cipher":"CCMP","information_elements_from_probe_response_frame_pairwise_ciphers":"CCMP","information_elements_from_probe_response_frame_authentication_suites":"PSK","information_elements_from_probe_response_frame_capabilities":"16-PTKSA-RC 1-GTKSA-RC (0x000c)","ht_capabilities_capabilities":"0x1ef","ht_capabilities_max_amsdu_length":"3839 bytes","ht_capabilities_minimum_rx_ampdu_time_spacing":"No restriction (0x00)","ht_capabilities_ht_rx_mcs_rate_indexes_supported":"0-15","ht_operation_primary_channel":1,"ht_operation_secondary_channel_offset":"no secondary","ht_operation_sta_channel_width":"20 MHz","vht_capabilities_0x33800192_max_mpdu_length":11454,"vht_capabilities_0x33800192_supported_channel_width":"neither 160 nor 80 80","vht_rx_mcs_set_1_streams":"MCS 0-9","vht_rx_mcs_set_2_streams":"MCS 0-9","vht_rx_mcs_set_3_streams":"not supported","vht_rx_mcs_set_4_streams":"not supported","vht_rx_mcs_set_5_streams":"not supported","vht_rx_mcs_set_6_streams":"not supported","vht_rx_mcs_set_7_streams":"not supported","vht_rx_mcs_set_8_streams":"not supported","vht_rx_mcs_set_vht_rx_highest_supported":"780 Mbps","vht_tx_mcs_set_1_streams":"MCS 0-9","vht_tx_mcs_set_2_streams":"MCS 0-9","vht_tx_mcs_set_3_streams":"not supported","vht_tx_mcs_set_4_streams":"not supported","vht_tx_mcs_set_5_streams":"not supported","vht_tx_mcs_set_6_streams":"not supported","vht_tx_mcs_set_7_streams":"not supported","vht_tx_mcs_set_8_streams":"not supported","vht_tx_mcs_set_vht_tx_highest_supported":"780 Mbps","vht_tx_mcs_set_vht_extended_nss":"not supported","vht_operation_channel_width":"0 (20 or 40 MHz)","vht_operation_center_freq_segment_1":0,"vht_operation_center_freq_segment_2":0,"vht_operation_vht_basic_mcs_set":"0xfffa","he_mac_capabilities_0x010102000040_minimum_payload_size_of_128_bytes":1,"he_mac_capabilities_0x010102000040_he_phy_capabilities":"(0x06304c090c008008020c00):","he_mac_capabilities_0x010102000040_device_class":1,"he_mac_capabilities_0x010102000040_dcm_max_constellation":1,"he_mac_capabilities_0x010102000040_dcm_max_constellation_rx":1,"he_mac_capabilities_0x010102000040_beamformee_sts_<=_80mhz":3,"he_mac_capabilities_0x010102000040_max_nc":1,"he_mac_capabilities_0x010102000040_1_streams":"MCS 0-11","he_mac_capabilities_0x010102000040_2_streams":"MCS 0-11","he_mac_capabilities_0x010102000040_3_streams":"not supported","he_mac_capabilities_0x010102000040_4_streams":"not supported","he_mac_capabilities_0x010102000040_5_streams":"not supported","he_mac_capabilities_0x010102000040_6_streams":"not supported","he_mac_capabilities_0x010102000040_7_streams":"not supported","he_mac_capabilities_0x010102000040_8_streams":"not supported","tsf_usec":608162896731,"beacon_interval_tus":100,"signal_dbm":-54.0,"last_seen_ms":1410}]

109
tests/fixtures/centos-7.7/iw-scan2.out vendored Normal file
View File

@@ -0,0 +1,109 @@
BSS xx:xx:xx:xx:3e:41(on wlan0-1)
last seen: 4206.107s [boottime]
TSF: 608162896731 usec (7d, 00:56:02)
freq: 2412
beacon interval: 100 TUs
capability: ESS (0x1431)
signal: -54.00 dBm
last seen: 1410 ms ago
Information elements from Probe Response frame:
SSID: Troubleshooting
RSN: * Version: 1
* Group cipher: CCMP
* Pairwise ciphers: CCMP
* Authentication suites: PSK
* Capabilities: 16-PTKSA-RC 1-GTKSA-RC (0x000c)
HT capabilities:
Capabilities: 0x1ef
RX LDPC
HT20/HT40
SM Power Save disabled
RX HT20 SGI
RX HT40 SGI
TX STBC
RX STBC 1-stream
Max AMSDU length: 3839 bytes
No DSSS/CCK HT40
Maximum RX AMPDU length 65535 bytes (exponent: 0x003)
Minimum RX AMPDU time spacing: No restriction (0x00)
HT RX MCS rate indexes supported: 0-15
HT TX MCS rate indexes are undefined
HT operation:
* primary channel: 1
* secondary channel offset: no secondary
* STA channel width: 20 MHz
VHT capabilities:
VHT Capabilities (0x33800192):
Max MPDU length: 11454
Supported Channel Width: neither 160 nor 80 80
RX LDPC
TX STBC
RX antenna pattern consistency
TX antenna pattern consistency
VHT RX MCS set:
1 streams: MCS 0-9
2 streams: MCS 0-9
3 streams: not supported
4 streams: not supported
5 streams: not supported
6 streams: not supported
7 streams: not supported
8 streams: not supported
VHT RX highest supported: 780 Mbps
VHT TX MCS set:
1 streams: MCS 0-9
2 streams: MCS 0-9
3 streams: not supported
4 streams: not supported
5 streams: not supported
6 streams: not supported
7 streams: not supported
8 streams: not supported
VHT TX highest supported: 780 Mbps
VHT extended NSS: not supported
VHT operation:
* channel width: 0 (20 or 40 MHz)
* center freq segment 1: 0
* center freq segment 2: 0
* VHT basic MCS set: 0xfffa
HE capabilities:
HE MAC Capabilities (0x010102000040):
HTC HE Supported
Minimum Payload size of 128 bytes: 1
OM Control
A-MSDU in A-MPDU
HE PHY Capabilities: (0x06304c090c008008020c00):
HE40/2.4GHz
HE40/HE80/5GHz
Device Class: 1
LDPC Coding in Payload
STBC Tx <= 80MHz
STBC Rx <= 80MHz
Full Bandwidth UL MU-MIMO
DCM Max Constellation: 1
DCM Max Constellation Rx: 1
Beamformee STS <= 80Mhz: 3
PPE Threshold Present
Max NC: 1
20MHz in 40MHz HE PPDU 2.4GHz
TX 1024-QAM
RX 1024-QAM
HE RX MCS and NSS set <= 80 MHz
1 streams: MCS 0-11
2 streams: MCS 0-11
3 streams: not supported
4 streams: not supported
5 streams: not supported
6 streams: not supported
7 streams: not supported
8 streams: not supported
HE TX MCS and NSS set <= 80 MHz
1 streams: MCS 0-11
2 streams: MCS 0-11
3 streams: not supported
4 streams: not supported
5 streams: not supported
6 streams: not supported
7 streams: not supported
8 streams: not supported
PPE Threshold 0x19 0x1c 0xc7 0x71

View File

@@ -0,0 +1,37 @@
Controller 48:A4:72:3C:96:63 (public)
Manufacturer: 0x0002 (2)
Version: 0x08 (8)
Name: ubuntu
Alias: ubuntu
Class: 0x007c0104 (8126724)
Powered: yes
Discoverable: yes
DiscoverableTimeout: 0x000000b4 (180)
Pairable: yes
UUID: Message Notification Se.. (00001133-0000-1000-8000-00805f9b34fb)
UUID: A/V Remote Control (0000110e-0000-1000-8000-00805f9b34fb)
UUID: OBEX Object Push (00001105-0000-1000-8000-00805f9b34fb)
UUID: Message Access Server (00001132-0000-1000-8000-00805f9b34fb)
UUID: PnP Information (00001200-0000-1000-8000-00805f9b34fb)
UUID: IrMC Sync (00001104-0000-1000-8000-00805f9b34fb)
UUID: Vendor specific (00005005-0000-1000-8000-0002ee000001)
UUID: A/V Remote Control Target (0000110c-0000-1000-8000-00805f9b34fb)
UUID: Generic Attribute Profile (00001801-0000-1000-8000-00805f9b34fb)
UUID: Phonebook Access Server (0000112f-0000-1000-8000-00805f9b34fb)
UUID: Audio Sink (0000110b-0000-1000-8000-00805f9b34fb)
UUID: Device Information (0000180a-0000-1000-8000-00805f9b34fb)
UUID: Generic Access Profile (00001800-0000-1000-8000-00805f9b34fb)
UUID: Handsfree Audio Gateway (0000111f-0000-1000-8000-00805f9b34fb)
UUID: Audio Source (0000110a-0000-1000-8000-00805f9b34fb)
UUID: OBEX File Transfer (00001106-0000-1000-8000-00805f9b34fb)
UUID: Handsfree (0000111e-0000-1000-8000-00805f9b34fb)
Modalias: usb:v1D6Bp0246d0548
Discovering: yes
Roles: central
Roles: peripheral
Advertising Features:
ActiveInstances: 0x00 (0)
SupportedInstances: 0x05 (5)
SupportedIncludes: tx-power
SupportedIncludes: appearance
SupportedIncludes: local-name

View File

@@ -0,0 +1,17 @@
Device 67:F6:B4:0E:5C:94 (public)
Name: WH-1000XM3
Alias: WH-1000XM3
Class: 0x11240404 (2360324)
Icon: audio-headset
Paired: yes
Bonded: yes
Trusted: yes
Blocked: no
Connected: yes
LegacyPairing: no
UUID: Vendor specific (fd096fad-eed7-4504-943b-5fa1c0e761b2)
UUID: Vendor specific (03c57488-f7b6-45a3-8a23-ed4a890075cd)
UUID: Vendor specific (77a369ae-e453-4ff7-bc84-dc8f411eaa6a)
UUID: Vendor specific (8c274bd0-e7bd-4ed0-a391-55465e38005c)
Modalias: usb:v052Cp0DC3d1426
Battery Percentage: 0x46 (70)

0
tests/fixtures/generic/csv-10k-sales-records.csv vendored Executable file → Normal file
View File

Can't render this file because it is too large.

View File

@@ -0,0 +1 @@
{"name":"eth0","supported_pause_frame_use":"Symmetric Receive-only","supports_auto_negotiation":true,"supported_fec_modes":[],"advertised_pause_frame_use":false,"advertised_auto_negotiation":true,"advertised_fec_modes":[],"link_partner_advertised_pause_frame_use":"Symmetric","link_partner_advertised_auto_negotiation":"Yes","link_partner_advertised_fec_modes":"Not reported","speed":"100Mb/s","duplex":"Full","port":"Twisted Pair","phyad":"0","transceiver":"external","auto_negotiation":false,"mdi_x":"Unknown","supports_wake_on":"d","wake_on":"d","link_detected":true,"supported_ports":["TP","MII"],"supported_link_modes":["10baseT/Half","10baseT/Full","100baseT/Half","100baseT/Full","1000baseT/Half","1000baseT/Full"],"advertised_link_modes":["10baseT/Half","10baseT/Full","100baseT/Half","100baseT/Full","1000baseT/Half","1000baseT/Full"],"link_partner_advertised_link_modes":["10baseT/Half","10baseT/Full","100baseT/Half","100baseT/Full"],"current_message_level":["0x00000007 (7)","drv probe link"],"speed_bps":100000000}

View File

@@ -0,0 +1,31 @@
Settings for eth0:
Supported ports: [ TP MII ]
Supported link modes: 10baseT/Half 10baseT/Full
100baseT/Half 100baseT/Full
1000baseT/Half 1000baseT/Full
Supported pause frame use: Symmetric Receive-only
Supports auto-negotiation: Yes
Supported FEC modes: Not reported
Advertised link modes: 10baseT/Half 10baseT/Full
100baseT/Half 100baseT/Full
1000baseT/Half 1000baseT/Full
Advertised pause frame use: Symmetric Receive-only
Advertised auto-negotiation: Yes
Advertised FEC modes: Not reported
Link partner advertised link modes: 10baseT/Half 10baseT/Full
100baseT/Half 100baseT/Full
Link partner advertised pause frame use: Symmetric
Link partner advertised auto-negotiation: Yes
Link partner advertised FEC modes: Not reported
Speed: 100Mb/s
Duplex: Full
Port: Twisted Pair
PHYAD: 0
Transceiver: external
Auto-negotiation: on
MDI-X: Unknown
Supports Wake-on: d
Wake-on: d
Current message level: 0x00000007 (7)
drv probe link
Link detected: yes

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +1 @@
[{"commit":"e05824a36ca62aa9f3a21854ec8b40a3e0f7a68d","author":"Benedikt Heine","author_email":"bebe@bebehei.de","date":"Mon Oct 28 12:42:22 2019 +0100","commit_by":"Benedikt Heine","commit_by_email":"bebe@bebehei.de","commit_by_date":"Sun Apr 12 17:27:16 2020 +0200","stats":{"files_changed":1,"insertions":13,"deletions":3,"files":["salt/modules/monit.py"]},"message":"Split monit status fields on monit version\n\nWith the commit [0] on monit, the field size changed. So splitting hard\nafter 35 chars, new versions of monit break when using monit.status.\n\n[0] https://bitbucket.org/tildeslash/monit/commits/\n471c4bbc388c1c536f07ce1dd26b811bd39a9467","epoch":1572291742,"epoch_utc":null},{"commit":"910a2ac4809bb05b886adfe75f4857eb53fdfbb1","merge":"6c3964ce30 f0a1e923e3","author":"Daniel Wozniak","author_email":"dwozniak@saltstack.com","date":"Sun Apr 12 00:09:37 2020 -0700","commit_by":"GitHub","commit_by_email":"noreply@github.com","commit_by_date":"Sun Apr 12 00:09:37 2020 -0700","message":"Merge pull request #53911 from terminalmage/squelch-log\n\nalternatives: Don't log error when running \"alternatives --display\" on nonexistant target","epoch":1586675377,"epoch_utc":null},{"commit":"6c3964ce30929e749c0965bc0d60527e9fe8dbb1","merge":"3026c25faf 2ac4da54e3","author":"Daniel Wozniak","author_email":"dwozniak@saltstack.com","date":"Sun Apr 12 00:09:16 2020 -0700","commit_by":"GitHub","commit_by_email":"noreply@github.com","commit_by_date":"Sun Apr 12 00:09:16 2020 -0700","message":"Merge pull request #54199 from driskell/patch-2\n\nFix broken sdb.get_or_set_hash for Hashicorp Vault","epoch":1586675356,"epoch_utc":null}]
[{"commit":"e05824a36ca62aa9f3a21854ec8b40a3e0f7a68d","author":"Benedikt Heine","author_email":"bebe@bebehei.de","date":"Mon Oct 28 12:42:22 2019 +0100","commit_by":"Benedikt Heine","commit_by_email":"bebe@bebehei.de","commit_by_date":"Sun Apr 12 17:27:16 2020 +0200","stats":{"files_changed":1,"insertions":13,"deletions":3,"files":["salt/modules/monit.py"],"file_stats":[{"name":"salt/modules/monit.py","lines_changed":16}]},"message":"Split monit status fields on monit version\n\nWith the commit [0] on monit, the field size changed. So splitting hard\nafter 35 chars, new versions of monit break when using monit.status.\n\n[0] https://bitbucket.org/tildeslash/monit/commits/\n471c4bbc388c1c536f07ce1dd26b811bd39a9467","epoch":1572291742,"epoch_utc":null},{"commit":"910a2ac4809bb05b886adfe75f4857eb53fdfbb1","merge":"6c3964ce30 f0a1e923e3","author":"Daniel Wozniak","author_email":"dwozniak@saltstack.com","date":"Sun Apr 12 00:09:37 2020 -0700","commit_by":"GitHub","commit_by_email":"noreply@github.com","commit_by_date":"Sun Apr 12 00:09:37 2020 -0700","message":"Merge pull request #53911 from terminalmage/squelch-log\n\nalternatives: Don't log error when running \"alternatives --display\" on nonexistant target","epoch":1586675377,"epoch_utc":null},{"commit":"6c3964ce30929e749c0965bc0d60527e9fe8dbb1","merge":"3026c25faf 2ac4da54e3","author":"Daniel Wozniak","author_email":"dwozniak@saltstack.com","date":"Sun Apr 12 00:09:16 2020 -0700","commit_by":"GitHub","commit_by_email":"noreply@github.com","commit_by_date":"Sun Apr 12 00:09:16 2020 -0700","message":"Merge pull request #54199 from driskell/patch-2\n\nFix broken sdb.get_or_set_hash for Hashicorp Vault","epoch":1586675356,"epoch_utc":null}]

View File

@@ -1 +1 @@
[{"commit":"e05824a36ca62aa9f3a21854ec8b40a3e0f7a68d","author":"Benedikt Heine","author_email":"bebe@bebehei.de","date":"Mon Oct 28 12:42:22 2019 +0100","commit_by":"Benedikt Heine","commit_by_email":"bebe@bebehei.de","commit_by_date":"Sun Apr 12 17:27:16 2020 +0200","stats":{"files_changed":1,"insertions":13,"deletions":3,"files":["salt/modules/monit.py"]},"message":"Split monit status fields on monit version\n\nWith the commit [0] on monit, the field size changed. So splitting hard\nafter 35 chars, new versions of monit break when using monit.status.\n\n[0] https://bitbucket.org/tildeslash/monit/commits/\n471c4bbc388c1c536f07ce1dd26b811bd39a9467","epoch":1572291742,"epoch_utc":null},{"commit":"910a2ac4809bb05b886adfe75f4857eb53fdfbb1","merge":"6c3964ce30 f0a1e923e3","author":"Daniel Wozniak","author_email":"dwozniak@saltstack.com","date":"Sun Apr 12 00:09:37 2020 -0700","commit_by":"GitHub","commit_by_email":"noreply@github.com","commit_by_date":"Sun Apr 12 00:09:37 2020 -0700","message":"Merge pull request #53911 from terminalmage/squelch-log\n\nalternatives: Don't log error when running \"alternatives --display\" on nonexistant target","epoch":1586675377,"epoch_utc":null},{"commit":"6c3964ce30929e749c0965bc0d60527e9fe8dbb1","merge":"3026c25faf 2ac4da54e3","author":"Daniel Wozniak","author_email":"dwozniak@saltstack.com","date":"Sun Apr 12 00:09:16 2020 -0700","commit_by":"GitHub","commit_by_email":"noreply@github.com","commit_by_date":"Sun Apr 12 00:09:16 2020 -0700","message":"Merge pull request #54199 from driskell/patch-2\n\nFix broken sdb.get_or_set_hash for Hashicorp Vault","epoch":1586675356,"epoch_utc":null}]
[{"commit":"e05824a36ca62aa9f3a21854ec8b40a3e0f7a68d","author":"Benedikt Heine","author_email":"bebe@bebehei.de","date":"Mon Oct 28 12:42:22 2019 +0100","commit_by":"Benedikt Heine","commit_by_email":"bebe@bebehei.de","commit_by_date":"Sun Apr 12 17:27:16 2020 +0200","stats":{"files_changed":1,"insertions":13,"deletions":3,"files":["salt/modules/monit.py"],"file_stats":[{"name":"salt/modules/monit.py","lines_changed":16}]},"message":"Split monit status fields on monit version\n\nWith the commit [0] on monit, the field size changed. So splitting hard\nafter 35 chars, new versions of monit break when using monit.status.\n\n[0] https://bitbucket.org/tildeslash/monit/commits/\n471c4bbc388c1c536f07ce1dd26b811bd39a9467","epoch":1572291742,"epoch_utc":null},{"commit":"910a2ac4809bb05b886adfe75f4857eb53fdfbb1","merge":"6c3964ce30 f0a1e923e3","author":"Daniel Wozniak","author_email":"dwozniak@saltstack.com","date":"Sun Apr 12 00:09:37 2020 -0700","commit_by":"GitHub","commit_by_email":"noreply@github.com","commit_by_date":"Sun Apr 12 00:09:37 2020 -0700","message":"Merge pull request #53911 from terminalmage/squelch-log\n\nalternatives: Don't log error when running \"alternatives --display\" on nonexistant target","epoch":1586675377,"epoch_utc":null},{"commit":"6c3964ce30929e749c0965bc0d60527e9fe8dbb1","merge":"3026c25faf 2ac4da54e3","author":"Daniel Wozniak","author_email":"dwozniak@saltstack.com","date":"Sun Apr 12 00:09:16 2020 -0700","commit_by":"GitHub","commit_by_email":"noreply@github.com","commit_by_date":"Sun Apr 12 00:09:16 2020 -0700","message":"Merge pull request #54199 from driskell/patch-2\n\nFix broken sdb.get_or_set_hash for Hashicorp Vault","epoch":1586675356,"epoch_utc":null}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
[{"filesystem":"//10.100.1.237/Bilder und Videos","mount_point":"/mnt/smb/thw/media","type":"cifs","options":["rw","nosuid","nodev","relatime","vers=2.1","cache=strict","username=iweinmann","uid=1000","noforceuid","gid=1000","noforcegid","addr=10.100.1.237","file_mode=0755","dir_mode=0755","iocharset=utf8","soft","nounix","serverino","mapposix","rsize=1048576","wsize=1048576","bsize=1048576","retrans=1","echo_interval=60","actimeo=1","closetimeo=1","user=ingo"]}]

View File

@@ -0,0 +1 @@
//10.100.1.237/Bilder und Videos on /mnt/smb/thw/media type cifs (rw,nosuid,nodev,relatime,vers=2.1,cache=strict,username=iweinmann,uid=1000,noforceuid,gid=1000,noforcegid,addr=10.100.1.237,file_mode=0755,dir_mode=0755,iocharset=utf8,soft,nounix,serverino,mapposix,rsize=1048576,wsize=1048576,bsize=1048576,retrans=1,echo_interval=60,actimeo=1,closetimeo=1,user=ingo)

View File

@@ -1 +1 @@
[{"zone":"sunet.se","status":{"state":"ok","served-serial":"2023091302 since 2023-09-14T00:50:11","commit-serial":"2023091302 since 2023-09-14T07:04:05","wait":"27023 sec between attempts"}},{"zone":"catz.sunet.se","status":{"pattern":"example.catalog","catalog-member-id":"4b6f6ce2de5929e4.zones.example.catalog.","state":"ok","served-serial":"1705484863 since 2024-01-17T13:06:02","commit-serial":"1705484863 since 2024-01-17T13:06:02","wait":"21341 sec between attempts"}}]
[{"zone":"sunet.se","status":{"state":"ok","served-serial":"2023091302 since 2023-09-14T00:50:11","commit-serial":"2023091302 since 2023-09-14T07:04:05","wait":"27023 sec between attempts"}},{"zone":"sunet.dev","status":{"state":"refreshing","served-serial":"2023095893 since 2024-10-10T10:10:35","commit-serial":"2023095893 since 2024-10-18T10:00:45","notified-serial":"2023095880 since 2024-10-19T06:11:35","transfer":"TCP connected to 192.168.1.100"}},{"zone":"catz.sunet.se","status":{"pattern":"example.catalog","catalog-member-id":"4b6f6ce2de5929e4.zones.example.catalog.","state":"ok","served-serial":"1705484863 since 2024-01-17T13:06:02","commit-serial":"1705484863 since 2024-01-17T13:06:02","wait":"21341 sec between attempts"}}]

View File

@@ -3,6 +3,12 @@ zone: sunet.se
served-serial: "2023091302 since 2023-09-14T00:50:11"
commit-serial: "2023091302 since 2023-09-14T07:04:05"
wait: "27023 sec between attempts"
zone: sunet.dev
state: refreshing
served-serial: "2023095893 since 2024-10-10T10:10:35"
commit-serial: "2023095893 since 2024-10-18T10:00:45"
notified-serial: "2023095880 since 2024-10-19T06:11:35"
transfer: "TCP connected to 192.168.1.100"
zone: catz.sunet.se
pattern: example.catalog
catalog-member-id: 4b6f6ce2de5929e4.zones.example.catalog.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More