mirror of
https://github.com/kellyjonbrazil/jc.git
synced 2026-04-03 17:44:07 +02:00
Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fd13e6987 | ||
|
|
1feb000231 | ||
|
|
d270ddc6ce | ||
|
|
07ef285b06 | ||
|
|
467ad60e20 | ||
|
|
91b3572ead | ||
|
|
60caa17eb9 | ||
|
|
eea31ddbe4 | ||
|
|
61793bba31 | ||
|
|
51e4a476ba | ||
|
|
883f0c9cf7 | ||
|
|
ffaf455d43 | ||
|
|
b520532578 | ||
|
|
3b1af1a9b6 | ||
|
|
0fa5f7f67f | ||
|
|
a77298e560 | ||
|
|
5f75df2e4b | ||
|
|
38d169f4d0 | ||
|
|
e737dde9e0 | ||
|
|
6e49c87575 | ||
|
|
6afa6b449e | ||
|
|
424d9c1347 | ||
|
|
10d1ab1c74 | ||
|
|
5db256c3e7 | ||
|
|
7f7dcc35ec | ||
|
|
edcde58cb9 | ||
|
|
3a7ca118ff | ||
|
|
f36c713110 | ||
|
|
0db2ad2f5e | ||
|
|
816c38e1fe | ||
|
|
e4cf7b502e | ||
|
|
4ca7179481 | ||
|
|
bebd6a60fb | ||
|
|
5d7940a89b | ||
|
|
ac8120e1fe | ||
|
|
a854f1d6b0 | ||
|
|
7c9e92e284 | ||
|
|
4c2775970d | ||
|
|
ba0fc5dad9 | ||
|
|
222d1b7255 | ||
|
|
34fda0905f | ||
|
|
ccbe36e712 | ||
|
|
bb1136c33c | ||
|
|
c6a13aa4d8 | ||
|
|
d04a175d36 | ||
|
|
966df71566 | ||
|
|
c8599253fb | ||
|
|
65d9cc718f | ||
|
|
9b4190f1e6 | ||
|
|
c519657d85 | ||
|
|
d5cceb77e9 | ||
|
|
5cde110afb | ||
|
|
65aa6c1a9a | ||
|
|
5394d7f62d | ||
|
|
ebbd4e9320 | ||
|
|
834acfb2d6 | ||
|
|
1182c9f263 | ||
|
|
ed3046bb1b | ||
|
|
ca73076b56 | ||
|
|
9efc014dce | ||
|
|
dbe1757a48 | ||
|
|
1c09289d95 | ||
|
|
e9ccedf0d7 | ||
|
|
fb4b4eeb58 | ||
|
|
896891ad9e | ||
|
|
7fda79d841 | ||
|
|
8995ac686a | ||
|
|
006047553d | ||
|
|
c56e38c66c | ||
|
|
ee3b873ef4 | ||
|
|
a39cb05228 | ||
|
|
f54ceaa793 | ||
|
|
99cce72244 | ||
|
|
0c40e3a3c9 | ||
|
|
025d00ecd2 | ||
|
|
a2b5d41308 | ||
|
|
366589268e | ||
|
|
53dd28b65e | ||
|
|
0be6528aff | ||
|
|
6ceaa7749e | ||
|
|
6ae2e17ea1 | ||
|
|
ef9ca9322e | ||
|
|
95cba21d73 | ||
|
|
be1dd031f1 | ||
|
|
7fbe1e9f21 | ||
|
|
7d33850d43 | ||
|
|
7887789d0d | ||
|
|
7ddd2a4ce2 | ||
|
|
34ab34cc66 | ||
|
|
a8b231da81 | ||
|
|
2278c7ecab | ||
|
|
4cb88977cc | ||
|
|
0af4a3a5d7 | ||
|
|
c0f9b705c6 | ||
|
|
fa416083f2 | ||
|
|
e858faa746 | ||
|
|
a8f769eea3 | ||
|
|
26133261f9 | ||
|
|
6072ea0ec7 | ||
|
|
95672c23b7 | ||
|
|
a2e0e6d549 | ||
|
|
2df5e79295 | ||
|
|
c5e0642b0b | ||
|
|
78150ded70 | ||
|
|
05f3e4ea8a | ||
|
|
b7cf0ca8d4 | ||
|
|
ba6e1e694e | ||
|
|
aec3e3cd13 | ||
|
|
d9363ae473 | ||
|
|
0f367a435a | ||
|
|
0955598b49 | ||
|
|
f2a8e0087b | ||
|
|
97e9798cef | ||
|
|
413519e02c | ||
|
|
5c855e40c6 | ||
|
|
b66c5dbf55 | ||
|
|
b054b1b782 | ||
|
|
1593d0bf79 | ||
|
|
8a22f8a468 | ||
|
|
c26f0641ff | ||
|
|
40eb2b7ef6 | ||
|
|
71af0c5555 | ||
|
|
9f5532d91f | ||
|
|
c68c919024 | ||
|
|
9eb4df34b1 | ||
|
|
921133f3ac | ||
|
|
0350607359 |
36
.github/workflows/pythonapp.yml
vendored
36
.github/workflows/pythonapp.yml
vendored
@@ -9,12 +9,41 @@ on:
|
||||
- "**/*.py"
|
||||
|
||||
jobs:
|
||||
old_python:
|
||||
very_old_python:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-13, ubuntu-20.04, windows-2019]
|
||||
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10"]
|
||||
os: [macos-13, windows-2022]
|
||||
python-version: ["3.6"]
|
||||
|
||||
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
|
||||
|
||||
old_python:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-13, ubuntu-22.04, windows-2022]
|
||||
python-version: ["3.7", "3.8", "3.9", "3.10"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
@@ -37,6 +66,7 @@ jobs:
|
||||
python -m unittest discover tests
|
||||
|
||||
latest_python:
|
||||
if: github.event.pull_request.draft == false
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -7,3 +7,4 @@ build/
|
||||
.vscode/
|
||||
_config.yml
|
||||
.venv
|
||||
.DS_Store
|
||||
|
||||
48
CHANGELOG
48
CHANGELOG
@@ -1,8 +1,56 @@
|
||||
jc changelog
|
||||
|
||||
202501012 v1.25.6
|
||||
- Add `net-localgroup` Windows command parser
|
||||
- Add `net-user` Windows command parser
|
||||
- Add `route-print` Windows command parser
|
||||
- Add `traceroute-s` streaming command parser
|
||||
- Add `max_hops` and `data_bytes` fields to `traceroute` command parser
|
||||
- Add `x509-crl` file parser to support Certificate Revocation List PEM and DER files
|
||||
- Add `yay` as a magic command for the `pacman` command parser
|
||||
- Fix `bluetoothctl` command parser to support output with the `cable_pairing` attribute
|
||||
- Fix `nmcli` command parser to support blank `team.config` JSON value and `team-port.config` JSON value
|
||||
- Fix `top` command parsers to correct memory size field parsing. Several new unit
|
||||
and byte conversion fields have been added
|
||||
- Fix `who` command parser to support the `process` field on Debian13
|
||||
|
||||
20250503 v1.25.5
|
||||
- Add `amixer` command parser
|
||||
- Enhance `iptables` command parser to add default policy statistics fields
|
||||
- Fix `bluetoothctl` parser failing to parse controllers with power state prop
|
||||
- Fix `lsblk` command parser to support multiple mountpoints. Also, added
|
||||
byte conversions for size fields.
|
||||
- Fix `nmcli` command parser to support `team.config` JSON field
|
||||
- Fix `time` command parser for output that does not contain centiseconds
|
||||
- Fix `x509-cert` parser to handle IDNA2008 encoded email addresses with a warning
|
||||
- Fix typing for upcoming python v3.14
|
||||
- Fix timezone setting for tests to support minimal chrooted builds
|
||||
|
||||
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
|
||||
|
||||
12
README.md
12
README.md
@@ -1,4 +1,3 @@
|
||||
[](https://github.com/kellyjonbrazil/jc/actions)
|
||||
[](https://pypi.org/project/jc/)
|
||||
|
||||
> Check out the `jc` Python [package documentation](https://github.com/kellyjonbrazil/jc/tree/master/docs) for developers
|
||||
@@ -159,6 +158,7 @@ option.
|
||||
| `--acpi` | `acpi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/acpi) |
|
||||
| `--airport` | `airport -I` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/airport) |
|
||||
| `--airport-s` | `airport -s` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/airport_s) |
|
||||
| `--amixer` | `amixer` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/amixer) |
|
||||
| `--apt-cache-show` | `apt-cache show` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/apt_cache_show) |
|
||||
| `--apt-get-sqq` | `apt-get -sqq` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/apt_get_sqq) |
|
||||
| `--arp` | `arp` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/arp) |
|
||||
@@ -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) |
|
||||
@@ -244,12 +245,15 @@ option.
|
||||
| `--mpstat-s` | `mpstat` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/mpstat_s) |
|
||||
| `--needrestart` | `needrestart -b` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/needrestart) |
|
||||
| `--netstat` | `netstat` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/netstat) |
|
||||
| `--net-localgroup` | `net localgroup` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/net_localgroup) |
|
||||
| `--net-user` | `net user` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/net_user) |
|
||||
| `--nmcli` | `nmcli` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/nmcli) |
|
||||
| `--nsd-control` | `nsd-control` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/nsd_control) |
|
||||
| `--ntpq` | `ntpq -p` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ntpq) |
|
||||
| `--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) |
|
||||
@@ -269,6 +273,7 @@ option.
|
||||
| `--ps` | `ps` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ps) |
|
||||
| `--resolve-conf` | `/etc/resolve.conf` file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/resolve_conf) |
|
||||
| `--route` | `route` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/route) |
|
||||
| `--route-print` | `route print` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/route_print) |
|
||||
| `--rpm-qi` | `rpm -qi` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rpm_qi) |
|
||||
| `--rsync` | `rsync` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync) |
|
||||
| `--rsync-s` | `rsync` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/rsync_s) |
|
||||
@@ -300,6 +305,7 @@ option.
|
||||
| `--top-s` | `top -b` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/top_s) |
|
||||
| `--tracepath` | `tracepath` and `tracepath6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/tracepath) |
|
||||
| `--traceroute` | `traceroute` and `traceroute6` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/traceroute) |
|
||||
| `--traceroute-s` | `traceroute` and `traceroute6` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/traceroute_s) |
|
||||
| `--tune2fs` | `tune2fs -l` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/tune2fs) |
|
||||
| `--udevadm` | `udevadm info` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/udevadm) |
|
||||
| `--ufw` | `ufw status` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ufw) |
|
||||
@@ -316,8 +322,10 @@ 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-crl` | X.509 PEM and DER certificate revocation list file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/x509_crl) |
|
||||
| `--x509-csr` | X.509 PEM and DER certificate request file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/x509_csr) |
|
||||
| `--xml` | XML file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/xml) |
|
||||
| `--xrandr` | `xrandr` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/xrandr) |
|
||||
@@ -1375,4 +1383,4 @@ cat istio.yaml | jc -p --yaml
|
||||
]
|
||||
```
|
||||
|
||||
© 2019-2024 Kelly Brazil
|
||||
© 2019-2025 Kelly Brazil
|
||||
@@ -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 amixer 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 net 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 yay zipinfo zpool)
|
||||
jc_parsers=(--acpi --airport --airport-s --amixer --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 --net-localgroup --net-user --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 --route-print --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 --traceroute-s --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-crl --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)
|
||||
|
||||
@@ -9,10 +9,11 @@ _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 amixer 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 net 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 yay zipinfo zpool)
|
||||
jc_commands_describe=(
|
||||
'acpi:run "acpi" command with magic syntax.'
|
||||
'airport:run "airport" command with magic syntax.'
|
||||
'amixer:run "amixer" command with magic syntax.'
|
||||
'apt-cache:run "apt-cache" command with magic syntax.'
|
||||
'apt-get:run "apt-get" command with magic syntax.'
|
||||
'arp:run "arp" command with magic syntax.'
|
||||
@@ -46,6 +47,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.'
|
||||
@@ -66,11 +68,13 @@ _jc() {
|
||||
'mount:run "mount" command with magic syntax.'
|
||||
'mpstat:run "mpstat" command with magic syntax.'
|
||||
'needrestart:run "needrestart" command with magic syntax.'
|
||||
'net:run "net" command with magic syntax.'
|
||||
'netstat:run "netstat" command with magic syntax.'
|
||||
'nmcli:run "nmcli" command with magic syntax.'
|
||||
'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,16 +120,19 @@ _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.'
|
||||
'yay:run "yay" 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 --amixer --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 --net-localgroup --net-user --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 --route-print --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 --traceroute-s --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-crl --x509-csr --xml --xrandr --yaml --zipinfo --zpool-iostat --zpool-status)
|
||||
jc_parsers_describe=(
|
||||
'--acpi:`acpi` command parser'
|
||||
'--airport:`airport -I` command parser'
|
||||
'--airport-s:`airport -s` command parser'
|
||||
'--amixer:`amixer` command parser'
|
||||
'--apt-cache-show:`apt-cache show` command parser'
|
||||
'--apt-get-sqq:`apt-get -sqq` command parser'
|
||||
'--arp:`arp` command parser'
|
||||
@@ -185,6 +192,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'
|
||||
@@ -211,12 +219,15 @@ _jc() {
|
||||
'--mpstat-s:`mpstat` command streaming parser'
|
||||
'--needrestart:`needrestart -b` command parser'
|
||||
'--netstat:`netstat` command parser'
|
||||
'--net-localgroup:`net localgroup` command parser'
|
||||
'--net-user:`net user` command parser'
|
||||
'--nmcli:`nmcli` command parser'
|
||||
'--nsd-control:`nsd-control` command parser'
|
||||
'--ntpq:`ntpq -p` command parser'
|
||||
'--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'
|
||||
@@ -287,6 +298,7 @@ _jc() {
|
||||
'--ps:`ps` command parser'
|
||||
'--resolve-conf:`/etc/resolve.conf` file parser'
|
||||
'--route:`route` command parser'
|
||||
'--route-print:`route print` command parser'
|
||||
'--rpm-qi:`rpm -qi` command parser'
|
||||
'--rsync:`rsync` command parser'
|
||||
'--rsync-s:`rsync` command streaming parser'
|
||||
@@ -318,6 +330,7 @@ _jc() {
|
||||
'--top-s:`top -b` command streaming parser'
|
||||
'--tracepath:`tracepath` and `tracepath6` command parser'
|
||||
'--traceroute:`traceroute` and `traceroute6` command parser'
|
||||
'--traceroute-s:`traceroute` and `traceroute6` command streaming parser'
|
||||
'--tune2fs:`tune2fs -l` command parser'
|
||||
'--udevadm:`udevadm info` command parser'
|
||||
'--ufw:`ufw status` command parser'
|
||||
@@ -334,8 +347,10 @@ _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-crl:X.509 PEM and DER certificate revocation list file parser'
|
||||
'--x509-csr:X.509 PEM and DER certificate request file parser'
|
||||
'--xml:XML file parser'
|
||||
'--xrandr:`xrandr` command parser'
|
||||
|
||||
12
docs/lib.md
12
docs/lib.md
@@ -36,7 +36,7 @@ returned.
|
||||
|
||||
Parameters:
|
||||
|
||||
documentation: (boolean) include parser docstrings if True
|
||||
documentation: (boolean) include parser docstrings if `True`
|
||||
show_hidden: (boolean) also show parsers marked as hidden
|
||||
in their info metadata.
|
||||
show_deprecated: (boolean) also show parsers marked as
|
||||
@@ -172,17 +172,17 @@ Parameters:
|
||||
variants of the module name.
|
||||
|
||||
A Module object can also be passed
|
||||
directly or via get_parser()
|
||||
directly or via `get_parser()`
|
||||
|
||||
data: (string or data to parse (string or bytes for
|
||||
bytes or standard parsers, iterable of
|
||||
iterable) strings for streaming parsers)
|
||||
|
||||
raw: (boolean) output preprocessed JSON if True
|
||||
raw: (boolean) output preprocessed JSON if `True`
|
||||
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
quiet: (boolean) suppress warning messages if `True`
|
||||
|
||||
ignore_exceptions: (boolean) ignore parsing exceptions if True
|
||||
ignore_exceptions: (boolean) ignore parsing exceptions if `True`
|
||||
(streaming parsers only)
|
||||
|
||||
Returns:
|
||||
@@ -209,7 +209,7 @@ Parameters:
|
||||
variants of the module name as well
|
||||
as a parser module object.
|
||||
|
||||
documentation: (boolean) include parser docstring if True
|
||||
documentation: (boolean) include parser docstring if `True`
|
||||
|
||||
<a id="jc.lib.parser_mod_list"></a>
|
||||
|
||||
|
||||
117
docs/parsers/amixer.md
Normal file
117
docs/parsers/amixer.md
Normal file
@@ -0,0 +1,117 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.amixer"></a>
|
||||
|
||||
# jc.parsers.amixer
|
||||
|
||||
jc - JSON Convert `amixer sget` command output parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ amixer sget <control_name> | jc --amixer
|
||||
$ amixer sget Master | jc --amixer
|
||||
$ amixer sget Capture | jc --amixer
|
||||
$ amixer sget Speakers | jc --amixer
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('amixer', <amixer_sget_command_output>)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"control_name": string,
|
||||
"capabilities": [
|
||||
string
|
||||
],
|
||||
"playback_channels": [
|
||||
string
|
||||
],
|
||||
"limits": {
|
||||
"playback_min": integer,
|
||||
"playback_max": integer
|
||||
},
|
||||
"mono": {
|
||||
"playback_value": integer,
|
||||
"percentage": integer,
|
||||
"db": float,
|
||||
"status": boolean
|
||||
}
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ amixer sget Master | jc --amixer -p
|
||||
{
|
||||
"control_name": "Capture",
|
||||
"capabilities": [
|
||||
"cvolume",
|
||||
"cswitch"
|
||||
],
|
||||
"playback_channels": [],
|
||||
"limits": {
|
||||
"playback_min": 0,
|
||||
"playback_max": 63
|
||||
},
|
||||
"front_left": {
|
||||
"playback_value": 63,
|
||||
"percentage": 100,
|
||||
"db": 30.0,
|
||||
"status": true
|
||||
},
|
||||
"front_right": {
|
||||
"playback_value": 63,
|
||||
"percentage": 100,
|
||||
"db": 30.0,
|
||||
"status": true
|
||||
}
|
||||
}
|
||||
|
||||
$ amixer sget Master | jc --amixer -p -r
|
||||
{
|
||||
"control_name": "Master",
|
||||
"capabilities": [
|
||||
"pvolume",
|
||||
"pvolume-joined",
|
||||
"pswitch",
|
||||
"pswitch-joined"
|
||||
],
|
||||
"playback_channels": [
|
||||
"Mono"
|
||||
],
|
||||
"limits": {
|
||||
"playback_min": "0",
|
||||
"playback_max": "87"
|
||||
},
|
||||
"mono": {
|
||||
"playback_value": "87",
|
||||
"percentage": "100%",
|
||||
"db": "0.00db",
|
||||
"status": "on"
|
||||
}
|
||||
}
|
||||
|
||||
<a id="jc.parsers.amixer.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: str, raw: bool = False, quiet: bool = False) -> Dict
|
||||
```
|
||||
|
||||
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:
|
||||
Dictionary. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Source: [`jc/parsers/amixer.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/amixer.py)
|
||||
|
||||
Version 1.0 by Eden Refael (edenraf@hotmail.com)
|
||||
@@ -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,
|
||||
@@ -67,6 +69,7 @@ a controller and a device but there might be fields corresponding to one entity.
|
||||
"blocked": string,
|
||||
"connected": string,
|
||||
"legacy_pairing": string,
|
||||
"cable_pairing": string,
|
||||
"rssi": int,
|
||||
"txpower": int,
|
||||
"uuids": array,
|
||||
@@ -134,4 +137,4 @@ Compatibility: linux
|
||||
|
||||
Source: [`jc/parsers/bluetoothctl.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/bluetoothctl.py)
|
||||
|
||||
Version 1.2 by Jake Ob (iakopap at gmail.com)
|
||||
Version 1.5 by Jake Ob (iakopap at gmail.com)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
461
docs/parsers/ipconfig.md
Normal 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)
|
||||
@@ -25,6 +25,9 @@ Schema:
|
||||
[
|
||||
{
|
||||
"chain": string,
|
||||
"default_policy": string,
|
||||
"default_packets": integer,
|
||||
"default_bytes": integer,
|
||||
"rules": [
|
||||
{
|
||||
"num" integer,
|
||||
@@ -49,6 +52,9 @@ Examples:
|
||||
[
|
||||
{
|
||||
"chain": "PREROUTING",
|
||||
"default_policy": "DROP",
|
||||
"default_packets": 0,
|
||||
"default_bytes": 0,
|
||||
"rules": [
|
||||
{
|
||||
"num": 1,
|
||||
@@ -108,6 +114,9 @@ Examples:
|
||||
[
|
||||
{
|
||||
"chain": "PREROUTING",
|
||||
"default_policy": "DROP",
|
||||
"default_packets": "0",
|
||||
"default_bytes": "0",
|
||||
"rules": [
|
||||
{
|
||||
"num": "1",
|
||||
@@ -188,4 +197,4 @@ Compatibility: linux
|
||||
|
||||
Source: [`jc/parsers/iptables.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/iptables.py)
|
||||
|
||||
Version 1.11 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.12 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -22,46 +22,53 @@ Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"name": string,
|
||||
"maj_min": string,
|
||||
"rm": boolean,
|
||||
"size": string,
|
||||
"ro": boolean,
|
||||
"type": string,
|
||||
"mountpoint": string,
|
||||
"kname": string,
|
||||
"fstype": string,
|
||||
"label": string,
|
||||
"uuid": string,
|
||||
"partlabel": string,
|
||||
"partuuid": string,
|
||||
"ra": integer,
|
||||
"model": string,
|
||||
"serial": string,
|
||||
"state": string,
|
||||
"owner": string,
|
||||
"group": string,
|
||||
"mode": string,
|
||||
"alignment": integer,
|
||||
"min_io": integer,
|
||||
"opt_io": integer,
|
||||
"phy_sec": integer,
|
||||
"log_sec": integer,
|
||||
"rota": boolean,
|
||||
"sched": string,
|
||||
"rq_size": integer,
|
||||
"disc_aln": integer,
|
||||
"disc_gran": string,
|
||||
"disc_max": string,
|
||||
"disc_zero": boolean,
|
||||
"wsame": string,
|
||||
"wwn": string,
|
||||
"rand": boolean,
|
||||
"pkname": string,
|
||||
"hctl": string,
|
||||
"tran": string,
|
||||
"rev": string,
|
||||
"vendor": string
|
||||
"name": string,
|
||||
"maj_min": string,
|
||||
"rm": boolean,
|
||||
"size": string,
|
||||
"size_bytes": integer
|
||||
"ro": boolean,
|
||||
"type": string,
|
||||
"mountpoint": string,
|
||||
"mountpoints": [
|
||||
string
|
||||
],
|
||||
"kname": string,
|
||||
"fstype": string,
|
||||
"label": string,
|
||||
"uuid": string,
|
||||
"partlabel": string,
|
||||
"partuuid": string,
|
||||
"ra": integer,
|
||||
"model": string,
|
||||
"serial": string,
|
||||
"state": string,
|
||||
"owner": string,
|
||||
"group": string,
|
||||
"mode": string,
|
||||
"alignment": integer,
|
||||
"min_io": integer,
|
||||
"opt_io": integer,
|
||||
"phy_sec": integer,
|
||||
"log_sec": integer,
|
||||
"rota": boolean,
|
||||
"sched": string,
|
||||
"rq_size": integer,
|
||||
"disc_aln": integer,
|
||||
"disc_gran": string,
|
||||
"disc_gran_bytes": integer,
|
||||
"disc_max": string,
|
||||
"disc_max_bytes": integer,
|
||||
"disc_zero": boolean,
|
||||
"wsame": string,
|
||||
"wsame_bytes": integer,
|
||||
"wwn": string,
|
||||
"rand": boolean,
|
||||
"pkname": string,
|
||||
"hctl": string,
|
||||
"tran": string,
|
||||
"rev": string,
|
||||
"vendor": string
|
||||
}
|
||||
]
|
||||
|
||||
@@ -74,6 +81,7 @@ Examples:
|
||||
"maj_min": "8:0",
|
||||
"rm": false,
|
||||
"size": "20G",
|
||||
"size_bytes": 20000000000,
|
||||
"ro": false,
|
||||
"type": "disk",
|
||||
"mountpoint": null
|
||||
@@ -83,6 +91,7 @@ Examples:
|
||||
"maj_min": "8:1",
|
||||
"rm": false,
|
||||
"size": "1G",
|
||||
"size_bytes": 1000000000
|
||||
"ro": false,
|
||||
"type": "part",
|
||||
"mountpoint": "/boot"
|
||||
@@ -100,6 +109,7 @@ Examples:
|
||||
"maj_min": "8:0",
|
||||
"rm": false,
|
||||
"size": "20G",
|
||||
"size_bytes": 20000000000,
|
||||
"ro": false,
|
||||
"type": "disk",
|
||||
"mountpoint": null,
|
||||
@@ -126,9 +136,12 @@ Examples:
|
||||
"rq_size": 128,
|
||||
"disc_aln": 0,
|
||||
"disc_gran": "0B",
|
||||
"disc_gran_bytes": 0,
|
||||
"disc_max": "0B",
|
||||
"disc_max_bytes": 0,
|
||||
"disc_zero": false,
|
||||
"wsame": "32M",
|
||||
"wsame_bytes": 32000000,
|
||||
"wwn": null,
|
||||
"rand": true,
|
||||
"pkname": null,
|
||||
@@ -142,6 +155,7 @@ Examples:
|
||||
"maj_min": "8:1",
|
||||
"rm": false,
|
||||
"size": "1G",
|
||||
"size_bytes": 1000000000
|
||||
"ro": false,
|
||||
"type": "part",
|
||||
"mountpoint": "/boot",
|
||||
@@ -168,9 +182,12 @@ Examples:
|
||||
"rq_size": 128,
|
||||
"disc_aln": 0,
|
||||
"disc_gran": "0B",
|
||||
"disc_gran_bytes": 0,
|
||||
"disc_max": "0B",
|
||||
"disc_max_bytes": 0,
|
||||
"disc_zero": false,
|
||||
"wsame": "32M",
|
||||
"wsame_bytes": 32000000,
|
||||
"wwn": null,
|
||||
"rand": true,
|
||||
"pkname": "sda",
|
||||
@@ -192,6 +209,7 @@ Examples:
|
||||
"maj_min": "8:0",
|
||||
"rm": "0",
|
||||
"size": "20G",
|
||||
"size_bytes": 20000000000,
|
||||
"ro": "0",
|
||||
"type": "disk",
|
||||
"mountpoint": null,
|
||||
@@ -218,9 +236,12 @@ Examples:
|
||||
"rq_size": "128",
|
||||
"disc_aln": "0",
|
||||
"disc_gran": "0B",
|
||||
"disc_gran_bytes": 0,
|
||||
"disc_max": "0B",
|
||||
"disc_max_bytes": 0,
|
||||
"disc_zero": "0",
|
||||
"wsame": "32M",
|
||||
"wsame_bytes": 32000000,
|
||||
"wwn": null,
|
||||
"rand": "1",
|
||||
"pkname": null,
|
||||
@@ -234,6 +255,7 @@ Examples:
|
||||
"maj_min": "8:1",
|
||||
"rm": "0",
|
||||
"size": "1G",
|
||||
"size_bytes": 1000000000
|
||||
"ro": "0",
|
||||
"type": "part",
|
||||
"mountpoint": "/boot",
|
||||
@@ -260,9 +282,12 @@ Examples:
|
||||
"rq_size": "128",
|
||||
"disc_aln": "0",
|
||||
"disc_gran": "0B",
|
||||
"disc_gran_bytes": 0,
|
||||
"disc_max": "0B",
|
||||
"disc_max_bytes": 0,
|
||||
"disc_zero": "0",
|
||||
"wsame": "32M",
|
||||
"wsame_bytes": 32000000,
|
||||
"wwn": null,
|
||||
"rand": "1",
|
||||
"pkname": "sda",
|
||||
@@ -299,4 +324,4 @@ Compatibility: linux
|
||||
|
||||
Source: [`jc/parsers/lsblk.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/lsblk.py)
|
||||
|
||||
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.10 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -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.11 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
81
docs/parsers/net_localgroup.md
Normal file
81
docs/parsers/net_localgroup.md
Normal file
@@ -0,0 +1,81 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.net_localgroup"></a>
|
||||
|
||||
# jc.parsers.net_localgroup
|
||||
|
||||
jc - JSON Convert `net localgroup` command output parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ net localgroup | jc --net-localgroup
|
||||
$ net localgroup /domain | jc --net-localgroup
|
||||
$ net localgroup Administrators | jc --net-localgroup
|
||||
$ net localgroup Administrators /domain | jc --net-localgroup
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('net_localgroup', net_localgroup_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"account_origin": string,
|
||||
"domain": string,
|
||||
"comment": string,
|
||||
"groups": [
|
||||
{
|
||||
"name": string
|
||||
"members": [
|
||||
string
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ net localgroup | jc --net-localgroup -p
|
||||
{
|
||||
"account_origin": null,
|
||||
"comment": null,
|
||||
"domain": null,
|
||||
"groups": [
|
||||
{
|
||||
"name": "Administrators",
|
||||
"members": [
|
||||
"Administrator",
|
||||
"Operator",
|
||||
"ansible",
|
||||
"user1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
<a id="jc.parsers.net_localgroup.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: win32
|
||||
|
||||
Source: [`jc/parsers/net_localgroup.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/net_localgroup.py)
|
||||
|
||||
Version 1.0 by joehacksalot (joehacksalot@gmail.com)
|
||||
212
docs/parsers/net_user.md
Normal file
212
docs/parsers/net_user.md
Normal file
@@ -0,0 +1,212 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.net_user"></a>
|
||||
|
||||
# jc.parsers.net_user
|
||||
|
||||
jc - JSON Convert `net user` command output parser
|
||||
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ net users | jc --net-user
|
||||
$ net users /domain | jc --net-user
|
||||
$ net users User1 | jc --net-user
|
||||
$ net users User1 /domain | jc --net-user
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('net_user', net_user_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"domain": string,
|
||||
"account_origin": string,
|
||||
"user_accounts": [
|
||||
{
|
||||
"user_name": string,
|
||||
"full_name": string,
|
||||
"comment": string,
|
||||
"user_comment": string,
|
||||
"country_region_code": string,
|
||||
"account_active": boolean,
|
||||
"account_expires": string,
|
||||
"password_last_set": string,
|
||||
"password_expires": string,
|
||||
"password_changeable": string,
|
||||
"password_required": boolean,
|
||||
"user_may_change_password": boolean,
|
||||
"workstations_allowed": string,
|
||||
"logon_script": string,
|
||||
"user_profile": string,
|
||||
"home_directory": string,
|
||||
"last_logon": string,
|
||||
"logon_hours_allowed": string,
|
||||
"local_group_memberships": [
|
||||
string,
|
||||
],
|
||||
"global_group_memberships": [
|
||||
string,
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
$ net users | jc --net-user -p
|
||||
{
|
||||
"account_origin": "\\\\WIN-SERVER16",
|
||||
"domain": "",
|
||||
"user_accounts": [
|
||||
{
|
||||
"user_name": "Administrator"
|
||||
},
|
||||
{
|
||||
"user_name": "DefaultAccount"
|
||||
},
|
||||
{
|
||||
"user_name": "Guest"
|
||||
},
|
||||
{
|
||||
"user_name": "pentera_BnlLQVnd7p"
|
||||
},
|
||||
{
|
||||
"user_name": "user1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ net users /domain | jc --net-user -p
|
||||
{
|
||||
"account_origin": "\\\\DESKTOP-WIN10-PRO.somecompany.corp",
|
||||
"domain": "somecompany.corp",
|
||||
"user_accounts": [
|
||||
{
|
||||
"user_name": "aaron"
|
||||
},
|
||||
{
|
||||
"user_name": "addison"
|
||||
},
|
||||
{
|
||||
"user_name": "Administrator"
|
||||
},
|
||||
{
|
||||
"user_name": "ansible"
|
||||
},
|
||||
{
|
||||
"user_name": "da"
|
||||
},
|
||||
{
|
||||
"user_name": "DefaultAccount"
|
||||
},
|
||||
{
|
||||
"user_name": "Guest"
|
||||
},
|
||||
{
|
||||
"user_name": "harrison"
|
||||
},
|
||||
{
|
||||
"user_name": "james"
|
||||
},
|
||||
{
|
||||
"user_name": "krbtgt"
|
||||
},
|
||||
{
|
||||
"user_name": "liam"
|
||||
},
|
||||
{
|
||||
"user_name": "localadmin"
|
||||
},
|
||||
{
|
||||
"user_name": "tiffany"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ net users Administrator | jc --net-user -p
|
||||
{
|
||||
"domain": "",
|
||||
"user_accounts": [
|
||||
{
|
||||
"account_active": true,
|
||||
"account_expires": "Never",
|
||||
"comment": "Built-in account for administering the computer/domain",
|
||||
"country_region_code": "000 (System Default)",
|
||||
"global_group_memberships": [],
|
||||
"last_logon": "2024-08-23T13:47:11",
|
||||
"local_group_memberships": [
|
||||
"Administrators"
|
||||
],
|
||||
"logon_hours_allowed": "All",
|
||||
"password_changeable": "2021-12-17T11:07:14",
|
||||
"password_expires": "2022-01-27T11:07:14",
|
||||
"password_last_set": "2021-12-16T11:07:14",
|
||||
"password_required": true,
|
||||
"user_may_change_password": true,
|
||||
"user_name": "Administrators",
|
||||
"workstations_allowed": "All"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ net users Administrator /domain | jc --net-user -p | jq
|
||||
{
|
||||
"domain": "somecompany.corp",
|
||||
"user_accounts": [
|
||||
{
|
||||
"account_active": true,
|
||||
"account_expires": "Never",
|
||||
"comment": "Built-in account for administering the computer/domain",
|
||||
"country_region_code": "000 (System Default)",
|
||||
"global_group_memberships": [
|
||||
"Domain Admins",
|
||||
"Domain Users",
|
||||
"Group Policy Creator",
|
||||
"Enterprise Admins",
|
||||
"Schema Admins"
|
||||
],
|
||||
"last_logon": "2024-07-17T13:46:12",
|
||||
"local_group_memberships": [
|
||||
"Administrators"
|
||||
],
|
||||
"logon_hours_allowed": "All",
|
||||
"password_changeable": "2023-09-30T11:44:26",
|
||||
"password_expires": "Never",
|
||||
"password_last_set": "2023-09-29T11:44:26",
|
||||
"password_required": true,
|
||||
"user_may_change_password": true,
|
||||
"user_name": "Administrators",
|
||||
"workstations_allowed": "All"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
<a id="jc.parsers.net_user.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: win32
|
||||
|
||||
Source: [`jc/parsers/net_user.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/net_user.py)
|
||||
|
||||
Version 1.0 by joehacksalot (joehacksalot@gmail.com)
|
||||
@@ -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)
|
||||
|
||||
@@ -41,6 +41,8 @@ These are documented below.
|
||||
[
|
||||
{
|
||||
"<key>": string/integer/float, # [0]
|
||||
"team_config": object/null,
|
||||
"team_port_config": object/null,
|
||||
"dhcp4_option_x": {
|
||||
"name": string,
|
||||
"value": string/integer/float,
|
||||
@@ -170,4 +172,4 @@ Compatibility: linux
|
||||
|
||||
Source: [`jc/parsers/nmcli.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/nmcli.py)
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -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
208
docs/parsers/pacman.md
Normal 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.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
@@ -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)
|
||||
|
||||
@@ -31,7 +31,7 @@ or
|
||||
|
||||
or
|
||||
|
||||
$ cat /proc/meminfo | jc --proc-memifno
|
||||
$ cat /proc/meminfo | jc --proc-meminfo
|
||||
|
||||
Usage (module):
|
||||
|
||||
|
||||
204
docs/parsers/route_print.md
Normal file
204
docs/parsers/route_print.md
Normal file
@@ -0,0 +1,204 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.route_print"></a>
|
||||
|
||||
# jc.parsers.route_print
|
||||
|
||||
jc - JSON Convert `route print` command output parser
|
||||
|
||||
See also: the `route` command parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ route print | jc --route-print
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('route_print', route_print_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"interface_list": [
|
||||
{
|
||||
"interface_index": integer,
|
||||
"mac_address": string,
|
||||
"description": string
|
||||
}
|
||||
],
|
||||
"ipv4_route_table": {
|
||||
"active_routes": [
|
||||
{
|
||||
"network_destination": string,
|
||||
"netmask": string,
|
||||
"gateway": string,
|
||||
"interface": string,
|
||||
"metric": integer, # [0]
|
||||
"metric_set_to_default": boolean # [1]
|
||||
}
|
||||
],
|
||||
"persistent_routes": [
|
||||
{
|
||||
"network_address": string,
|
||||
"netmask": string,
|
||||
"gateway_address": string,
|
||||
"metric": integer # [0]
|
||||
"metric_set_to_default": boolean # [1]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ipv6_route_table": {
|
||||
"active_routes": [
|
||||
{
|
||||
"interface": integer,
|
||||
"metric": integer, # [0]
|
||||
"metric_set_to_default": boolean, # [1]
|
||||
"network_destination": string,
|
||||
"gateway": string
|
||||
}
|
||||
],
|
||||
"persistent_routes": [
|
||||
{
|
||||
"interface": integer,
|
||||
"metric": integer, # [0]
|
||||
"metric_set_to_default": boolean, # [1]
|
||||
"network_destination": string,
|
||||
"gateway": string
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
[0] Null/None if "metric" = "Default"
|
||||
[1] True if "metric" = "Default"
|
||||
|
||||
Examples:
|
||||
|
||||
$ route print | jc --route-print -p
|
||||
{
|
||||
"interface_list": [
|
||||
{
|
||||
"interface_index": 28,
|
||||
"mac_address": null,
|
||||
"description": "Tailscale Tunnel"
|
||||
},
|
||||
{
|
||||
"interface_index": 12,
|
||||
"mac_address": "00:1c:42:da:01:6a",
|
||||
"description": "Parallels VirtIO Ethernet Adapter"
|
||||
},
|
||||
{
|
||||
"interface_index": 1,
|
||||
"mac_address": null,
|
||||
"description": "Software Loopback Interface 1"
|
||||
}
|
||||
],
|
||||
"ipv4_route_table": {
|
||||
"active_routes": [
|
||||
{
|
||||
"network_destination": "0.0.0.0",
|
||||
"netmask": "0.0.0.0",
|
||||
"gateway": "10.211.55.1",
|
||||
"interface": "10.211.55.3",
|
||||
"metric": 15,
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
{
|
||||
"network_destination": "10.0.0.0",
|
||||
"netmask": "255.0.0.0",
|
||||
"gateway": "192.168.22.1",
|
||||
"interface": "10.211.55.3",
|
||||
"metric": 16,
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
...
|
||||
{
|
||||
"network_destination": "255.255.255.255",
|
||||
"netmask": "255.255.255.255",
|
||||
"gateway": "On-link",
|
||||
"interface": "10.211.55.3",
|
||||
"metric": null,
|
||||
"metric_set_to_default": true
|
||||
}
|
||||
],
|
||||
"persistent_routes": [
|
||||
{
|
||||
"network_address": "10.0.1.0",
|
||||
"netmask": "255.255.255.0",
|
||||
"gateway_address": "192.168.22.1",
|
||||
"metric": 1,
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
{
|
||||
"network_address": "10.0.3.0",
|
||||
"netmask": "255.255.255.0",
|
||||
"gateway_address": "192.168.22.1",
|
||||
"metric": 1,
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
...
|
||||
]
|
||||
},
|
||||
"ipv6_route_table": {
|
||||
"active_routes": [
|
||||
{
|
||||
"interface": 1,
|
||||
"metric": 331,
|
||||
"network_destination": "::1/128",
|
||||
"gateway": "On-link",
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
{
|
||||
"interface": 12,
|
||||
"metric": 271,
|
||||
"network_destination": "2001:db8::/64",
|
||||
"gateway": "fe80::1",
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
...
|
||||
{
|
||||
"interface": 12,
|
||||
"metric": 271,
|
||||
"network_destination": "ff00::/8",
|
||||
"gateway": "On-link",
|
||||
"metric_set_to_default": false
|
||||
}
|
||||
],
|
||||
"persistent_routes": [
|
||||
{
|
||||
"interface": 0,
|
||||
"metric": 4294967295,
|
||||
"network_destination": "2001:db8::/64",
|
||||
"gateway": "fe80::1",
|
||||
"metric_set_to_default": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
<a id="jc.parsers.route_print.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: win32
|
||||
|
||||
Source: [`jc/parsers/route_print.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/route_print.py)
|
||||
|
||||
Version 1.0 by joehacksalot (joehacksalot@gmail.com)
|
||||
@@ -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)
|
||||
|
||||
@@ -157,4 +157,4 @@ Compatibility: linux, darwin, cygwin, aix, freebsd
|
||||
|
||||
Source: [`jc/parsers/time.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/time.py)
|
||||
|
||||
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.5 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -50,23 +50,31 @@ All `-` values are converted to `null`
|
||||
"cpu_hardware": float,
|
||||
"cpu_software": float,
|
||||
"cpu_steal": float,
|
||||
"mem_total": float, # [0]
|
||||
"mem_free": float, # [0]
|
||||
"mem_used": float, # [0]
|
||||
"mem_buff_cache": float, # [0]
|
||||
"swap_total": float, # [0]
|
||||
"swap_free": float, # [0]
|
||||
"swap_used": float, # [0]
|
||||
"mem_available": float, # [0]
|
||||
"mem_unit": string,
|
||||
"mem_total": float,
|
||||
"mem_free": float,
|
||||
"mem_used": float,
|
||||
"mem_buff_cache": float,
|
||||
"swap_unit": string,
|
||||
"swap_total": float,
|
||||
"swap_free": float,
|
||||
"swap_used": float,
|
||||
"mem_available": float,
|
||||
"processes": [
|
||||
{
|
||||
"pid": integer,
|
||||
"user": string,
|
||||
"priority": integer,
|
||||
"nice": integer,
|
||||
"virtual_mem": float, # [1]
|
||||
"resident_mem": float, # [1]
|
||||
"shared_mem": float, # [1]
|
||||
"virtual_mem": float,
|
||||
"virtual_mem_bytes": integer,
|
||||
"virtual_mem_unit": string,
|
||||
"resident_mem": float,
|
||||
"resident_mem_bytes": integer,
|
||||
"resident_mem_unit": string,
|
||||
"shared_mem": float,
|
||||
"shared_mem_bytes": integer,
|
||||
"shared_mem_unit": string,
|
||||
"status": string,
|
||||
"percent_cpu": float,
|
||||
"percent_mem": float,
|
||||
@@ -87,9 +95,15 @@ All `-` values are converted to `null`
|
||||
"thread_count": integer,
|
||||
"last_used_processor": integer,
|
||||
"time": string,
|
||||
"swap": float, # [1]
|
||||
"code": float, # [1]
|
||||
"data": float, # [1]
|
||||
"swap": float,
|
||||
"swap_bytes": integer,
|
||||
"swap_unit": string,
|
||||
"code": float,
|
||||
"code_bytes": integer,
|
||||
"code_unit": string,
|
||||
"data": float,
|
||||
"data_bytes": integer,
|
||||
"data_unit": string,
|
||||
"major_page_fault_count": integer,
|
||||
"minor_page_fault_count": integer,
|
||||
"dirty_pages_count": integer,
|
||||
@@ -108,7 +122,9 @@ All `-` values are converted to `null`
|
||||
]
|
||||
"major_page_fault_count_delta": integer,
|
||||
"minor_page_fault_count_delta": integer,
|
||||
"used": float, # [1]
|
||||
"used": float,
|
||||
"used_bytes": integer,
|
||||
"used_unit": string,
|
||||
"ipc_namespace_inode": integer,
|
||||
"mount_namespace_inode": integer,
|
||||
"net_namespace_inode": integer,
|
||||
@@ -129,9 +145,6 @@ All `-` values are converted to `null`
|
||||
}
|
||||
]
|
||||
|
||||
[0] Values are in the units output by `top`
|
||||
[1] Unit suffix stripped during float conversion
|
||||
|
||||
Examples:
|
||||
|
||||
$ top -b -n 3 | jc --top -p
|
||||
@@ -338,4 +351,4 @@ Compatibility: linux
|
||||
|
||||
Source: [`jc/parsers/top.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/top.py)
|
||||
|
||||
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -48,23 +48,39 @@ Schema:
|
||||
"cpu_hardware": float,
|
||||
"cpu_software": float,
|
||||
"cpu_steal": float,
|
||||
"mem_total": float, # [0]
|
||||
"mem_free": float, # [0]
|
||||
"mem_used": float, # [0]
|
||||
"mem_buff_cache": float, # [0]
|
||||
"swap_total": float, # [0]
|
||||
"swap_free": float, # [0]
|
||||
"swap_used": float, # [0]
|
||||
"mem_available": float, # [0]
|
||||
"mem_unit": string,
|
||||
"swap_unit": string,
|
||||
"mem_total": float,
|
||||
"mem_total_bytes": integer,
|
||||
"mem_free": float,
|
||||
"mem_free_bytes": integer,
|
||||
"mem_used": float,
|
||||
"mem_used_bytes": integer,
|
||||
"mem_buff_cache": float,
|
||||
"mem_buff_cache_bytes": integer,
|
||||
"swap_total": float,
|
||||
"swap_total_bytes": integer,
|
||||
"swap_free": float,
|
||||
"swap_free_bytes": integer,
|
||||
"swap_used": float,
|
||||
"swap_used_bytes": integer,
|
||||
"mem_available": float,
|
||||
"mem_available_bytes": integer,
|
||||
"processes": [
|
||||
{
|
||||
"pid": integer,
|
||||
"user": string,
|
||||
"priority": integer,
|
||||
"nice": integer,
|
||||
"virtual_mem": float, # [1]
|
||||
"resident_mem": float, # [1]
|
||||
"shared_mem": float, # [1]
|
||||
"virtual_mem": float,
|
||||
"virtual_mem_unit": string,
|
||||
"virtual_mem_bytes": integer,
|
||||
"resident_mem": float,
|
||||
"resident_mem_unit": string,
|
||||
"resident_mem_bytes": integer,
|
||||
"shared_mem": float,
|
||||
"shared_mem_unit": string,
|
||||
"shared_mem_bytes": integer,
|
||||
"status": string,
|
||||
"percent_cpu": float,
|
||||
"percent_mem": float,
|
||||
@@ -85,9 +101,15 @@ Schema:
|
||||
"thread_count": integer,
|
||||
"last_used_processor": integer,
|
||||
"time": string,
|
||||
"swap": float, # [1]
|
||||
"code": float, # [1]
|
||||
"data": float, # [1]
|
||||
"swap": float,
|
||||
"swap_unit": string,
|
||||
"swap_bytes": integer,
|
||||
"code": float,
|
||||
"code_unit": string,
|
||||
"code_bytes": integer
|
||||
"data": float,
|
||||
"data_unit": string,
|
||||
"data_bytes": integer,
|
||||
"major_page_fault_count": integer,
|
||||
"minor_page_fault_count": integer,
|
||||
"dirty_pages_count": integer,
|
||||
@@ -106,7 +128,9 @@ Schema:
|
||||
]
|
||||
"major_page_fault_count_delta": integer,
|
||||
"minor_page_fault_count_delta": integer,
|
||||
"used": float, # [1]
|
||||
"used": float,
|
||||
"used_unit": string,
|
||||
"used_bytes": integer,
|
||||
"ipc_namespace_inode": integer,
|
||||
"mount_namespace_inode": integer,
|
||||
"net_namespace_inode": integer,
|
||||
@@ -133,9 +157,6 @@ Schema:
|
||||
}
|
||||
}
|
||||
|
||||
[0] Values are in the units output by `top`
|
||||
[1] Unit suffix stripped during float conversion
|
||||
|
||||
Examples:
|
||||
|
||||
$ top -b | jc --top-s
|
||||
@@ -178,4 +199,4 @@ Compatibility: linux
|
||||
|
||||
Source: [`jc/parsers/top_s.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/top_s.py)
|
||||
|
||||
Version 1.2 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -32,6 +32,8 @@ Schema:
|
||||
{
|
||||
"destination_ip": string,
|
||||
"destination_name": string,
|
||||
"max_hops": integer,
|
||||
"data_bytes": integer,
|
||||
"hops": [
|
||||
{
|
||||
"hop": integer,
|
||||
@@ -54,6 +56,8 @@ Examples:
|
||||
{
|
||||
"destination_ip": "216.58.194.46",
|
||||
"destination_name": "google.com",
|
||||
"max_hops": 64,
|
||||
"data_bytes": 50,
|
||||
"hops": [
|
||||
{
|
||||
"hop": 1,
|
||||
@@ -89,6 +93,8 @@ Examples:
|
||||
{
|
||||
"destination_ip": "216.58.194.46",
|
||||
"destination_name": "google.com",
|
||||
"max_hops": "64",
|
||||
"data_bytes": "50",
|
||||
"hops": [
|
||||
{
|
||||
"hop": "1",
|
||||
@@ -145,4 +151,4 @@ Compatibility: linux, darwin, freebsd
|
||||
|
||||
Source: [`jc/parsers/traceroute.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/traceroute.py)
|
||||
|
||||
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
167
docs/parsers/traceroute_s.md
Normal file
167
docs/parsers/traceroute_s.md
Normal file
@@ -0,0 +1,167 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.traceroute_s"></a>
|
||||
|
||||
# jc.parsers.traceroute_s
|
||||
|
||||
jc - JSON Convert `traceroute` command output streaming parser
|
||||
|
||||
> This streaming parser outputs JSON Lines (cli) or returns an Iterable of
|
||||
> Dictionaries (module)
|
||||
|
||||
Supports `traceroute` and `traceroute6` output.
|
||||
|
||||
> Note: On some operating systems you will need to redirect `STDERR` to
|
||||
> `STDOUT` for destination info since the header line is sent to
|
||||
> `STDERR`. A warning message will be printed to `STDERR` if the
|
||||
> header row is not found.
|
||||
>
|
||||
> e.g. `$ traceroute 8.8.8.8 2>&1 | jc --traceroute-s`
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ traceroute 1.2.3.4 | jc --traceroute-s
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('traceroute_s', traceroute_command_output.splitlines())
|
||||
for item in result:
|
||||
# do something
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
# 'header' or 'hop'
|
||||
"type": string,
|
||||
|
||||
# 'header' type has the fields below:
|
||||
"destination_ip": string,
|
||||
"destination_name": string,
|
||||
"max_hops": integer,
|
||||
"data_bytes": integer,
|
||||
|
||||
# 'hop' type has the fields below:
|
||||
"hop": integer,
|
||||
"probes": [
|
||||
{
|
||||
"annotation": string,
|
||||
"asn": integer,
|
||||
"ip": string,
|
||||
"name": string,
|
||||
"rtt": float
|
||||
}
|
||||
]
|
||||
|
||||
# below object only exists if using -qq or ignore_exceptions=True
|
||||
"_jc_meta": {
|
||||
"success": boolean, # false if error parsing
|
||||
"error": string, # exists if "success" is false
|
||||
"line": string # exists if "success" is false
|
||||
}
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ traceroute google.com | jc --traceroute-s -p
|
||||
{
|
||||
"type": "header",
|
||||
"destination_ip": "216.58.194.46",
|
||||
"destination_name": "google.com",
|
||||
"max_hops": 30,
|
||||
"data_bytes": 60
|
||||
}
|
||||
{
|
||||
"type": "hop",
|
||||
"hop": 1,
|
||||
"probes": [
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": 198.574
|
||||
},
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": null
|
||||
},
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": 198.65
|
||||
}
|
||||
]
|
||||
}
|
||||
...
|
||||
|
||||
$ traceroute google.com | jc --traceroute-s -p -r
|
||||
{
|
||||
"type": "header",
|
||||
"destination_ip": "216.58.194.46",
|
||||
"destination_name": "google.com",
|
||||
"max_hops": "30",
|
||||
"data_bytes": "60"
|
||||
}
|
||||
{
|
||||
"type": "hop",
|
||||
"hop": "1",
|
||||
"probes": [
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": "198.574"
|
||||
},
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": null
|
||||
},
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": "198.650"
|
||||
}
|
||||
]
|
||||
}
|
||||
...
|
||||
|
||||
<a id="jc.parsers.traceroute_s.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data, raw=False, quiet=False, ignore_exceptions=False)
|
||||
```
|
||||
|
||||
Main text parsing function. Returns an iterable object.
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (iterable) line-based text data to parse
|
||||
(e.g. sys.stdin or str.splitlines())
|
||||
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
ignore_exceptions: (boolean) ignore parsing exceptions if True
|
||||
|
||||
Returns:
|
||||
|
||||
Iterable of Dictionaries
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
|
||||
Source: [`jc/parsers/traceroute_s.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/traceroute_s.py)
|
||||
|
||||
Version 1.0 by Shintaro Kojima (goodies@codeout.net)
|
||||
@@ -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
172
docs/parsers/wg_show.md
Normal 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)
|
||||
@@ -30,6 +30,7 @@ Schema:
|
||||
"user": string,
|
||||
"event": string,
|
||||
"writeable_tty": string,
|
||||
"process": string,
|
||||
"tty": string,
|
||||
"time": string,
|
||||
"epoch": integer, # [0]
|
||||
@@ -160,4 +161,4 @@ Compatibility: linux, darwin, cygwin, aix, freebsd
|
||||
|
||||
Source: [`jc/parsers/who.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/who.py)
|
||||
|
||||
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.9 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
@@ -435,4 +435,4 @@ Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Source: [`jc/parsers/x509_cert.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/x509_cert.py)
|
||||
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
||||
304
docs/parsers/x509_crl.md
Normal file
304
docs/parsers/x509_crl.md
Normal file
@@ -0,0 +1,304 @@
|
||||
[Home](https://kellyjonbrazil.github.io/jc/)
|
||||
<a id="jc.parsers.x509_crl"></a>
|
||||
|
||||
# jc.parsers.x509_crl
|
||||
|
||||
jc - JSON Convert X.509 Certificate Revocation List format file parser
|
||||
|
||||
This parser will convert DER and PEM encoded X.509 certificate revocation
|
||||
list files.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat certificateRevocationList.pem | jc --x509-crl
|
||||
$ cat certificateRevocationList.der | jc --x509-crl
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('x509_crl', x509_crl_file_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"tbs_cert_list": {
|
||||
"version": string,
|
||||
"signature": {
|
||||
"algorithm": string,
|
||||
"parameters": string/null
|
||||
},
|
||||
"issuer": {
|
||||
"organization_name": string,
|
||||
"organizational_unit_name": string,
|
||||
"common_name": string
|
||||
},
|
||||
"this_update": integer, # [1]
|
||||
"next_update": integer, # [1]
|
||||
"revoked_certificates": [
|
||||
{
|
||||
"user_certificate": integer,
|
||||
"revocation_date": integer, # [1]
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": string,
|
||||
"critical": boolean,
|
||||
"extn_value": string,
|
||||
"extn_value_iso": string
|
||||
},
|
||||
"revocation_date_iso": string
|
||||
}
|
||||
],
|
||||
"crl_extensions": [
|
||||
{
|
||||
"extn_id": string,
|
||||
"critical": boolean,
|
||||
"extn_value": array/object/string/integer # [2]
|
||||
}
|
||||
],
|
||||
"this_update_iso": string,
|
||||
"next_update_iso": string
|
||||
},
|
||||
"signature_algorithm": {
|
||||
"algorithm": string,
|
||||
"parameters": string/null
|
||||
},
|
||||
"signature": string # [0]
|
||||
}
|
||||
|
||||
[0] in colon-delimited hex notation
|
||||
[1] time-zone-aware (UTC) epoch timestamp
|
||||
[2] See below for well-known Extension schemas:
|
||||
|
||||
Basic Constraints:
|
||||
{
|
||||
"extn_id": "basic_constraints",
|
||||
"critical": boolean,
|
||||
"extn_value": {
|
||||
"ca": boolean,
|
||||
"path_len_constraint": string/null
|
||||
}
|
||||
}
|
||||
|
||||
Key Usage:
|
||||
{
|
||||
"extn_id": "key_usage",
|
||||
"critical": boolean,
|
||||
"extn_value": [
|
||||
string
|
||||
]
|
||||
}
|
||||
|
||||
Key Identifier:
|
||||
{
|
||||
"extn_id": "key_identifier",
|
||||
"critical": boolean,
|
||||
"extn_value": string # [0]
|
||||
}
|
||||
|
||||
Authority Key Identifier:
|
||||
{
|
||||
"extn_id": "authority_key_identifier",
|
||||
"critical": boolean,
|
||||
"extn_value": {
|
||||
"key_identifier": string, # [0]
|
||||
"authority_cert_issuer": string/null,
|
||||
"authority_cert_serial_number": string/null
|
||||
}
|
||||
}
|
||||
|
||||
Subject Alternative Name:
|
||||
{
|
||||
"extn_id": "subject_alt_name",
|
||||
"critical": boolean,
|
||||
"extn_value": [
|
||||
string
|
||||
]
|
||||
}
|
||||
|
||||
Certificate Policies:
|
||||
{
|
||||
"extn_id": "certificate_policies",
|
||||
"critical": boolean,
|
||||
"extn_value": [
|
||||
{
|
||||
"policy_identifier": string,
|
||||
"policy_qualifiers": [ array or null
|
||||
{
|
||||
"policy_qualifier_id": string,
|
||||
"qualifier": string
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Signed Certificate Timestamp List:
|
||||
{
|
||||
"extn_id": "signed_certificate_timestamp_list",
|
||||
"critical": boolean,
|
||||
"extn_value": string # [0]
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat sample-crl.pem | jc --x509-crl -p
|
||||
{
|
||||
"tbs_cert_list": {
|
||||
"version": "v2",
|
||||
"signature": {
|
||||
"algorithm": "sha1_rsa",
|
||||
"parameters": null
|
||||
},
|
||||
"issuer": {
|
||||
"organization_name": "Sample Signer Organization",
|
||||
"organizational_unit_name": "Sample Signer Unit",
|
||||
"common_name": "Sample Signer Cert"
|
||||
},
|
||||
"this_update": 1361183520,
|
||||
"next_update": 1361184120,
|
||||
"revoked_certificates": [
|
||||
{
|
||||
"user_certificate": 1341767,
|
||||
"revocation_date": 1361182932,
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": "crl_reason",
|
||||
"critical": false,
|
||||
"extn_value": "affiliation_changed"
|
||||
},
|
||||
{
|
||||
"extn_id": "invalidity_date",
|
||||
"critical": false,
|
||||
"extn_value": 1361182920,
|
||||
"extn_value_iso": "2013-02-18T10:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revocation_date_iso": "2013-02-18T10:22:12+00:00"
|
||||
},
|
||||
{
|
||||
"user_certificate": 1341768,
|
||||
"revocation_date": 1361182942,
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": "crl_reason",
|
||||
"critical": false,
|
||||
"extn_value": "certificate_hold"
|
||||
},
|
||||
{
|
||||
"extn_id": "invalidity_date",
|
||||
"critical": false,
|
||||
"extn_value": 1361182920,
|
||||
"extn_value_iso": "2013-02-18T10:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revocation_date_iso": "2013-02-18T10:22:22+00:00"
|
||||
},
|
||||
{
|
||||
"user_certificate": 1341769,
|
||||
"revocation_date": 1361182952,
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": "crl_reason",
|
||||
"critical": false,
|
||||
"extn_value": "superseded"
|
||||
},
|
||||
{
|
||||
"extn_id": "invalidity_date",
|
||||
"critical": false,
|
||||
"extn_value": 1361182920,
|
||||
"extn_value_iso": "2013-02-18T10:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revocation_date_iso": "2013-02-18T10:22:32+00:00"
|
||||
},
|
||||
{
|
||||
"user_certificate": 1341770,
|
||||
"revocation_date": 1361182962,
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": "crl_reason",
|
||||
"critical": false,
|
||||
"extn_value": "key_compromise"
|
||||
},
|
||||
{
|
||||
"extn_id": "invalidity_date",
|
||||
"critical": false,
|
||||
"extn_value": 1361182920,
|
||||
"extn_value_iso": "2013-02-18T10:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revocation_date_iso": "2013-02-18T10:22:42+00:00"
|
||||
},
|
||||
{
|
||||
"user_certificate": 1341771,
|
||||
"revocation_date": 1361182971,
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": "crl_reason",
|
||||
"critical": false,
|
||||
"extn_value": "cessation_of_operation"
|
||||
},
|
||||
{
|
||||
"extn_id": "invalidity_date",
|
||||
"critical": false,
|
||||
"extn_value": 1361182920,
|
||||
"extn_value_iso": "2013-02-18T10:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revocation_date_iso": "2013-02-18T10:22:51+00:00"
|
||||
}
|
||||
],
|
||||
"crl_extensions": [
|
||||
{
|
||||
"extn_id": "authority_key_identifier",
|
||||
"critical": false,
|
||||
"extn_value": {
|
||||
"key_identifier": "be:12:01:cc:aa:ea:11:80:da:2e:ad:b2...",
|
||||
"authority_cert_issuer": null,
|
||||
"authority_cert_serial_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"extn_id": "crl_number",
|
||||
"critical": false,
|
||||
"extn_value": 3
|
||||
}
|
||||
],
|
||||
"this_update_iso": "2013-02-18T10:32:00+00:00",
|
||||
"next_update_iso": "2013-02-18T10:42:00+00:00"
|
||||
},
|
||||
"signature_algorithm": {
|
||||
"algorithm": "sha1_rsa",
|
||||
"parameters": null
|
||||
},
|
||||
"signature": "42:21:be:81:f1:c3:79:76:66:5b:ce:21:13:8a:68:a..."
|
||||
}
|
||||
|
||||
<a id="jc.parsers.x509_crl.parse"></a>
|
||||
|
||||
### parse
|
||||
|
||||
```python
|
||||
def parse(data: Union[str, bytes],
|
||||
raw: bool = False,
|
||||
quiet: bool = False) -> Dict
|
||||
```
|
||||
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string or bytes) text or binary data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Raw or processed structured data.
|
||||
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
Source: [`jc/parsers/x509_crl.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/x509_crl.py)
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
@@ -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)
|
||||
|
||||
@@ -23,8 +23,9 @@ jc - JSON Convert streaming utils
|
||||
def add_jc_meta(func: ~F) -> ~F
|
||||
```
|
||||
|
||||
Decorator for streaming parsers to add stream_success and stream_error
|
||||
objects. This simplifies the yield lines in the streaming parsers.
|
||||
Decorator for streaming parsers to add `stream_success` and
|
||||
`stream_error` objects. This simplifies the `yield` lines in the
|
||||
streaming parsers.
|
||||
|
||||
With the decorator on parse():
|
||||
|
||||
@@ -58,7 +59,7 @@ In all cases above:
|
||||
successfully parse.
|
||||
|
||||
ignore_exceptions: (bool) continue processing lines and ignore
|
||||
exceptions if True.
|
||||
exceptions if `True`.
|
||||
|
||||
<a id="jc.streaming.raise_or_yield"></a>
|
||||
|
||||
@@ -69,8 +70,8 @@ def raise_or_yield(ignore_exceptions: bool, e: BaseException,
|
||||
line: str) -> Tuple[BaseException, str]
|
||||
```
|
||||
|
||||
Return the exception object and line string if ignore_exceptions is
|
||||
True. Otherwise, re-raise the exception from the exception object with
|
||||
Return the exception object and line string if `ignore_exceptions` is
|
||||
`True`. Otherwise, re-raise the exception from the exception object with
|
||||
an annotation.
|
||||
|
||||
<a id="jc.streaming.stream_error"></a>
|
||||
|
||||
@@ -44,7 +44,7 @@ Parameters:
|
||||
the parser. compatible options:
|
||||
linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
quiet: (bool) suppress compatibility message if True
|
||||
quiet: (bool) suppress compatibility message if `True`
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -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')
|
||||
@@ -168,7 +181,7 @@ Parameters:
|
||||
|
||||
Returns:
|
||||
|
||||
None - just prints output to STDERR
|
||||
None - just prints output to `STDERR`
|
||||
|
||||
<a id="jc.utils.has_data"></a>
|
||||
|
||||
@@ -181,7 +194,7 @@ def has_data(data: Union[str, bytes]) -> bool
|
||||
Checks if the string input contains data. If there are any
|
||||
non-whitespace characters then return `True`, else return `False`.
|
||||
|
||||
For bytes, returns True if there is any data.
|
||||
For bytes, returns `True` if there is any data.
|
||||
|
||||
Parameters:
|
||||
|
||||
@@ -189,9 +202,9 @@ Parameters:
|
||||
|
||||
Returns:
|
||||
|
||||
Boolean True if input string (data) contains non-whitespace
|
||||
characters, otherwise False. For bytes data, returns
|
||||
True if there is any data, otherwise False.
|
||||
Boolean `True` if input string (data) contains non-whitespace
|
||||
characters, otherwise `False`. For bytes data, returns
|
||||
`True` if there is any data, otherwise `False`.
|
||||
|
||||
<a id="jc.utils.input_type_check"></a>
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ class info():
|
||||
author: str = 'Kelly Brazil'
|
||||
author_email: str = 'kellyjonbrazil@gmail.com'
|
||||
website: str = 'https://github.com/kellyjonbrazil/jc'
|
||||
copyright: str = '© 2019-2024 Kelly Brazil'
|
||||
copyright: str = '© 2019-2025 Kelly Brazil'
|
||||
license: str = 'MIT License'
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -101,8 +101,8 @@ Examples:
|
||||
$ jc --pretty /proc/meminfo
|
||||
|
||||
Line Slicing:
|
||||
$ cat output.txt | jc 4:15 --parser # Parse from line 4 to 14
|
||||
with parser (zero-based)
|
||||
$ cat output.txt | jc 4:15 --<PARSER> # Parse from line 4 to 14
|
||||
# with <PARSER> (zero-based)
|
||||
|
||||
Parser Documentation:
|
||||
$ jc --help --dig
|
||||
|
||||
@@ -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
|
||||
|
||||
32
jc/lib.py
32
jc/lib.py
@@ -10,12 +10,13 @@ from jc import appdirs
|
||||
from jc import utils
|
||||
|
||||
|
||||
__version__ = '1.25.3'
|
||||
__version__ = '1.25.6'
|
||||
|
||||
parsers: List[str] = [
|
||||
'acpi',
|
||||
'airport',
|
||||
'airport-s',
|
||||
'amixer',
|
||||
'apt-cache-show',
|
||||
'apt-get-sqq',
|
||||
'arp',
|
||||
@@ -75,6 +76,7 @@ parsers: List[str] = [
|
||||
'iostat',
|
||||
'iostat-s',
|
||||
'ip-address',
|
||||
'ipconfig',
|
||||
'iptables',
|
||||
'ip-route',
|
||||
'iw-scan',
|
||||
@@ -101,12 +103,15 @@ parsers: List[str] = [
|
||||
'mpstat-s',
|
||||
'needrestart',
|
||||
'netstat',
|
||||
'net-localgroup',
|
||||
'net-user',
|
||||
'nmcli',
|
||||
'nsd-control',
|
||||
'ntpq',
|
||||
'openvpn',
|
||||
'os-prober',
|
||||
'os-release',
|
||||
'pacman',
|
||||
'passwd',
|
||||
'path',
|
||||
'path-list',
|
||||
@@ -177,6 +182,7 @@ parsers: List[str] = [
|
||||
'ps',
|
||||
'resolve-conf',
|
||||
'route',
|
||||
'route-print',
|
||||
'rpm-qi',
|
||||
'rsync',
|
||||
'rsync-s',
|
||||
@@ -208,6 +214,7 @@ parsers: List[str] = [
|
||||
'top-s',
|
||||
'tracepath',
|
||||
'traceroute',
|
||||
'traceroute-s',
|
||||
'tune2fs',
|
||||
'udevadm',
|
||||
'ufw',
|
||||
@@ -224,8 +231,10 @@ parsers: List[str] = [
|
||||
'vmstat-s',
|
||||
'w',
|
||||
'wc',
|
||||
'wg-show',
|
||||
'who',
|
||||
'x509-cert',
|
||||
'x509-crl',
|
||||
'x509-csr',
|
||||
'xml',
|
||||
'xrandr',
|
||||
@@ -342,7 +351,8 @@ def _get_parser(parser_mod_name: str) -> ModuleType:
|
||||
|
||||
def _parser_is_slurpable(parser: ModuleType) -> bool:
|
||||
"""
|
||||
Returns True if this parser can use the `--slurp` command option, else False
|
||||
Returns `True` if this parser can use the `--slurp` command option, else
|
||||
`False`
|
||||
|
||||
parser is a parser module object.
|
||||
"""
|
||||
@@ -354,7 +364,7 @@ def _parser_is_slurpable(parser: ModuleType) -> bool:
|
||||
|
||||
def _parser_is_streaming(parser: ModuleType) -> bool:
|
||||
"""
|
||||
Returns True if this is a streaming parser, else False
|
||||
Returns `True` if this is a streaming parser, else `False`
|
||||
|
||||
parser is a parser module object.
|
||||
"""
|
||||
@@ -365,7 +375,7 @@ def _parser_is_streaming(parser: ModuleType) -> bool:
|
||||
|
||||
def _parser_is_hidden(parser: ModuleType) -> bool:
|
||||
"""
|
||||
Returns True if this is a hidden parser, else False
|
||||
Returns `True` if this is a hidden parser, else `False`
|
||||
|
||||
parser is a parser module object.
|
||||
"""
|
||||
@@ -376,7 +386,7 @@ def _parser_is_hidden(parser: ModuleType) -> bool:
|
||||
|
||||
def _parser_is_deprecated(parser: ModuleType) -> bool:
|
||||
"""
|
||||
Returns True if this is a deprecated parser, else False
|
||||
Returns `True` if this is a deprecated parser, else `False`
|
||||
|
||||
parser is a parser module object.
|
||||
"""
|
||||
@@ -463,17 +473,17 @@ def parse(
|
||||
variants of the module name.
|
||||
|
||||
A Module object can also be passed
|
||||
directly or via get_parser()
|
||||
directly or via `get_parser()`
|
||||
|
||||
data: (string or data to parse (string or bytes for
|
||||
bytes or standard parsers, iterable of
|
||||
iterable) strings for streaming parsers)
|
||||
|
||||
raw: (boolean) output preprocessed JSON if True
|
||||
raw: (boolean) output preprocessed JSON if `True`
|
||||
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
quiet: (boolean) suppress warning messages if `True`
|
||||
|
||||
ignore_exceptions: (boolean) ignore parsing exceptions if True
|
||||
ignore_exceptions: (boolean) ignore parsing exceptions if `True`
|
||||
(streaming parsers only)
|
||||
|
||||
Returns:
|
||||
@@ -623,7 +633,7 @@ def parser_info(
|
||||
variants of the module name as well
|
||||
as a parser module object.
|
||||
|
||||
documentation: (boolean) include parser docstring if True
|
||||
documentation: (boolean) include parser docstring if `True`
|
||||
"""
|
||||
parser_mod = get_parser(parser_mod_name)
|
||||
parser_mod_name = parser_mod.__name__.split('.')[-1]
|
||||
@@ -660,7 +670,7 @@ def all_parser_info(
|
||||
|
||||
Parameters:
|
||||
|
||||
documentation: (boolean) include parser docstrings if True
|
||||
documentation: (boolean) include parser docstrings if `True`
|
||||
show_hidden: (boolean) also show parsers marked as hidden
|
||||
in their info metadata.
|
||||
show_deprecated: (boolean) also show parsers marked as
|
||||
|
||||
273
jc/parsers/amixer.py
Normal file
273
jc/parsers/amixer.py
Normal file
@@ -0,0 +1,273 @@
|
||||
r"""jc - JSON Convert `amixer sget` command output parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ amixer sget <control_name> | jc --amixer
|
||||
$ amixer sget Master | jc --amixer
|
||||
$ amixer sget Capture | jc --amixer
|
||||
$ amixer sget Speakers | jc --amixer
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('amixer', <amixer_sget_command_output>)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"control_name": string,
|
||||
"capabilities": [
|
||||
string
|
||||
],
|
||||
"playback_channels": [
|
||||
string
|
||||
],
|
||||
"limits": {
|
||||
"playback_min": integer,
|
||||
"playback_max": integer
|
||||
},
|
||||
"mono": {
|
||||
"playback_value": integer,
|
||||
"percentage": integer,
|
||||
"db": float,
|
||||
"status": boolean
|
||||
}
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ amixer sget Master | jc --amixer -p
|
||||
{
|
||||
"control_name": "Capture",
|
||||
"capabilities": [
|
||||
"cvolume",
|
||||
"cswitch"
|
||||
],
|
||||
"playback_channels": [],
|
||||
"limits": {
|
||||
"playback_min": 0,
|
||||
"playback_max": 63
|
||||
},
|
||||
"front_left": {
|
||||
"playback_value": 63,
|
||||
"percentage": 100,
|
||||
"db": 30.0,
|
||||
"status": true
|
||||
},
|
||||
"front_right": {
|
||||
"playback_value": 63,
|
||||
"percentage": 100,
|
||||
"db": 30.0,
|
||||
"status": true
|
||||
}
|
||||
}
|
||||
|
||||
$ amixer sget Master | jc --amixer -p -r
|
||||
{
|
||||
"control_name": "Master",
|
||||
"capabilities": [
|
||||
"pvolume",
|
||||
"pvolume-joined",
|
||||
"pswitch",
|
||||
"pswitch-joined"
|
||||
],
|
||||
"playback_channels": [
|
||||
"Mono"
|
||||
],
|
||||
"limits": {
|
||||
"playback_min": "0",
|
||||
"playback_max": "87"
|
||||
},
|
||||
"mono": {
|
||||
"playback_value": "87",
|
||||
"percentage": "100%",
|
||||
"db": "0.00db",
|
||||
"status": "on"
|
||||
}
|
||||
}
|
||||
"""
|
||||
from typing import Dict
|
||||
|
||||
import jc.utils
|
||||
from jc.utils import convert_to_int
|
||||
from jc.exceptions import ParseError
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`amixer` command parser'
|
||||
author = 'Eden Refael'
|
||||
author_email = 'edenraf@hotmail.com'
|
||||
compatible = ['linux']
|
||||
magic_commands = ['amixer']
|
||||
tags = ['command']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def _process(proc_data: Dict) -> Dict:
|
||||
"""
|
||||
Processes raw structured data to match the schema requirements.
|
||||
|
||||
Parameters:
|
||||
proc_data: (dict) raw structured data from the parser
|
||||
|
||||
Returns:
|
||||
(dict) processed structured data adhering to the schema
|
||||
"""
|
||||
if not proc_data:
|
||||
return {}
|
||||
|
||||
# Initialize the processed dictionary
|
||||
processed = {
|
||||
"control_name": proc_data.get("control_name", ""),
|
||||
"capabilities": proc_data.get("capabilities", []),
|
||||
"playback_channels": proc_data.get("playback_channels", []),
|
||||
"limits": {
|
||||
"playback_min": convert_to_int(proc_data.get("limits", {}).get("playback_min", 0)),
|
||||
"playback_max": convert_to_int(proc_data.get("limits", {}).get("playback_max", 0)),
|
||||
},
|
||||
}
|
||||
|
||||
# Process Mono or channel-specific data
|
||||
channels = ["mono", "front_left", "front_right"]
|
||||
for channel in channels:
|
||||
if channel in proc_data:
|
||||
channel_data = proc_data[channel]
|
||||
processed[channel] = {
|
||||
"playback_value": convert_to_int(channel_data.get("playback_value", 0)),
|
||||
"percentage": convert_to_int(channel_data.get("percentage", "0%").strip("%")),
|
||||
"db": float(channel_data.get("db", "0.0db").strip("db")),
|
||||
"status": channel_data.get("status", "off") == "on",
|
||||
}
|
||||
|
||||
return processed
|
||||
|
||||
|
||||
def parse(
|
||||
data: str,
|
||||
raw: bool = False,
|
||||
quiet: bool = False
|
||||
) -> Dict:
|
||||
"""
|
||||
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:
|
||||
Dictionary. Raw or processed structured data.
|
||||
"""
|
||||
"""
|
||||
The Algorithm for parsing the `amixer sget` command, Input Explained/Rules/Pseudo Algorithm:
|
||||
1. There will always be the first line which tells the user about the control name.
|
||||
2. There will always be the Capabilities which include many of capabilities - It will be listed and separated by `" "`.
|
||||
3. After that we'll need to distinct between the Channel - Could be many of channels - It will be listed and separated
|
||||
by `" "`.
|
||||
3a. Capture channels - List of channels
|
||||
3b. Playback channels - List of channels
|
||||
4. Limits - We'll always have the minimum limit and the maximum limit.
|
||||
|
||||
Input Example:
|
||||
1."":~$ amixer sget Capture
|
||||
Simple mixer control 'Capture',0
|
||||
Capabilities: cvolume cswitch
|
||||
Capture channels: Front Left - Front Right
|
||||
Limits: Capture 0 - 63
|
||||
Front Left: Capture 63 [100%] [30.00db] [on]
|
||||
Front Right: Capture 63 [100%] [30.00db] [on]
|
||||
|
||||
2."":~$ amixer sget Master
|
||||
Simple mixer control 'Master',0
|
||||
Capabilities: pvolume pvolume-joined pswitch pswitch-joined
|
||||
Playback channels: Mono
|
||||
Limits: Playback 0 - 87
|
||||
Mono: Playback 87 [100%] [0.00db] [on]
|
||||
|
||||
3."":~$ amixer sget Speaker
|
||||
Simple mixer control 'Speaker',0
|
||||
Capabilities: pvolume pswitch
|
||||
Playback channels: Front Left - Front Right
|
||||
Limits: Playback 0 - 87
|
||||
Mono:
|
||||
Front Left: Playback 87 [100%] [0.00db] [on]
|
||||
Front Right: Playback 87 [100%] [0.00db] [on]
|
||||
|
||||
4."":~$ amixer sget Headphone
|
||||
Simple mixer control 'Headphone',0
|
||||
Capabilities: pvolume pswitch
|
||||
Playback channels: Front Left - Front Right
|
||||
Limits: Playback 0 - 87
|
||||
Mono:
|
||||
Front Left: Playback 0 [0%] [-65.25db] [off]
|
||||
Front Right: Playback 0 [0%] [-65.25db] [off]
|
||||
"""
|
||||
# checks os compatibility and print a stderr massage if not compatible. quiet True could remove this check.
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
|
||||
# check if string
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
# starts the parsing from here
|
||||
mapping: Dict = {}
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
# split lines and than work on each line
|
||||
lines = data.splitlines()
|
||||
first_line = lines[0].strip()
|
||||
|
||||
# Extract the control name from the first line
|
||||
if first_line.startswith("Simple mixer control"):
|
||||
control_name = first_line.split("'")[1]
|
||||
else:
|
||||
raise ParseError("Invalid amixer output format: missing control name.")
|
||||
# map the control name
|
||||
mapping["control_name"] = control_name
|
||||
|
||||
# Process subsequent lines for capabilities, channels, limits, and channel-specific mapping.
|
||||
# gets the lines from the next line - because we already took care the first line.
|
||||
for line in lines[1:]:
|
||||
# strip the line (maybe there are white spaces in the begin&end)
|
||||
line = line.strip()
|
||||
|
||||
if line.startswith("Capabilities:"):
|
||||
mapping["capabilities"] = line.split(":")[1].strip().split()
|
||||
elif line.startswith("Playback channels:"):
|
||||
mapping["playback_channels"] = line.split(":")[1].strip().split(" - ")
|
||||
elif line.startswith("Limits:"):
|
||||
limits = line.split(":")[1].strip().split(" - ")
|
||||
mapping["limits"] = {
|
||||
"playback_min": limits[0].split()[1],
|
||||
"playback_max": limits[1]
|
||||
}
|
||||
elif line.startswith("Mono:") or line.startswith("Front Left:") or line.startswith("Front Right:"):
|
||||
# Identify the channel name and parse its information
|
||||
channel_name = line.split(":")[0].strip().lower().replace(" ", "_")
|
||||
channel_info = line.split(":")[1].strip()
|
||||
# Example: "Playback 255 [100%] [0.00db] [on]"
|
||||
channel_data = channel_info.split(" ")
|
||||
if channel_data[0] == "":
|
||||
continue
|
||||
|
||||
if "dB" in channel_data[3]:
|
||||
db_value = channel_data[3].strip("[]")
|
||||
status = channel_data[4].strip("[]")
|
||||
else:
|
||||
db_value = "0.0db"
|
||||
status = channel_data[3].strip("[]")
|
||||
|
||||
playback_value = channel_data[1]
|
||||
percentage = channel_data[2].strip("[]") # Extract percentage e.g., "100%"
|
||||
|
||||
# Store channel mapping in the dictionary
|
||||
mapping[channel_name] = {
|
||||
"playback_value": playback_value,
|
||||
"percentage": percentage,
|
||||
"db": db_value.lower(),
|
||||
"status": status
|
||||
}
|
||||
|
||||
return mapping if raw else _process(mapping)
|
||||
@@ -255,7 +255,7 @@ class EmailAddress(IA5String):
|
||||
# fix to allow incorrectly encoded email addresses to succeed with warning
|
||||
try:
|
||||
self._unicode = mailbox.decode('cp1252') + '@' + hostname.decode('idna')
|
||||
except UnicodeDecodeError:
|
||||
except (UnicodeDecodeError, UnicodeError):
|
||||
ascii_mailbox = mailbox.decode('ascii', errors='backslashreplace')
|
||||
ascii_hostname = hostname.decode('ascii', errors='backslashreplace')
|
||||
from jc.utils import warning_message
|
||||
|
||||
@@ -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,
|
||||
@@ -62,6 +64,7 @@ a controller and a device but there might be fields corresponding to one entity.
|
||||
"blocked": string,
|
||||
"connected": string,
|
||||
"legacy_pairing": string,
|
||||
"cable_pairing": string,
|
||||
"rssi": int,
|
||||
"txpower": int,
|
||||
"uuids": array,
|
||||
@@ -110,7 +113,7 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.2'
|
||||
version = '1.5'
|
||||
description = '`bluetoothctl` command parser'
|
||||
author = 'Jake Ob'
|
||||
author_email = 'iakopap at gmail.com'
|
||||
@@ -127,6 +130,8 @@ try:
|
||||
Controller = TypedDict(
|
||||
"Controller",
|
||||
{
|
||||
"manufacturer": str,
|
||||
"version": str,
|
||||
"name": str,
|
||||
"is_default": bool,
|
||||
"is_public": bool,
|
||||
@@ -135,6 +140,7 @@ try:
|
||||
"alias": str,
|
||||
"class": str,
|
||||
"powered": str,
|
||||
"power_state": str,
|
||||
"discoverable": str,
|
||||
"discoverable_timeout": str,
|
||||
"pairable": str,
|
||||
@@ -160,6 +166,7 @@ try:
|
||||
"blocked": str,
|
||||
"connected": str,
|
||||
"legacy_pairing": str,
|
||||
"cable_pairing": str,
|
||||
"rssi": int,
|
||||
"txpower": int,
|
||||
"uuids": List[str],
|
||||
@@ -175,10 +182,13 @@ 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>.+)"
|
||||
+ r"|\s*PowerState:\s*(?P<power_state>.+)"
|
||||
+ r"|\s*Discoverable:\s*(?P<discoverable>.+)"
|
||||
+ r"|\s*DiscoverableTimeout:\s*(?P<discoverable_timeout>.+)"
|
||||
+ r"|\s*Pairable:\s*(?P<pairable>.+)"
|
||||
@@ -203,6 +213,8 @@ def _parse_controller(next_lines: List[str]) -> Optional[Controller]:
|
||||
return None
|
||||
|
||||
controller: Controller = {
|
||||
"manufacturer": '',
|
||||
"version": '',
|
||||
"name": '',
|
||||
"is_default": False,
|
||||
"is_public": False,
|
||||
@@ -211,6 +223,7 @@ def _parse_controller(next_lines: List[str]) -> Optional[Controller]:
|
||||
"alias": '',
|
||||
"class": '',
|
||||
"powered": '',
|
||||
"power_state": '',
|
||||
"discoverable": '',
|
||||
"discoverable_timeout": '',
|
||||
"pairable": '',
|
||||
@@ -241,7 +254,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"]
|
||||
@@ -249,6 +266,8 @@ def _parse_controller(next_lines: List[str]) -> Optional[Controller]:
|
||||
controller["class"] = matches["class"]
|
||||
elif matches["powered"]:
|
||||
controller["powered"] = matches["powered"]
|
||||
elif matches["power_state"]:
|
||||
controller["power_state"] = matches["power_state"]
|
||||
elif matches["discoverable"]:
|
||||
controller["discoverable"] = matches["discoverable"]
|
||||
elif matches["discoverable_timeout"]:
|
||||
@@ -285,6 +304,7 @@ _device_line_pattern = (
|
||||
+ 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>.+))"
|
||||
+ r"|\s*CablePairing:\s*(?P<cable_pairing>.+)"
|
||||
)
|
||||
|
||||
|
||||
@@ -318,6 +338,7 @@ def _parse_device(next_lines: List[str], quiet: bool) -> Optional[Device]:
|
||||
"blocked": '',
|
||||
"connected": '',
|
||||
"legacy_pairing": '',
|
||||
"cable_pairing": '',
|
||||
"rssi": 0,
|
||||
"txpower": 0,
|
||||
"uuids": [],
|
||||
@@ -366,6 +387,8 @@ def _parse_device(next_lines: List[str], quiet: bool) -> Optional[Device]:
|
||||
device["connected"] = matches["connected"]
|
||||
elif matches["legacy_pairing"]:
|
||||
device["legacy_pairing"] = matches["legacy_pairing"]
|
||||
elif matches["cable_pairing"]:
|
||||
device["cable_pairing"] = matches["cable_pairing"]
|
||||
elif matches["rssi"]:
|
||||
rssi = matches["rssi"]
|
||||
try:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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')
|
||||
]
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
809
jc/parsers/ipconfig.py
Normal 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
|
||||
@@ -20,6 +20,9 @@ Schema:
|
||||
[
|
||||
{
|
||||
"chain": string,
|
||||
"default_policy": string,
|
||||
"default_packets": integer,
|
||||
"default_bytes": integer,
|
||||
"rules": [
|
||||
{
|
||||
"num" integer,
|
||||
@@ -44,6 +47,9 @@ Examples:
|
||||
[
|
||||
{
|
||||
"chain": "PREROUTING",
|
||||
"default_policy": "DROP",
|
||||
"default_packets": 0,
|
||||
"default_bytes": 0,
|
||||
"rules": [
|
||||
{
|
||||
"num": 1,
|
||||
@@ -103,6 +109,9 @@ Examples:
|
||||
[
|
||||
{
|
||||
"chain": "PREROUTING",
|
||||
"default_policy": "DROP",
|
||||
"default_packets": "0",
|
||||
"default_bytes": "0",
|
||||
"rules": [
|
||||
{
|
||||
"num": "1",
|
||||
@@ -158,12 +167,13 @@ Examples:
|
||||
...
|
||||
]
|
||||
"""
|
||||
import re
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.11'
|
||||
version = '1.12'
|
||||
description = '`iptables` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -174,6 +184,17 @@ class info():
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
chain_pkt_byt_pattern = re.compile(
|
||||
r'''
|
||||
\s\(policy\s
|
||||
(?P<policy_name>.+)
|
||||
\s
|
||||
(?P<packets>.+)
|
||||
\spackets,\s
|
||||
(?P<bytes>.+)
|
||||
\sbytes\)
|
||||
''', re.VERBOSE
|
||||
)
|
||||
|
||||
def _process(proc_data):
|
||||
"""
|
||||
@@ -188,6 +209,13 @@ def _process(proc_data):
|
||||
List of Dictionaries. Structured data to conform to the schema.
|
||||
"""
|
||||
for entry in proc_data:
|
||||
|
||||
if 'default_packets' in entry:
|
||||
entry['default_packets'] = jc.utils.convert_to_int(entry['default_packets'])
|
||||
|
||||
if 'default_bytes' in entry:
|
||||
entry['default_bytes'] = jc.utils.convert_size_to_int(entry['default_bytes'])
|
||||
|
||||
for rule in entry['rules']:
|
||||
int_list = ['num', 'pkts']
|
||||
for key in rule:
|
||||
@@ -243,6 +271,14 @@ def parse(data, raw=False, quiet=False):
|
||||
parsed_line = line.split()
|
||||
|
||||
chain['chain'] = parsed_line[1]
|
||||
|
||||
stats_match = re.search(chain_pkt_byt_pattern, line)
|
||||
if stats_match:
|
||||
stats = stats_match.groupdict()
|
||||
chain['default_policy'] = stats['policy_name']
|
||||
chain['default_packets'] = stats['packets']
|
||||
chain['default_bytes'] = stats['bytes']
|
||||
|
||||
chain['rules'] = []
|
||||
|
||||
continue
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -17,46 +17,53 @@ Schema:
|
||||
|
||||
[
|
||||
{
|
||||
"name": string,
|
||||
"maj_min": string,
|
||||
"rm": boolean,
|
||||
"size": string,
|
||||
"ro": boolean,
|
||||
"type": string,
|
||||
"mountpoint": string,
|
||||
"kname": string,
|
||||
"fstype": string,
|
||||
"label": string,
|
||||
"uuid": string,
|
||||
"partlabel": string,
|
||||
"partuuid": string,
|
||||
"ra": integer,
|
||||
"model": string,
|
||||
"serial": string,
|
||||
"state": string,
|
||||
"owner": string,
|
||||
"group": string,
|
||||
"mode": string,
|
||||
"alignment": integer,
|
||||
"min_io": integer,
|
||||
"opt_io": integer,
|
||||
"phy_sec": integer,
|
||||
"log_sec": integer,
|
||||
"rota": boolean,
|
||||
"sched": string,
|
||||
"rq_size": integer,
|
||||
"disc_aln": integer,
|
||||
"disc_gran": string,
|
||||
"disc_max": string,
|
||||
"disc_zero": boolean,
|
||||
"wsame": string,
|
||||
"wwn": string,
|
||||
"rand": boolean,
|
||||
"pkname": string,
|
||||
"hctl": string,
|
||||
"tran": string,
|
||||
"rev": string,
|
||||
"vendor": string
|
||||
"name": string,
|
||||
"maj_min": string,
|
||||
"rm": boolean,
|
||||
"size": string,
|
||||
"size_bytes": integer
|
||||
"ro": boolean,
|
||||
"type": string,
|
||||
"mountpoint": string,
|
||||
"mountpoints": [
|
||||
string
|
||||
],
|
||||
"kname": string,
|
||||
"fstype": string,
|
||||
"label": string,
|
||||
"uuid": string,
|
||||
"partlabel": string,
|
||||
"partuuid": string,
|
||||
"ra": integer,
|
||||
"model": string,
|
||||
"serial": string,
|
||||
"state": string,
|
||||
"owner": string,
|
||||
"group": string,
|
||||
"mode": string,
|
||||
"alignment": integer,
|
||||
"min_io": integer,
|
||||
"opt_io": integer,
|
||||
"phy_sec": integer,
|
||||
"log_sec": integer,
|
||||
"rota": boolean,
|
||||
"sched": string,
|
||||
"rq_size": integer,
|
||||
"disc_aln": integer,
|
||||
"disc_gran": string,
|
||||
"disc_gran_bytes": integer,
|
||||
"disc_max": string,
|
||||
"disc_max_bytes": integer,
|
||||
"disc_zero": boolean,
|
||||
"wsame": string,
|
||||
"wsame_bytes": integer,
|
||||
"wwn": string,
|
||||
"rand": boolean,
|
||||
"pkname": string,
|
||||
"hctl": string,
|
||||
"tran": string,
|
||||
"rev": string,
|
||||
"vendor": string
|
||||
}
|
||||
]
|
||||
|
||||
@@ -69,6 +76,7 @@ Examples:
|
||||
"maj_min": "8:0",
|
||||
"rm": false,
|
||||
"size": "20G",
|
||||
"size_bytes": 20000000000,
|
||||
"ro": false,
|
||||
"type": "disk",
|
||||
"mountpoint": null
|
||||
@@ -78,6 +86,7 @@ Examples:
|
||||
"maj_min": "8:1",
|
||||
"rm": false,
|
||||
"size": "1G",
|
||||
"size_bytes": 1000000000
|
||||
"ro": false,
|
||||
"type": "part",
|
||||
"mountpoint": "/boot"
|
||||
@@ -95,6 +104,7 @@ Examples:
|
||||
"maj_min": "8:0",
|
||||
"rm": false,
|
||||
"size": "20G",
|
||||
"size_bytes": 20000000000,
|
||||
"ro": false,
|
||||
"type": "disk",
|
||||
"mountpoint": null,
|
||||
@@ -121,9 +131,12 @@ Examples:
|
||||
"rq_size": 128,
|
||||
"disc_aln": 0,
|
||||
"disc_gran": "0B",
|
||||
"disc_gran_bytes": 0,
|
||||
"disc_max": "0B",
|
||||
"disc_max_bytes": 0,
|
||||
"disc_zero": false,
|
||||
"wsame": "32M",
|
||||
"wsame_bytes": 32000000,
|
||||
"wwn": null,
|
||||
"rand": true,
|
||||
"pkname": null,
|
||||
@@ -137,6 +150,7 @@ Examples:
|
||||
"maj_min": "8:1",
|
||||
"rm": false,
|
||||
"size": "1G",
|
||||
"size_bytes": 1000000000
|
||||
"ro": false,
|
||||
"type": "part",
|
||||
"mountpoint": "/boot",
|
||||
@@ -163,9 +177,12 @@ Examples:
|
||||
"rq_size": 128,
|
||||
"disc_aln": 0,
|
||||
"disc_gran": "0B",
|
||||
"disc_gran_bytes": 0,
|
||||
"disc_max": "0B",
|
||||
"disc_max_bytes": 0,
|
||||
"disc_zero": false,
|
||||
"wsame": "32M",
|
||||
"wsame_bytes": 32000000,
|
||||
"wwn": null,
|
||||
"rand": true,
|
||||
"pkname": "sda",
|
||||
@@ -187,6 +204,7 @@ Examples:
|
||||
"maj_min": "8:0",
|
||||
"rm": "0",
|
||||
"size": "20G",
|
||||
"size_bytes": 20000000000,
|
||||
"ro": "0",
|
||||
"type": "disk",
|
||||
"mountpoint": null,
|
||||
@@ -213,9 +231,12 @@ Examples:
|
||||
"rq_size": "128",
|
||||
"disc_aln": "0",
|
||||
"disc_gran": "0B",
|
||||
"disc_gran_bytes": 0,
|
||||
"disc_max": "0B",
|
||||
"disc_max_bytes": 0,
|
||||
"disc_zero": "0",
|
||||
"wsame": "32M",
|
||||
"wsame_bytes": 32000000,
|
||||
"wwn": null,
|
||||
"rand": "1",
|
||||
"pkname": null,
|
||||
@@ -229,6 +250,7 @@ Examples:
|
||||
"maj_min": "8:1",
|
||||
"rm": "0",
|
||||
"size": "1G",
|
||||
"size_bytes": 1000000000
|
||||
"ro": "0",
|
||||
"type": "part",
|
||||
"mountpoint": "/boot",
|
||||
@@ -255,9 +277,12 @@ Examples:
|
||||
"rq_size": "128",
|
||||
"disc_aln": "0",
|
||||
"disc_gran": "0B",
|
||||
"disc_gran_bytes": 0,
|
||||
"disc_max": "0B",
|
||||
"disc_max_bytes": 0,
|
||||
"disc_zero": "0",
|
||||
"wsame": "32M",
|
||||
"wsame_bytes": 32000000,
|
||||
"wwn": null,
|
||||
"rand": "1",
|
||||
"pkname": "sda",
|
||||
@@ -275,7 +300,7 @@ import jc.parsers.universal
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.9'
|
||||
version = '1.10'
|
||||
description = '`lsblk` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -304,14 +329,19 @@ def _process(proc_data):
|
||||
int_list = {'ra', 'alignment', 'min_io', 'opt_io', 'phy_sec', 'log_sec',
|
||||
'rq_size', 'disc_aln'}
|
||||
|
||||
size_list = {'size', 'disc_gran', 'disc_max', 'wsame'}
|
||||
|
||||
for entry in proc_data:
|
||||
for key in entry:
|
||||
for key in entry.copy():
|
||||
if key in bool_list:
|
||||
entry[key] = jc.utils.convert_to_bool(entry[key])
|
||||
|
||||
if key in int_list:
|
||||
entry[key] = jc.utils.convert_to_int(entry[key])
|
||||
|
||||
if key in size_list:
|
||||
entry[key + '_bytes'] = jc.utils.convert_size_to_int(entry[key], posix_mode=True)
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
@@ -335,6 +365,7 @@ def parse(data, raw=False, quiet=False):
|
||||
# Clear any blank lines
|
||||
cleandata = list(filter(None, data.splitlines()))
|
||||
raw_output = []
|
||||
new_list = []
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
@@ -346,11 +377,23 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
raw_output = jc.parsers.universal.sparse_table_parse(cleandata)
|
||||
|
||||
# clean up non-ascii characters, if any
|
||||
# find multiple mount points and add to a single entry
|
||||
for entry in raw_output:
|
||||
entry['name'] = entry['name'].encode('ascii', errors='ignore').decode()
|
||||
if entry['name']:
|
||||
if 'mountpoints' in entry:
|
||||
if entry['mountpoints']:
|
||||
entry['mountpoints'] = [entry['mountpoints']]
|
||||
else:
|
||||
entry['mountpoints'] = []
|
||||
new_list.append(entry)
|
||||
elif 'mountpoints' in entry and entry['mountpoints']:
|
||||
new_list[-1]['mountpoints'].append(entry['mountpoints'])
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return _process(raw_output)
|
||||
# clean up tree characters, if any
|
||||
for entry in new_list:
|
||||
tree_chars = ['`-', '|-', '├─', '└─']
|
||||
for chars in tree_chars:
|
||||
if entry['name'][0:2] == chars:
|
||||
entry['name'] = entry['name'][2:]
|
||||
|
||||
return new_list if raw else _process(new_list)
|
||||
|
||||
@@ -71,13 +71,12 @@ Example:
|
||||
]
|
||||
"""
|
||||
import re
|
||||
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.9'
|
||||
version = '1.11'
|
||||
description = '`mount` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -111,19 +110,24 @@ def _osx_parse(data):
|
||||
for entry in data:
|
||||
output_line = {}
|
||||
|
||||
filesystem = entry.split(' on ')
|
||||
filesystem = filesystem[0]
|
||||
output_line['filesystem'] = filesystem
|
||||
pattern = re.compile(
|
||||
r'''
|
||||
(?P<filesystem>.*)
|
||||
\son\s
|
||||
(?P<mount_point>.*?)
|
||||
\s
|
||||
\((?P<options>.*?)\)\s*
|
||||
''', re.VERBOSE
|
||||
)
|
||||
|
||||
mount_point = entry.split(' on ')
|
||||
mount_point = mount_point[1].split(' (')
|
||||
mount_point = mount_point[0]
|
||||
output_line['mount_point'] = mount_point
|
||||
mymatch = pattern.match(entry)
|
||||
groups = mymatch.groupdict()
|
||||
|
||||
options = entry.split('(', maxsplit=1)
|
||||
options = options[1].rstrip(')')
|
||||
options = options.split(', ')
|
||||
output_line['options'] = options
|
||||
if groups:
|
||||
output_line['filesystem'] = groups['filesystem']
|
||||
output_line['mount_point'] = groups['mount_point']
|
||||
options = groups['options'].split(', ')
|
||||
output_line['options'] = options
|
||||
|
||||
output.append(output_line)
|
||||
|
||||
@@ -138,16 +142,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"]
|
||||
@@ -215,7 +222,7 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
# check for OSX and AIX output
|
||||
if ' type ' not in cleandata[0]:
|
||||
if 'node' in cleandata[0]:
|
||||
if ' node ' in cleandata[0]:
|
||||
raw_output = _aix_parse(cleandata)
|
||||
else:
|
||||
raw_output = _osx_parse(cleandata)
|
||||
@@ -223,7 +230,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)
|
||||
|
||||
194
jc/parsers/net_localgroup.py
Normal file
194
jc/parsers/net_localgroup.py
Normal file
@@ -0,0 +1,194 @@
|
||||
r"""jc - JSON Convert `net localgroup` command output parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ net localgroup | jc --net-localgroup
|
||||
$ net localgroup /domain | jc --net-localgroup
|
||||
$ net localgroup Administrators | jc --net-localgroup
|
||||
$ net localgroup Administrators /domain | jc --net-localgroup
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('net_localgroup', net_localgroup_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"account_origin": string,
|
||||
"domain": string,
|
||||
"comment": string,
|
||||
"groups": [
|
||||
{
|
||||
"name": string
|
||||
"members": [
|
||||
string
|
||||
]
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ net localgroup | jc --net-localgroup -p
|
||||
{
|
||||
"account_origin": null,
|
||||
"comment": null,
|
||||
"domain": null,
|
||||
"groups": [
|
||||
{
|
||||
"name": "Administrators",
|
||||
"members": [
|
||||
"Administrator",
|
||||
"Operator",
|
||||
"ansible",
|
||||
"user1"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
import re
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`net localgroup` command parser'
|
||||
author = 'joehacksalot'
|
||||
author_email = 'joehacksalot@gmail.com'
|
||||
compatible = ['win32']
|
||||
magic_commands = ['net localgroup']
|
||||
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(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.
|
||||
"""
|
||||
return proc_data
|
||||
|
||||
|
||||
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_net_localgroup_list(line_iter, expect_asterisk):
|
||||
name_list = []
|
||||
while True:
|
||||
try:
|
||||
line = next(line_iter)
|
||||
if not line.strip():
|
||||
continue # Skip empty lines
|
||||
|
||||
# Check if the line starts with an asterisk
|
||||
if line == 'The command completed successfully.':
|
||||
break
|
||||
elif expect_asterisk and line.startswith('*'):
|
||||
name_list.append(line[1:].strip())
|
||||
else:
|
||||
name_list.append(line)
|
||||
except StopIteration:
|
||||
break
|
||||
return name_list
|
||||
|
||||
|
||||
def _parse(data):
|
||||
lines = data.splitlines()
|
||||
parse_type = None # Can be 'groups_list' or 'members'
|
||||
|
||||
result = {
|
||||
"account_origin": None,
|
||||
"domain": None,
|
||||
"comment": None,
|
||||
"groups": []
|
||||
}
|
||||
|
||||
group_name = ""
|
||||
lines = data.splitlines()
|
||||
lines = [line.rstrip() for line in lines if line.strip() != ""]
|
||||
|
||||
line_iter = _PushbackIterator(iter(lines))
|
||||
for line in line_iter:
|
||||
line = line.rstrip()
|
||||
|
||||
# Skip empty lines
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
match_domain_processed = re.match(r"^The request will be processed at a domain controller for domain (.+)", line, re.IGNORECASE)
|
||||
match_localgroup_list = re.match(r"^Aliases for[\s]*([^:]+)", line, re.IGNORECASE) # "Aliases for \\DESKTOP-WIN11:"
|
||||
match_localgroup_members = re.match(r"^Alias name[\s]*([^:]+)", line, re.IGNORECASE) # "Alias name administrators:"
|
||||
if match_domain_processed:
|
||||
# Extract the domain name
|
||||
result["domain"] = match_domain_processed.group(1).strip()
|
||||
elif match_localgroup_list:
|
||||
# Extract the account origin
|
||||
result["account_origin"] = match_localgroup_list.group(1).strip()
|
||||
parse_type = 'groups_list' # Prepare to read groups
|
||||
elif match_localgroup_members:
|
||||
# We are querying a specific group
|
||||
group_name = match_localgroup_members.group(1).strip()
|
||||
parse_type = 'members_list' # Prepare to read members
|
||||
elif line.startswith('Comment'):
|
||||
comment_line = line.split('Comment', 1)[1].strip()
|
||||
result["comment"] = comment_line if comment_line else None
|
||||
elif line.startswith('---'):
|
||||
# Start of a section (groups or members)
|
||||
if parse_type == 'groups_list':
|
||||
names_list = _parse_net_localgroup_list(line_iter, expect_asterisk=True)
|
||||
result["groups"] = [{"name": group_name, "members": []} for group_name in names_list]
|
||||
elif parse_type == 'members_list':
|
||||
names_list = _parse_net_localgroup_list(line_iter, expect_asterisk=False)
|
||||
result["groups"] = [{
|
||||
"name": group_name,
|
||||
"members": names_list
|
||||
}]
|
||||
|
||||
return result
|
||||
441
jc/parsers/net_user.py
Normal file
441
jc/parsers/net_user.py
Normal file
@@ -0,0 +1,441 @@
|
||||
r"""jc - JSON Convert `net user` command output parser
|
||||
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ net users | jc --net-user
|
||||
$ net users /domain | jc --net-user
|
||||
$ net users User1 | jc --net-user
|
||||
$ net users User1 /domain | jc --net-user
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('net_user', net_user_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"domain": string,
|
||||
"account_origin": string,
|
||||
"user_accounts": [
|
||||
{
|
||||
"user_name": string,
|
||||
"full_name": string,
|
||||
"comment": string,
|
||||
"user_comment": string,
|
||||
"country_region_code": string,
|
||||
"account_active": boolean,
|
||||
"account_expires": string,
|
||||
"password_last_set": string,
|
||||
"password_expires": string,
|
||||
"password_changeable": string,
|
||||
"password_required": boolean,
|
||||
"user_may_change_password": boolean,
|
||||
"workstations_allowed": string,
|
||||
"logon_script": string,
|
||||
"user_profile": string,
|
||||
"home_directory": string,
|
||||
"last_logon": string,
|
||||
"logon_hours_allowed": string,
|
||||
"local_group_memberships": [
|
||||
string,
|
||||
],
|
||||
"global_group_memberships": [
|
||||
string,
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
Examples:
|
||||
|
||||
$ net users | jc --net-user -p
|
||||
{
|
||||
"account_origin": "\\\\WIN-SERVER16",
|
||||
"domain": "",
|
||||
"user_accounts": [
|
||||
{
|
||||
"user_name": "Administrator"
|
||||
},
|
||||
{
|
||||
"user_name": "DefaultAccount"
|
||||
},
|
||||
{
|
||||
"user_name": "Guest"
|
||||
},
|
||||
{
|
||||
"user_name": "pentera_BnlLQVnd7p"
|
||||
},
|
||||
{
|
||||
"user_name": "user1"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ net users /domain | jc --net-user -p
|
||||
{
|
||||
"account_origin": "\\\\DESKTOP-WIN10-PRO.somecompany.corp",
|
||||
"domain": "somecompany.corp",
|
||||
"user_accounts": [
|
||||
{
|
||||
"user_name": "aaron"
|
||||
},
|
||||
{
|
||||
"user_name": "addison"
|
||||
},
|
||||
{
|
||||
"user_name": "Administrator"
|
||||
},
|
||||
{
|
||||
"user_name": "ansible"
|
||||
},
|
||||
{
|
||||
"user_name": "da"
|
||||
},
|
||||
{
|
||||
"user_name": "DefaultAccount"
|
||||
},
|
||||
{
|
||||
"user_name": "Guest"
|
||||
},
|
||||
{
|
||||
"user_name": "harrison"
|
||||
},
|
||||
{
|
||||
"user_name": "james"
|
||||
},
|
||||
{
|
||||
"user_name": "krbtgt"
|
||||
},
|
||||
{
|
||||
"user_name": "liam"
|
||||
},
|
||||
{
|
||||
"user_name": "localadmin"
|
||||
},
|
||||
{
|
||||
"user_name": "tiffany"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ net users Administrator | jc --net-user -p
|
||||
{
|
||||
"domain": "",
|
||||
"user_accounts": [
|
||||
{
|
||||
"account_active": true,
|
||||
"account_expires": "Never",
|
||||
"comment": "Built-in account for administering the computer/domain",
|
||||
"country_region_code": "000 (System Default)",
|
||||
"global_group_memberships": [],
|
||||
"last_logon": "2024-08-23T13:47:11",
|
||||
"local_group_memberships": [
|
||||
"Administrators"
|
||||
],
|
||||
"logon_hours_allowed": "All",
|
||||
"password_changeable": "2021-12-17T11:07:14",
|
||||
"password_expires": "2022-01-27T11:07:14",
|
||||
"password_last_set": "2021-12-16T11:07:14",
|
||||
"password_required": true,
|
||||
"user_may_change_password": true,
|
||||
"user_name": "Administrators",
|
||||
"workstations_allowed": "All"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
$ net users Administrator /domain | jc --net-user -p | jq
|
||||
{
|
||||
"domain": "somecompany.corp",
|
||||
"user_accounts": [
|
||||
{
|
||||
"account_active": true,
|
||||
"account_expires": "Never",
|
||||
"comment": "Built-in account for administering the computer/domain",
|
||||
"country_region_code": "000 (System Default)",
|
||||
"global_group_memberships": [
|
||||
"Domain Admins",
|
||||
"Domain Users",
|
||||
"Group Policy Creator",
|
||||
"Enterprise Admins",
|
||||
"Schema Admins"
|
||||
],
|
||||
"last_logon": "2024-07-17T13:46:12",
|
||||
"local_group_memberships": [
|
||||
"Administrators"
|
||||
],
|
||||
"logon_hours_allowed": "All",
|
||||
"password_changeable": "2023-09-30T11:44:26",
|
||||
"password_expires": "Never",
|
||||
"password_last_set": "2023-09-29T11:44:26",
|
||||
"password_required": true,
|
||||
"user_may_change_password": true,
|
||||
"user_name": "Administrators",
|
||||
"workstations_allowed": "All"
|
||||
}
|
||||
]
|
||||
}
|
||||
"""
|
||||
from datetime import datetime
|
||||
import re
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`net user` command parser'
|
||||
author = 'joehacksalot'
|
||||
author_email = 'joehacksalot@gmail.com'
|
||||
compatible = ['win32']
|
||||
magic_commands = ['net user']
|
||||
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):
|
||||
raw_output = _parse(data)
|
||||
|
||||
if not raw_output:
|
||||
raw_output = {}
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
|
||||
def _set_if_not_none(output_dict, key, value):
|
||||
if value is not None:
|
||||
output_dict[key] = value
|
||||
|
||||
def _process_string_is_yes(text):
|
||||
if text:
|
||||
return text.lower() == "yes"
|
||||
else:
|
||||
return None
|
||||
|
||||
def _process_date(s):
|
||||
if s is not None:
|
||||
for fmt in ('%m/%d/%Y %I:%M:%S %p', '%m/%d/%Y %H:%M:%S %p'):
|
||||
try:
|
||||
return datetime.strptime(s, fmt).isoformat()
|
||||
except ValueError:
|
||||
continue
|
||||
return s # Return the original string if parsing fails
|
||||
|
||||
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.
|
||||
"""
|
||||
for user_account in proc_data.get("user_accounts", []):
|
||||
_set_if_not_none(user_account, "account_active", _process_string_is_yes(user_account.get("account_active", None)))
|
||||
_set_if_not_none(user_account, "password_last_set", _process_date(user_account.get("password_last_set", None)))
|
||||
_set_if_not_none(user_account, "password_expires", _process_date(user_account.get("password_expires", None)))
|
||||
_set_if_not_none(user_account, "password_changeable", _process_date(user_account.get("password_changeable", None)))
|
||||
_set_if_not_none(user_account, "password_required", _process_string_is_yes(user_account.get("password_required", None)))
|
||||
_set_if_not_none(user_account, "user_may_change_password", _process_string_is_yes(user_account.get("user_may_change_password", None)))
|
||||
_set_if_not_none(user_account, "last_logon", _process_date(user_account.get("last_logon", None)))
|
||||
|
||||
if not proc_data:
|
||||
proc_data = {}
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
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_user_account_keypairs(keypair_dict):
|
||||
user_account_parsed = {}
|
||||
_set_if_not_none(user_account_parsed, "user_name", keypair_dict.get("user_name", None))
|
||||
_set_if_not_none(user_account_parsed, "full_name", keypair_dict.get("full_name", None))
|
||||
_set_if_not_none(user_account_parsed, "comment", keypair_dict.get("comment", None))
|
||||
_set_if_not_none(user_account_parsed, "users_comment", keypair_dict.get("users_comment", None))
|
||||
_set_if_not_none(user_account_parsed, "country_region_code", keypair_dict.get("country_region_code", None))
|
||||
_set_if_not_none(user_account_parsed, "account_active", keypair_dict.get("account_active", None))
|
||||
_set_if_not_none(user_account_parsed, "account_expires", keypair_dict.get("account_expires", None))
|
||||
_set_if_not_none(user_account_parsed, "password_last_set", keypair_dict.get("password_last_set", None))
|
||||
_set_if_not_none(user_account_parsed, "password_expires", keypair_dict.get("password_expires", None))
|
||||
_set_if_not_none(user_account_parsed, "password_changeable", keypair_dict.get("password_changeable", None))
|
||||
_set_if_not_none(user_account_parsed, "password_required", keypair_dict.get("password_required", None))
|
||||
_set_if_not_none(user_account_parsed, "user_may_change_password", keypair_dict.get("user_may_change_password", None))
|
||||
_set_if_not_none(user_account_parsed, "workstations_allowed", keypair_dict.get("workstations_allowed", None))
|
||||
_set_if_not_none(user_account_parsed, "logon_script", keypair_dict.get("logon_script", None))
|
||||
_set_if_not_none(user_account_parsed, "user_profile", keypair_dict.get("user_profile", None))
|
||||
_set_if_not_none(user_account_parsed, "home_directory", keypair_dict.get("home_directory", None))
|
||||
_set_if_not_none(user_account_parsed, "last_logon", keypair_dict.get("last_logon", None))
|
||||
_set_if_not_none(user_account_parsed, "logon_hours_allowed", keypair_dict.get("logon_hours_allowed", None))
|
||||
_set_if_not_none(user_account_parsed, "local_group_memberships", keypair_dict.get("local_group_memberships", None))
|
||||
_set_if_not_none(user_account_parsed, "global_group_memberships", keypair_dict.get("global_group_memberships", None))
|
||||
return user_account_parsed
|
||||
|
||||
|
||||
def _parse_groups(line_iter):
|
||||
group_list = []
|
||||
# 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).strip()
|
||||
if not next_line:
|
||||
continue # Skip empty lines
|
||||
|
||||
# Check if the line is indented (starts with whitespace)
|
||||
if next_line.startswith('*'):
|
||||
groups = next_line.split("*")
|
||||
groups = [group.strip() for group in groups if group.strip() != ""]
|
||||
if "None" in groups:
|
||||
groups.remove("None")
|
||||
# It's an indented line; append the stripped line to entry_list
|
||||
group_list.extend(groups)
|
||||
else:
|
||||
# Not an indented line; push it back and exit
|
||||
line_iter.pushback(next_line)
|
||||
break
|
||||
except StopIteration:
|
||||
break
|
||||
return group_list
|
||||
|
||||
def _parse(data):
|
||||
result = {
|
||||
"domain": "",
|
||||
"user_accounts": []
|
||||
}
|
||||
|
||||
lines = data.splitlines()
|
||||
lines = [line.rstrip() for line in lines if line.strip() != ""]
|
||||
|
||||
line_iter = _PushbackIterator(iter(lines))
|
||||
for line in line_iter:
|
||||
line = line.rstrip()
|
||||
|
||||
# Skip empty lines
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
match_domain_processed = re.match(r"^The request will be processed at a domain controller for domain (.+)\.$", line, re.IGNORECASE)
|
||||
if match_domain_processed:
|
||||
result["domain"] = match_domain_processed.group(1).strip()
|
||||
# Check if the text is of the first type (detailed user info)
|
||||
elif "User name" in line:
|
||||
line_iter.pushback(line)
|
||||
user_account_keypairs = {}
|
||||
|
||||
# Regular expression to match key-value pairs
|
||||
kv_pattern = re.compile(r'^([\w\s\/\'\-]{1,29})\s*(.+)?$')
|
||||
key = None
|
||||
|
||||
while True:
|
||||
# Process each line
|
||||
# Break when done
|
||||
try:
|
||||
line = next(line_iter)
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue # Skip empty lines
|
||||
|
||||
match = kv_pattern.match(line)
|
||||
if "The command completed" in line:
|
||||
break
|
||||
elif match:
|
||||
key_raw = match.group(1).strip()
|
||||
key = key_raw.lower().replace(" ", "_").replace("'", "").replace("/", "_").replace("-", "_")
|
||||
if len(match.groups()) == 2 and match.group(2) is not None:
|
||||
value = match.group(2).strip()
|
||||
if key in ["local_group_memberships", "global_group_memberships"]:
|
||||
line_iter.pushback(value)
|
||||
user_account_keypairs[key] = _parse_groups(line_iter)
|
||||
else:
|
||||
user_account_keypairs[key] = value
|
||||
else:
|
||||
# Line without value, it's a key with empty value
|
||||
user_account_keypairs[key] = None
|
||||
else:
|
||||
raise ValueError(f"Unexpected line: {line}")
|
||||
except StopIteration:
|
||||
break
|
||||
|
||||
# Convert specific fields
|
||||
result["user_accounts"].append(_parse_user_account_keypairs(user_account_keypairs))
|
||||
elif "User accounts for" in line:
|
||||
line_iter.pushback(line)
|
||||
collecting_users = False
|
||||
|
||||
while True:
|
||||
# Process each line
|
||||
# Break when done
|
||||
try:
|
||||
line = next(line_iter)
|
||||
line = line.strip()
|
||||
if not line:
|
||||
continue # Skip empty lines
|
||||
|
||||
# Check for domain line
|
||||
domain_pattern = re.compile(r'^User accounts for (.+)$')
|
||||
account_origin_match = domain_pattern.match(line)
|
||||
if account_origin_match:
|
||||
result["account_origin"] = account_origin_match.group(1)
|
||||
continue
|
||||
|
||||
# Check for the line of dashes indicating start of user list
|
||||
if line.startswith('---'):
|
||||
collecting_users = True
|
||||
continue
|
||||
|
||||
# Check for the completion message
|
||||
if line.startswith('The command completed'):
|
||||
break
|
||||
|
||||
if collecting_users:
|
||||
# Split the line into usernames
|
||||
user_matches = re.match(r'(.{1,20})(\s+.{1,20})?(\s+.{1,20})?', line)
|
||||
if user_matches:
|
||||
for username in user_matches.groups():
|
||||
if username:
|
||||
username = username.strip()
|
||||
user_account = {"user_name": username}
|
||||
result["user_accounts"].append(user_account)
|
||||
except StopIteration:
|
||||
break
|
||||
else:
|
||||
raise ValueError(f"Unexpected line: {line}")
|
||||
|
||||
return result
|
||||
@@ -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'
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -36,6 +36,8 @@ These are documented below.
|
||||
[
|
||||
{
|
||||
"<key>": string/integer/float, # [0]
|
||||
"team_config": object/null,
|
||||
"team_port_config": object/null,
|
||||
"dhcp4_option_x": {
|
||||
"name": string,
|
||||
"value": string/integer/float,
|
||||
@@ -141,6 +143,7 @@ Examples:
|
||||
]
|
||||
"""
|
||||
import re
|
||||
import json
|
||||
from typing import List, Dict, Optional
|
||||
import jc.utils
|
||||
from jc.parsers.universal import sparse_table_parse
|
||||
@@ -149,7 +152,7 @@ from jc.exceptions import ParseError
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
version = '1.2'
|
||||
description = '`nmcli` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -313,8 +316,39 @@ def _device_show_parse(data: str) -> List[Dict]:
|
||||
def _connection_show_x_parse(data: str) -> List[Dict]:
|
||||
raw_output: List = []
|
||||
item: Dict = {}
|
||||
in_team_config: bool = False
|
||||
team_config_value: List = []
|
||||
|
||||
for line in filter(None, data.splitlines()):
|
||||
|
||||
# fix for team.config and team-port.config, which are multi-line JSON
|
||||
if line.startswith('team.config:') or line.startswith('team-port.config:'):
|
||||
key, value = line.split(':', maxsplit=1)
|
||||
key_team = _normalize_key(key)
|
||||
value_team = value.strip()
|
||||
|
||||
if value_team == '--':
|
||||
team_config_value = []
|
||||
item[key_team] = None
|
||||
continue
|
||||
|
||||
in_team_config = True
|
||||
team_config_value.append(value_team)
|
||||
item[key_team] = {}
|
||||
continue
|
||||
|
||||
STARTSWITH_TEAM = line.startswith('team.') or line.startswith('team-port.')
|
||||
if not STARTSWITH_TEAM and in_team_config:
|
||||
team_config_value.append(line.strip())
|
||||
continue
|
||||
|
||||
in_team_config = False
|
||||
|
||||
if team_config_value:
|
||||
# team.config and team-port.config values should always be JSON
|
||||
item[key_team] = json.loads(''.join(team_config_value))
|
||||
team_config_value = []
|
||||
|
||||
key, value = line.split(':', maxsplit=1)
|
||||
|
||||
key_n = _normalize_key(key)
|
||||
|
||||
@@ -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
348
jc/parsers/pacman.py
Normal 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.1'
|
||||
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', 'yay']
|
||||
|
||||
|
||||
__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)
|
||||
@@ -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',
|
||||
|
||||
@@ -26,7 +26,7 @@ or
|
||||
|
||||
or
|
||||
|
||||
$ cat /proc/meminfo | jc --proc-memifno
|
||||
$ cat /proc/meminfo | jc --proc-meminfo
|
||||
|
||||
Usage (module):
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ Edid module
|
||||
|
||||
import struct
|
||||
from collections import namedtuple
|
||||
from typing import ByteString
|
||||
|
||||
__all__ = ["Edid"]
|
||||
|
||||
@@ -108,10 +107,10 @@ class Edid:
|
||||
),
|
||||
)
|
||||
|
||||
def __init__(self, edid: ByteString):
|
||||
def __init__(self, edid: bytes):
|
||||
self._parse_edid(edid)
|
||||
|
||||
def _parse_edid(self, edid: ByteString):
|
||||
def _parse_edid(self, edid: bytes):
|
||||
"""Convert edid byte string to edid object"""
|
||||
if struct.calcsize(self._STRUCT_FORMAT) != 128:
|
||||
raise ValueError("Wrong edid size.")
|
||||
|
||||
@@ -3,7 +3,7 @@ EDID helper
|
||||
"""
|
||||
|
||||
from subprocess import CalledProcessError, check_output
|
||||
from typing import ByteString, List
|
||||
from typing import List
|
||||
|
||||
__all__ = ["EdidHelper"]
|
||||
|
||||
@@ -12,14 +12,14 @@ class EdidHelper:
|
||||
"""Class for working with EDID data"""
|
||||
|
||||
@staticmethod
|
||||
def hex2bytes(hex_data: str) -> ByteString:
|
||||
def hex2bytes(hex_data: str) -> bytes:
|
||||
"""Convert hex EDID string to bytes
|
||||
|
||||
Args:
|
||||
hex_data (str): hex edid string
|
||||
|
||||
Returns:
|
||||
ByteString: edid byte string
|
||||
bytes: edid byte string
|
||||
"""
|
||||
# delete edid 1.3 additional block
|
||||
if len(hex_data) > 256:
|
||||
@@ -32,14 +32,14 @@ class EdidHelper:
|
||||
return bytes(numbers)
|
||||
|
||||
@classmethod
|
||||
def get_edids(cls) -> List[ByteString]:
|
||||
def get_edids(cls) -> List[bytes]:
|
||||
"""Get edids from xrandr
|
||||
|
||||
Raises:
|
||||
`RuntimeError`: if error with retrieving xrandr util data
|
||||
|
||||
Returns:
|
||||
List[ByteString]: list with edids
|
||||
List[bytes]: list with edids
|
||||
"""
|
||||
try:
|
||||
output = check_output(["xrandr", "--verbose"])
|
||||
|
||||
@@ -111,8 +111,6 @@ Examples:
|
||||
]
|
||||
"""
|
||||
import jc.utils
|
||||
import jc.parsers.universal
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
|
||||
455
jc/parsers/route_print.py
Normal file
455
jc/parsers/route_print.py
Normal file
@@ -0,0 +1,455 @@
|
||||
r"""jc - JSON Convert `route print` command output parser
|
||||
|
||||
See also: the `route` command parser
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ route print | jc --route-print
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('route_print', route_print_command_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"interface_list": [
|
||||
{
|
||||
"interface_index": integer,
|
||||
"mac_address": string,
|
||||
"description": string
|
||||
}
|
||||
],
|
||||
"ipv4_route_table": {
|
||||
"active_routes": [
|
||||
{
|
||||
"network_destination": string,
|
||||
"netmask": string,
|
||||
"gateway": string,
|
||||
"interface": string,
|
||||
"metric": integer, # [0]
|
||||
"metric_set_to_default": boolean # [1]
|
||||
}
|
||||
],
|
||||
"persistent_routes": [
|
||||
{
|
||||
"network_address": string,
|
||||
"netmask": string,
|
||||
"gateway_address": string,
|
||||
"metric": integer # [0]
|
||||
"metric_set_to_default": boolean # [1]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ipv6_route_table": {
|
||||
"active_routes": [
|
||||
{
|
||||
"interface": integer,
|
||||
"metric": integer, # [0]
|
||||
"metric_set_to_default": boolean, # [1]
|
||||
"network_destination": string,
|
||||
"gateway": string
|
||||
}
|
||||
],
|
||||
"persistent_routes": [
|
||||
{
|
||||
"interface": integer,
|
||||
"metric": integer, # [0]
|
||||
"metric_set_to_default": boolean, # [1]
|
||||
"network_destination": string,
|
||||
"gateway": string
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
[0] Null/None if "metric" = "Default"
|
||||
[1] True if "metric" = "Default"
|
||||
|
||||
Examples:
|
||||
|
||||
$ route print | jc --route-print -p
|
||||
{
|
||||
"interface_list": [
|
||||
{
|
||||
"interface_index": 28,
|
||||
"mac_address": null,
|
||||
"description": "Tailscale Tunnel"
|
||||
},
|
||||
{
|
||||
"interface_index": 12,
|
||||
"mac_address": "00:1c:42:da:01:6a",
|
||||
"description": "Parallels VirtIO Ethernet Adapter"
|
||||
},
|
||||
{
|
||||
"interface_index": 1,
|
||||
"mac_address": null,
|
||||
"description": "Software Loopback Interface 1"
|
||||
}
|
||||
],
|
||||
"ipv4_route_table": {
|
||||
"active_routes": [
|
||||
{
|
||||
"network_destination": "0.0.0.0",
|
||||
"netmask": "0.0.0.0",
|
||||
"gateway": "10.211.55.1",
|
||||
"interface": "10.211.55.3",
|
||||
"metric": 15,
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
{
|
||||
"network_destination": "10.0.0.0",
|
||||
"netmask": "255.0.0.0",
|
||||
"gateway": "192.168.22.1",
|
||||
"interface": "10.211.55.3",
|
||||
"metric": 16,
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
...
|
||||
{
|
||||
"network_destination": "255.255.255.255",
|
||||
"netmask": "255.255.255.255",
|
||||
"gateway": "On-link",
|
||||
"interface": "10.211.55.3",
|
||||
"metric": null,
|
||||
"metric_set_to_default": true
|
||||
}
|
||||
],
|
||||
"persistent_routes": [
|
||||
{
|
||||
"network_address": "10.0.1.0",
|
||||
"netmask": "255.255.255.0",
|
||||
"gateway_address": "192.168.22.1",
|
||||
"metric": 1,
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
{
|
||||
"network_address": "10.0.3.0",
|
||||
"netmask": "255.255.255.0",
|
||||
"gateway_address": "192.168.22.1",
|
||||
"metric": 1,
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
...
|
||||
]
|
||||
},
|
||||
"ipv6_route_table": {
|
||||
"active_routes": [
|
||||
{
|
||||
"interface": 1,
|
||||
"metric": 331,
|
||||
"network_destination": "::1/128",
|
||||
"gateway": "On-link",
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
{
|
||||
"interface": 12,
|
||||
"metric": 271,
|
||||
"network_destination": "2001:db8::/64",
|
||||
"gateway": "fe80::1",
|
||||
"metric_set_to_default": false
|
||||
},
|
||||
...
|
||||
{
|
||||
"interface": 12,
|
||||
"metric": 271,
|
||||
"network_destination": "ff00::/8",
|
||||
"gateway": "On-link",
|
||||
"metric_set_to_default": false
|
||||
}
|
||||
],
|
||||
"persistent_routes": [
|
||||
{
|
||||
"interface": 0,
|
||||
"metric": 4294967295,
|
||||
"network_destination": "2001:db8::/64",
|
||||
"gateway": "fe80::1",
|
||||
"metric_set_to_default": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"""
|
||||
import re
|
||||
import jc.utils
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`route print` command parser'
|
||||
author = 'joehacksalot'
|
||||
author_email = 'joehacksalot@gmail.com'
|
||||
details = 'See also: `route` command parser'
|
||||
compatible = ['win32']
|
||||
magic_commands = ['route print']
|
||||
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):
|
||||
raw_data = {
|
||||
"interface_list": [],
|
||||
"ipv4_route_table": {
|
||||
"active_routes": [],
|
||||
"persistent_routes": []
|
||||
},
|
||||
"ipv6_route_table": {
|
||||
"active_routes": [],
|
||||
"persistent_routes": []
|
||||
}
|
||||
}
|
||||
|
||||
lines = data.splitlines()
|
||||
_parse_interface_list(raw_data, _PushbackIterator(iter(lines)))
|
||||
_parse_ipv4_route_table(raw_data, _PushbackIterator(iter(lines)))
|
||||
_parse_ipv6_route_table(raw_data, _PushbackIterator(iter(lines)))
|
||||
raw_output = raw_data
|
||||
return raw_output if raw else _process(raw_output)
|
||||
|
||||
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.
|
||||
"""
|
||||
if not proc_data:
|
||||
return {}
|
||||
|
||||
for interface in proc_data['interface_list']:
|
||||
if interface["mac_address"] == '' or interface["mac_address"] == '00 00 00 00 00 00 00 e0': # Placeholder MAC address for virtual adapters
|
||||
mac_address = None
|
||||
else:
|
||||
mac_address = interface["mac_address"].replace(" ", ":")
|
||||
|
||||
interface["mac_address"] = mac_address
|
||||
|
||||
interface["interface_index"] = jc.utils.convert_to_int(interface["interface_index"])
|
||||
|
||||
for ipv4_active_route in proc_data['ipv4_route_table']['active_routes']:
|
||||
if ipv4_active_route["metric"] != "Default":
|
||||
ipv4_active_route["metric"] = jc.utils.convert_to_int(ipv4_active_route["metric"])
|
||||
ipv4_active_route["metric_set_to_default"] = False
|
||||
else:
|
||||
ipv4_active_route["metric"] = None
|
||||
ipv4_active_route["metric_set_to_default"] = True
|
||||
|
||||
for ipv4_persistent_route in proc_data['ipv4_route_table']['persistent_routes']:
|
||||
if ipv4_persistent_route["metric"] != "Default":
|
||||
ipv4_persistent_route["metric"] = jc.utils.convert_to_int(ipv4_persistent_route["metric"])
|
||||
ipv4_persistent_route["metric_set_to_default"] = False
|
||||
else:
|
||||
ipv4_persistent_route["metric"] = None
|
||||
ipv4_persistent_route["metric_set_to_default"] = True
|
||||
|
||||
for ipv6_active_route in proc_data['ipv6_route_table']['active_routes']:
|
||||
ipv6_active_route["interface"] = jc.utils.convert_to_int(ipv6_active_route["interface"])
|
||||
|
||||
if ipv6_active_route["metric"] != "Default":
|
||||
ipv6_active_route["metric"] = jc.utils.convert_to_int(ipv6_active_route["metric"])
|
||||
ipv6_active_route["metric_set_to_default"] = False
|
||||
else:
|
||||
ipv6_active_route["metric"] = None
|
||||
ipv6_active_route["metric_set_to_default"] = True
|
||||
|
||||
for ipv6_persistent_route in proc_data['ipv6_route_table']['persistent_routes']:
|
||||
ipv6_persistent_route["interface"] = jc.utils.convert_to_int(ipv6_persistent_route["interface"])
|
||||
|
||||
if ipv6_persistent_route["metric"] != "Default":
|
||||
ipv6_persistent_route["metric"] = jc.utils.convert_to_int(ipv6_persistent_route["metric"])
|
||||
ipv6_persistent_route["metric_set_to_default"] = False
|
||||
else:
|
||||
ipv6_persistent_route["metric"] = None
|
||||
ipv6_persistent_route["metric_set_to_default"] = True
|
||||
|
||||
return proc_data
|
||||
|
||||
|
||||
class _PushbackIterator:
|
||||
"""Iterator that allows pushing back values onto the iterator. Supports handing off
|
||||
parsing to localized parsers while maintaining line synchonization."""
|
||||
|
||||
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 contains(self, pattern):
|
||||
iter_lines = list(self.iterator)
|
||||
list_lines = self.pushback_stack.copy()
|
||||
list_lines.extend(iter_lines)
|
||||
self.iterator = iter(list_lines)
|
||||
self.pushback_stack = []
|
||||
|
||||
# Check the pushback stack first
|
||||
for line in list_lines:
|
||||
if re.match(pattern, line):
|
||||
return True
|
||||
return False
|
||||
|
||||
def skip_until(self, pattern):
|
||||
for line in self:
|
||||
if re.match(pattern, line):
|
||||
return line
|
||||
return None
|
||||
|
||||
|
||||
def _parse_interface_list(data, lines_iter):
|
||||
start_of_interface_list_pattern = r'^Interface List'
|
||||
if lines_iter.contains(start_of_interface_list_pattern):
|
||||
line = lines_iter.skip_until(start_of_interface_list_pattern)
|
||||
for line in lines_iter:
|
||||
if re.match(r'^=+$', line):
|
||||
break # End of interface list
|
||||
interface_index = line[:5].replace(".", "").strip()
|
||||
mac_address = line[5:30].replace(".","").strip()
|
||||
description = line[30:].strip()
|
||||
data['interface_list'].append({
|
||||
"interface_index": interface_index,
|
||||
"mac_address": mac_address,
|
||||
"description": description
|
||||
})
|
||||
|
||||
def _parse_ipv4_route_table(data, lines_iter):
|
||||
def _parse_ipv4_active_routes(data, lines_iter):
|
||||
line = lines_iter.skip_until(r'^Active Routes')
|
||||
line = next(lines_iter, '') # Skip the header line
|
||||
if line.strip() == 'None':
|
||||
return
|
||||
for line in lines_iter:
|
||||
if re.match(r'^=+$', line):
|
||||
break # End of interface list
|
||||
if 'Default Gateway' in line:
|
||||
continue
|
||||
lines_split = line.split()
|
||||
network_destination = lines_split[0]
|
||||
netmask = lines_split[1]
|
||||
gateway = lines_split[2]
|
||||
interface = lines_split[3]
|
||||
metric = lines_split[4]
|
||||
data['ipv4_route_table']["active_routes"].append({
|
||||
"network_destination": network_destination,
|
||||
"netmask": netmask,
|
||||
"gateway": gateway,
|
||||
"interface": interface,
|
||||
"metric": metric
|
||||
})
|
||||
|
||||
def _parse_ipv4_persistent_routes(data, lines_iter):
|
||||
line = lines_iter.skip_until(r'^Persistent Routes')
|
||||
line = next(lines_iter, '') # line is either "None" and we abort parsing this section or we skip header line
|
||||
if line.strip() == 'None':
|
||||
return
|
||||
for line in lines_iter:
|
||||
if re.match(r'^=+$', line):
|
||||
break
|
||||
lines_split = line.split()
|
||||
network_address = lines_split[0]
|
||||
netmask = lines_split[1]
|
||||
gateway_address = lines_split[2]
|
||||
metric = lines_split[3]
|
||||
data['ipv4_route_table']["persistent_routes"].append({
|
||||
"network_address": network_address,
|
||||
"netmask": netmask,
|
||||
"gateway_address": gateway_address,
|
||||
"metric": metric
|
||||
})
|
||||
|
||||
start_of_ipv4_route_table_pattern = r'^IPv4 Route Table'
|
||||
if lines_iter.contains(start_of_ipv4_route_table_pattern):
|
||||
line = lines_iter.skip_until(start_of_ipv4_route_table_pattern)
|
||||
line = next(lines_iter, '') # Skip the separator line
|
||||
_parse_ipv4_active_routes(data, lines_iter)
|
||||
_parse_ipv4_persistent_routes(data, lines_iter)
|
||||
|
||||
def _parse_ipv6_route_table(data, lines_iter):
|
||||
def _parse_ipv6_active_routes(data, lines_iter):
|
||||
line = lines_iter.skip_until(r'^Active Routes')
|
||||
line = next(lines_iter, '') # line is either "None" and we abort parsing this section or we skip header line
|
||||
if line.strip() == 'None':
|
||||
return
|
||||
for line in lines_iter:
|
||||
if re.match(r'^=+$', line):
|
||||
break
|
||||
split_line = line.split()
|
||||
interface = split_line[0]
|
||||
metric = split_line[1]
|
||||
network_destination = split_line[2]
|
||||
if len(split_line) > 3:
|
||||
gateway = split_line[3]
|
||||
else:
|
||||
gateway = next(lines_iter, '').strip()
|
||||
data['ipv6_route_table']["active_routes"].append({
|
||||
"interface": interface,
|
||||
"metric": metric,
|
||||
"network_destination": network_destination,
|
||||
"gateway": gateway
|
||||
})
|
||||
|
||||
def _parse_ipv6_persistent_routes(data, lines_iter):
|
||||
line = lines_iter.skip_until(r'^Persistent Routes')
|
||||
line = next(lines_iter, '') # line is either "None" and we abort parsing this section or we skip header line
|
||||
if line.strip() == 'None':
|
||||
return
|
||||
for line in lines_iter:
|
||||
if re.match(r'^=+$', line):
|
||||
break
|
||||
split_line = line.split()
|
||||
interface = split_line[0]
|
||||
metric = split_line[1]
|
||||
network_destination = split_line[2]
|
||||
if len(split_line) > 3:
|
||||
gateway = split_line[3]
|
||||
else:
|
||||
gateway = next(lines_iter, '').strip()
|
||||
data['ipv6_route_table']["persistent_routes"].append({
|
||||
"interface": interface,
|
||||
"metric": metric,
|
||||
"network_destination": network_destination,
|
||||
"gateway": gateway
|
||||
})
|
||||
|
||||
start_of_ipv6_route_table_pattern = r'^IPv6 Route Table'
|
||||
if lines_iter.contains(start_of_ipv6_route_table_pattern):
|
||||
line = lines_iter.skip_until(start_of_ipv6_route_table_pattern)
|
||||
line = next(lines_iter, '') # Skip the separator line
|
||||
_parse_ipv6_active_routes(data, lines_iter)
|
||||
_parse_ipv6_persistent_routes(data, lines_iter)
|
||||
@@ -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
|
||||
|
||||
@@ -132,7 +132,7 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.4'
|
||||
version = '1.5'
|
||||
description = '`/usr/bin/time` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -171,13 +171,18 @@ def _process(proc_data):
|
||||
proc_data['command_being_timed'] = proc_data['command_being_timed'][1:-1]
|
||||
|
||||
if 'elapsed_time' in proc_data:
|
||||
proc_data['elapsed_time'] = proc_data['elapsed_time'].replace('.', ':')
|
||||
*hours, minutes, seconds, centiseconds = proc_data['elapsed_time'].split(':')
|
||||
proc_data['elapsed_time'] = proc_data['elapsed_time'][::-1].replace(':', '.', 1)[::-1]
|
||||
*hours, minutes, ss = proc_data['elapsed_time'].split(':')
|
||||
if '.' in ss:
|
||||
seconds, centiseconds = ss.split('.')
|
||||
else:
|
||||
seconds = ss
|
||||
centiseconds = '0'
|
||||
|
||||
if hours:
|
||||
proc_data['elapsed_time_hours'] = jc.utils.convert_to_int(hours[0])
|
||||
else:
|
||||
proc_data['elapsed_time_hours'] = 0
|
||||
|
||||
proc_data['elapsed_time_minutes'] = jc.utils.convert_to_int(minutes)
|
||||
proc_data['elapsed_time_seconds'] = jc.utils.convert_to_int(seconds)
|
||||
proc_data['elapsed_time_centiseconds'] = jc.utils.convert_to_int(centiseconds)
|
||||
|
||||
@@ -45,23 +45,31 @@ All `-` values are converted to `null`
|
||||
"cpu_hardware": float,
|
||||
"cpu_software": float,
|
||||
"cpu_steal": float,
|
||||
"mem_total": float, # [0]
|
||||
"mem_free": float, # [0]
|
||||
"mem_used": float, # [0]
|
||||
"mem_buff_cache": float, # [0]
|
||||
"swap_total": float, # [0]
|
||||
"swap_free": float, # [0]
|
||||
"swap_used": float, # [0]
|
||||
"mem_available": float, # [0]
|
||||
"mem_unit": string,
|
||||
"mem_total": float,
|
||||
"mem_free": float,
|
||||
"mem_used": float,
|
||||
"mem_buff_cache": float,
|
||||
"swap_unit": string,
|
||||
"swap_total": float,
|
||||
"swap_free": float,
|
||||
"swap_used": float,
|
||||
"mem_available": float,
|
||||
"processes": [
|
||||
{
|
||||
"pid": integer,
|
||||
"user": string,
|
||||
"priority": integer,
|
||||
"nice": integer,
|
||||
"virtual_mem": float, # [1]
|
||||
"resident_mem": float, # [1]
|
||||
"shared_mem": float, # [1]
|
||||
"virtual_mem": float,
|
||||
"virtual_mem_bytes": integer,
|
||||
"virtual_mem_unit": string,
|
||||
"resident_mem": float,
|
||||
"resident_mem_bytes": integer,
|
||||
"resident_mem_unit": string,
|
||||
"shared_mem": float,
|
||||
"shared_mem_bytes": integer,
|
||||
"shared_mem_unit": string,
|
||||
"status": string,
|
||||
"percent_cpu": float,
|
||||
"percent_mem": float,
|
||||
@@ -82,9 +90,15 @@ All `-` values are converted to `null`
|
||||
"thread_count": integer,
|
||||
"last_used_processor": integer,
|
||||
"time": string,
|
||||
"swap": float, # [1]
|
||||
"code": float, # [1]
|
||||
"data": float, # [1]
|
||||
"swap": float,
|
||||
"swap_bytes": integer,
|
||||
"swap_unit": string,
|
||||
"code": float,
|
||||
"code_bytes": integer,
|
||||
"code_unit": string,
|
||||
"data": float,
|
||||
"data_bytes": integer,
|
||||
"data_unit": string,
|
||||
"major_page_fault_count": integer,
|
||||
"minor_page_fault_count": integer,
|
||||
"dirty_pages_count": integer,
|
||||
@@ -103,7 +117,9 @@ All `-` values are converted to `null`
|
||||
]
|
||||
"major_page_fault_count_delta": integer,
|
||||
"minor_page_fault_count_delta": integer,
|
||||
"used": float, # [1]
|
||||
"used": float,
|
||||
"used_bytes": integer,
|
||||
"used_unit": string,
|
||||
"ipc_namespace_inode": integer,
|
||||
"mount_namespace_inode": integer,
|
||||
"net_namespace_inode": integer,
|
||||
@@ -124,9 +140,6 @@ All `-` values are converted to `null`
|
||||
}
|
||||
]
|
||||
|
||||
[0] Values are in the units output by `top`
|
||||
[1] Unit suffix stripped during float conversion
|
||||
|
||||
Examples:
|
||||
|
||||
$ top -b -n 3 | jc --top -p
|
||||
@@ -316,7 +329,7 @@ from jc.parsers.universal import sparse_table_parse as parse_table
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.2'
|
||||
version = '1.3'
|
||||
description = '`top -b` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -440,13 +453,25 @@ def _process(proc_data: List[Dict], quiet=False) -> List[Dict]:
|
||||
'mem_available', 'virtual_mem', 'resident_mem', 'shared_mem', 'swap', 'code', 'data', 'used'
|
||||
}
|
||||
|
||||
bytes_list: Set = {
|
||||
'mem_total', 'mem_free', 'mem_used', 'mem_available', 'mem_buff_cache',
|
||||
'swap_total', 'swap_free', 'swap_used', 'virtual_mem', 'resident_mem',
|
||||
'shared_mem', 'swap', 'code', 'data', 'used'
|
||||
}
|
||||
|
||||
for idx, item in enumerate(proc_data):
|
||||
for key in item:
|
||||
for key in item.copy():
|
||||
# root truncation warnings
|
||||
if isinstance(item[key], str) and item[key].endswith('+') and not quiet:
|
||||
jc.utils.warning_message([f'item[{idx}]["{key}"] was truncated by top'])
|
||||
|
||||
# root int and float conversions
|
||||
if key in bytes_list:
|
||||
if key.startswith('mem_'):
|
||||
item[key + '_bytes'] = jc.utils.convert_size_to_int(item[key] + item['mem_unit'])
|
||||
if key.startswith('swap_'):
|
||||
item[key + '_bytes'] = jc.utils.convert_size_to_int(item[key] + item['swap_unit'])
|
||||
|
||||
if key in int_list:
|
||||
item[key] = jc.utils.convert_to_int(item[key])
|
||||
|
||||
@@ -463,7 +488,8 @@ def _process(proc_data: List[Dict], quiet=False) -> List[Dict]:
|
||||
jc.utils.warning_message([f'Unknown field detected at item[{idx}]["processes"]: {old_key}'])
|
||||
|
||||
# cleanup values
|
||||
for key in proc.keys():
|
||||
proc_copy = proc.copy()
|
||||
for key in proc_copy.keys():
|
||||
|
||||
# set dashes to nulls
|
||||
if proc[key] == '-':
|
||||
@@ -482,6 +508,13 @@ def _process(proc_data: List[Dict], quiet=False) -> List[Dict]:
|
||||
|
||||
# do int/float conversions for the process objects
|
||||
if proc[key]:
|
||||
if key in bytes_list:
|
||||
if proc[key][-1] not in ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'):
|
||||
proc[key + '_unit'] = proc[key][-1]
|
||||
else:
|
||||
proc[key + '_unit'] = 'b'
|
||||
proc[key + '_bytes'] = jc.utils.convert_size_to_int(proc[key], posix_mode=True)
|
||||
|
||||
if key in int_list:
|
||||
proc[key] = jc.utils.convert_to_int(proc[key])
|
||||
|
||||
@@ -604,6 +637,7 @@ def parse(
|
||||
line_list = line.split()
|
||||
item_obj.update(
|
||||
{
|
||||
'mem_unit': line_list[0],
|
||||
'mem_total': line_list[3],
|
||||
'mem_free': line_list[5],
|
||||
'mem_used': line_list[7],
|
||||
@@ -617,6 +651,7 @@ def parse(
|
||||
line_list = line.split()
|
||||
item_obj.update(
|
||||
{
|
||||
'swap_unit': line_list[0],
|
||||
'swap_total': line_list[2],
|
||||
'swap_free': line_list[4],
|
||||
'swap_used': line_list[6],
|
||||
|
||||
@@ -43,23 +43,39 @@ Schema:
|
||||
"cpu_hardware": float,
|
||||
"cpu_software": float,
|
||||
"cpu_steal": float,
|
||||
"mem_total": float, # [0]
|
||||
"mem_free": float, # [0]
|
||||
"mem_used": float, # [0]
|
||||
"mem_buff_cache": float, # [0]
|
||||
"swap_total": float, # [0]
|
||||
"swap_free": float, # [0]
|
||||
"swap_used": float, # [0]
|
||||
"mem_available": float, # [0]
|
||||
"mem_unit": string,
|
||||
"swap_unit": string,
|
||||
"mem_total": float,
|
||||
"mem_total_bytes": integer,
|
||||
"mem_free": float,
|
||||
"mem_free_bytes": integer,
|
||||
"mem_used": float,
|
||||
"mem_used_bytes": integer,
|
||||
"mem_buff_cache": float,
|
||||
"mem_buff_cache_bytes": integer,
|
||||
"swap_total": float,
|
||||
"swap_total_bytes": integer,
|
||||
"swap_free": float,
|
||||
"swap_free_bytes": integer,
|
||||
"swap_used": float,
|
||||
"swap_used_bytes": integer,
|
||||
"mem_available": float,
|
||||
"mem_available_bytes": integer,
|
||||
"processes": [
|
||||
{
|
||||
"pid": integer,
|
||||
"user": string,
|
||||
"priority": integer,
|
||||
"nice": integer,
|
||||
"virtual_mem": float, # [1]
|
||||
"resident_mem": float, # [1]
|
||||
"shared_mem": float, # [1]
|
||||
"virtual_mem": float,
|
||||
"virtual_mem_unit": string,
|
||||
"virtual_mem_bytes": integer,
|
||||
"resident_mem": float,
|
||||
"resident_mem_unit": string,
|
||||
"resident_mem_bytes": integer,
|
||||
"shared_mem": float,
|
||||
"shared_mem_unit": string,
|
||||
"shared_mem_bytes": integer,
|
||||
"status": string,
|
||||
"percent_cpu": float,
|
||||
"percent_mem": float,
|
||||
@@ -80,9 +96,15 @@ Schema:
|
||||
"thread_count": integer,
|
||||
"last_used_processor": integer,
|
||||
"time": string,
|
||||
"swap": float, # [1]
|
||||
"code": float, # [1]
|
||||
"data": float, # [1]
|
||||
"swap": float,
|
||||
"swap_unit": string,
|
||||
"swap_bytes": integer,
|
||||
"code": float,
|
||||
"code_unit": string,
|
||||
"code_bytes": integer
|
||||
"data": float,
|
||||
"data_unit": string,
|
||||
"data_bytes": integer,
|
||||
"major_page_fault_count": integer,
|
||||
"minor_page_fault_count": integer,
|
||||
"dirty_pages_count": integer,
|
||||
@@ -101,7 +123,9 @@ Schema:
|
||||
]
|
||||
"major_page_fault_count_delta": integer,
|
||||
"minor_page_fault_count_delta": integer,
|
||||
"used": float, # [1]
|
||||
"used": float,
|
||||
"used_unit": string,
|
||||
"used_bytes": integer,
|
||||
"ipc_namespace_inode": integer,
|
||||
"mount_namespace_inode": integer,
|
||||
"net_namespace_inode": integer,
|
||||
@@ -128,9 +152,6 @@ Schema:
|
||||
}
|
||||
}
|
||||
|
||||
[0] Values are in the units output by `top`
|
||||
[1] Unit suffix stripped during float conversion
|
||||
|
||||
Examples:
|
||||
|
||||
$ top -b | jc --top-s
|
||||
@@ -153,7 +174,7 @@ from jc.parsers.universal import sparse_table_parse as parse_table
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.2'
|
||||
version = '1.3'
|
||||
description = '`top -b` command streaming parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -277,12 +298,24 @@ def _process(proc_data: Dict, idx=0, quiet=False) -> Dict:
|
||||
'mem_available', 'virtual_mem', 'resident_mem', 'shared_mem', 'swap', 'code', 'data', 'used'
|
||||
}
|
||||
|
||||
for key in proc_data:
|
||||
bytes_list: Set = {
|
||||
'mem_total', 'mem_free', 'mem_used', 'mem_available', 'mem_buff_cache',
|
||||
'swap_total', 'swap_free', 'swap_used', 'virtual_mem', 'resident_mem',
|
||||
'shared_mem', 'swap', 'code', 'data', 'used'
|
||||
}
|
||||
|
||||
for key in proc_data.copy():
|
||||
# root truncation warnings
|
||||
if isinstance(proc_data[key], str) and proc_data[key].endswith('+') and not quiet:
|
||||
jc.utils.warning_message([f'item[{idx}]["{key}"] was truncated by top'])
|
||||
|
||||
# root int and float conversions
|
||||
if key in bytes_list:
|
||||
if key.startswith('mem_'):
|
||||
proc_data[key + '_bytes'] = jc.utils.convert_size_to_int(proc_data[key] + proc_data['mem_unit'])
|
||||
if key.startswith('swap_'):
|
||||
proc_data[key + '_bytes'] = jc.utils.convert_size_to_int(proc_data[key] + proc_data['swap_unit'])
|
||||
|
||||
if key in int_list:
|
||||
proc_data[key] = jc.utils.convert_to_int(proc_data[key])
|
||||
|
||||
@@ -299,7 +332,8 @@ def _process(proc_data: Dict, idx=0, quiet=False) -> Dict:
|
||||
jc.utils.warning_message([f'Unknown field detected at item[{idx}]["processes"]: {old_key}'])
|
||||
|
||||
# cleanup values
|
||||
for key in proc.keys():
|
||||
proc_copy = proc.copy()
|
||||
for key in proc_copy.keys():
|
||||
|
||||
# set dashes to nulls
|
||||
if proc[key] == '-':
|
||||
@@ -318,6 +352,13 @@ def _process(proc_data: Dict, idx=0, quiet=False) -> Dict:
|
||||
|
||||
# do int/float conversions for the process objects
|
||||
if proc[key]:
|
||||
if key in bytes_list:
|
||||
if proc[key][-1] not in ('0', '1', '2', '3', '4', '5', '6', '7', '8', '9'):
|
||||
proc[key + '_unit'] = proc[key][-1]
|
||||
else:
|
||||
proc[key + '_unit'] = 'b'
|
||||
proc[key + '_bytes'] = jc.utils.convert_size_to_int(proc[key], posix_mode=True)
|
||||
|
||||
if key in int_list:
|
||||
proc[key] = jc.utils.convert_to_int(proc[key])
|
||||
|
||||
@@ -448,6 +489,7 @@ def parse(
|
||||
line_list = line.split()
|
||||
output_line.update(
|
||||
{
|
||||
'mem_unit': line_list[0],
|
||||
'mem_total': line_list[3],
|
||||
'mem_free': line_list[5],
|
||||
'mem_used': line_list[7],
|
||||
@@ -461,6 +503,7 @@ def parse(
|
||||
line_list = line.split()
|
||||
output_line.update(
|
||||
{
|
||||
'swap_unit': line_list[0],
|
||||
'swap_total': line_list[2],
|
||||
'swap_free': line_list[4],
|
||||
'swap_used': line_list[6],
|
||||
@@ -489,3 +532,5 @@ def parse(
|
||||
if process_list:
|
||||
output_line['processes'] = parse_table(process_list)
|
||||
yield output_line if raw else _process(output_line, idx=idx, quiet=quiet)
|
||||
|
||||
return None
|
||||
|
||||
@@ -27,6 +27,8 @@ Schema:
|
||||
{
|
||||
"destination_ip": string,
|
||||
"destination_name": string,
|
||||
"max_hops": integer,
|
||||
"data_bytes": integer,
|
||||
"hops": [
|
||||
{
|
||||
"hop": integer,
|
||||
@@ -49,6 +51,8 @@ Examples:
|
||||
{
|
||||
"destination_ip": "216.58.194.46",
|
||||
"destination_name": "google.com",
|
||||
"max_hops": 64,
|
||||
"data_bytes": 50,
|
||||
"hops": [
|
||||
{
|
||||
"hop": 1,
|
||||
@@ -84,6 +88,8 @@ Examples:
|
||||
{
|
||||
"destination_ip": "216.58.194.46",
|
||||
"destination_name": "google.com",
|
||||
"max_hops": "64",
|
||||
"data_bytes": "50",
|
||||
"hops": [
|
||||
{
|
||||
"hop": "1",
|
||||
@@ -119,11 +125,12 @@ import re
|
||||
from decimal import Decimal
|
||||
import jc.utils
|
||||
from copy import deepcopy
|
||||
from jc.exceptions import ParseError
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.7'
|
||||
version = '1.9'
|
||||
description = '`traceroute` and `traceroute6` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -164,10 +171,11 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
'''
|
||||
|
||||
RE_HEADER = re.compile(r'(\S+)\s+\((\d+\.\d+\.\d+\.\d+|[0-9a-fA-F:]+)\)')
|
||||
RE_HEADER = re.compile(r'traceroute6? to (\S+)\s+\((\d+\.\d+\.\d+\.\d+|[0-9a-fA-F:]+)\)')
|
||||
RE_HEADER_HOPS_BYTES = re.compile(r'(\d+) hops max, (\d+) byte packets')
|
||||
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+)\]')
|
||||
@@ -175,9 +183,11 @@ RE_PROBE_RTT_ANNOTATION = re.compile(r'(?:(\d+(?:\.?\d+)?)\s+ms|(\s+\*\s+))\s*(!
|
||||
|
||||
|
||||
class _Traceroute(object):
|
||||
def __init__(self, dest_name, dest_ip):
|
||||
def __init__(self, dest_name, dest_ip, max_hops=None, data_bytes=None):
|
||||
self.dest_name = dest_name
|
||||
self.dest_ip = dest_ip
|
||||
self.max_hops = max_hops
|
||||
self.data_bytes = data_bytes
|
||||
self.hops = []
|
||||
|
||||
def add_hop(self, hop):
|
||||
@@ -291,8 +301,23 @@ def _get_probes(hop_string: str):
|
||||
return probes
|
||||
|
||||
|
||||
def _loads(data):
|
||||
lines = data.splitlines()
|
||||
def _loads(data: str, quiet: bool):
|
||||
lines = []
|
||||
|
||||
# remove any warning lines
|
||||
for data_line in data.splitlines():
|
||||
if 'traceroute: Warning: ' not in data_line and 'traceroute6: Warning: ' not in data_line:
|
||||
lines.append(data_line)
|
||||
else:
|
||||
continue
|
||||
|
||||
# check if header row exists, otherwise add a dummy header
|
||||
if not lines[0].startswith('traceroute to ') and not lines[0].startswith('traceroute6 to '):
|
||||
lines[:0] = ['traceroute to <<_>> (<<_>>), ? hops max, ? byte packets']
|
||||
|
||||
# print warning to STDERR
|
||||
if not quiet:
|
||||
jc.utils.warning_message(['No header row found. For destination info redirect STDERR to STDOUT'])
|
||||
|
||||
# Get headers
|
||||
match_dest = RE_HEADER.search(lines[0])
|
||||
@@ -301,8 +326,15 @@ def _loads(data):
|
||||
dest_name = match_dest.group(1)
|
||||
dest_ip = match_dest.group(2)
|
||||
|
||||
m = RE_HEADER_HOPS_BYTES.search(lines[0])
|
||||
max_hops = None
|
||||
data_bytes = None
|
||||
if m:
|
||||
max_hops = m.group(1)
|
||||
data_bytes = m.group(2)
|
||||
|
||||
# The Traceroute node is the root of the tree
|
||||
traceroute = _Traceroute(dest_name, dest_ip)
|
||||
traceroute = _Traceroute(dest_name, dest_ip, max_hops, data_bytes)
|
||||
|
||||
# Parse the remaining lines, they should be only hops/probes
|
||||
for line in lines[1:]:
|
||||
@@ -312,16 +344,17 @@ def _loads(data):
|
||||
|
||||
hop_match = RE_HOP.match(line)
|
||||
|
||||
if hop_match.group(1):
|
||||
hop_index = int(hop_match.group(1))
|
||||
else:
|
||||
hop_index = None
|
||||
if hop_match:
|
||||
if hop_match.group(1):
|
||||
hop_index = int(hop_match.group(1))
|
||||
else:
|
||||
hop_index = None
|
||||
|
||||
if hop_index is not None:
|
||||
hop = _Hop(hop_index)
|
||||
traceroute.add_hop(hop)
|
||||
if hop_index is not None:
|
||||
hop = _Hop(hop_index)
|
||||
traceroute.add_hop(hop)
|
||||
|
||||
hop_string = hop_match.group(2)
|
||||
hop_string = hop_match.group(2)
|
||||
|
||||
probes = _get_probes(hop_string)
|
||||
for probe in probes:
|
||||
@@ -330,11 +363,25 @@ def _loads(data):
|
||||
return traceroute
|
||||
|
||||
|
||||
class ParseError(Exception):
|
||||
pass
|
||||
def _serialize_hop(hop: _Hop):
|
||||
hop_obj = {}
|
||||
hop_obj['hop'] = str(hop.idx)
|
||||
probe_list = []
|
||||
|
||||
if hop.probes:
|
||||
for probe in hop.probes:
|
||||
probe_obj = {
|
||||
'annotation': probe.annotation,
|
||||
'asn': None if probe.asn is None else str(probe.asn),
|
||||
'ip': probe.ip,
|
||||
'name': probe.name,
|
||||
'rtt': None if probe.rtt is None else str(probe.rtt)
|
||||
}
|
||||
probe_list.append(probe_obj)
|
||||
|
||||
########################################################################################
|
||||
hop_obj['probes'] = probe_list
|
||||
|
||||
return hop_obj
|
||||
|
||||
|
||||
def _process(proc_data):
|
||||
@@ -349,26 +396,20 @@ def _process(proc_data):
|
||||
|
||||
Dictionary. Structured to conform to the schema.
|
||||
"""
|
||||
int_list = {'hop', 'asn'}
|
||||
int_list = {'hop', 'asn', 'max_hops', 'data_bytes'}
|
||||
float_list = {'rtt'}
|
||||
|
||||
if 'hops' in proc_data:
|
||||
for entry in proc_data['hops']:
|
||||
for key in entry:
|
||||
if key in int_list:
|
||||
entry[key] = jc.utils.convert_to_int(entry[key])
|
||||
for entry in proc_data.get('hops', []):
|
||||
_process(entry)
|
||||
for entry in proc_data.get('probes', []):
|
||||
_process(entry)
|
||||
|
||||
if key in float_list:
|
||||
entry[key] = jc.utils.convert_to_float(entry[key])
|
||||
for key in proc_data:
|
||||
if key in int_list:
|
||||
proc_data[key] = jc.utils.convert_to_int(proc_data[key])
|
||||
|
||||
if 'probes' in entry:
|
||||
for item in entry['probes']:
|
||||
for key in item:
|
||||
if key in int_list:
|
||||
item[key] = jc.utils.convert_to_int(item[key])
|
||||
|
||||
if key in float_list:
|
||||
item[key] = jc.utils.convert_to_float(item[key])
|
||||
if key in float_list:
|
||||
proc_data[key] = jc.utils.convert_to_float(proc_data[key])
|
||||
|
||||
return proc_data
|
||||
|
||||
@@ -393,52 +434,17 @@ def parse(data, raw=False, quiet=False):
|
||||
raw_output = {}
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
# remove any warning lines
|
||||
new_data = []
|
||||
for data_line in data.splitlines():
|
||||
if 'traceroute: Warning: ' not in data_line and 'traceroute6: Warning: ' not in data_line:
|
||||
new_data.append(data_line)
|
||||
else:
|
||||
continue
|
||||
|
||||
# check if header row exists, otherwise add a dummy header
|
||||
if not new_data[0].startswith('traceroute to ') and not new_data[0].startswith('traceroute6 to '):
|
||||
new_data[:0] = ['traceroute to <<_>> (<<_>>), 30 hops max, 60 byte packets']
|
||||
|
||||
# print warning to STDERR
|
||||
if not quiet:
|
||||
jc.utils.warning_message(['No header row found. For destination info redirect STDERR to STDOUT'])
|
||||
|
||||
data = '\n'.join(new_data)
|
||||
|
||||
tr = _loads(data)
|
||||
hops = tr.hops
|
||||
tr = _loads(data, quiet)
|
||||
hops_list = []
|
||||
|
||||
if hops:
|
||||
for hop in hops:
|
||||
hop_obj = {}
|
||||
hop_obj['hop'] = str(hop.idx)
|
||||
probe_list = []
|
||||
|
||||
if hop.probes:
|
||||
for probe in hop.probes:
|
||||
probe_obj = {
|
||||
'annotation': probe.annotation,
|
||||
'asn': None if probe.asn is None else str(probe.asn),
|
||||
'ip': probe.ip,
|
||||
'name': probe.name,
|
||||
'rtt': None if probe.rtt is None else str(probe.rtt)
|
||||
}
|
||||
probe_list.append(probe_obj)
|
||||
|
||||
hop_obj['probes'] = probe_list
|
||||
hops_list.append(hop_obj)
|
||||
for hop in tr.hops:
|
||||
hops_list.append(_serialize_hop(hop))
|
||||
|
||||
raw_output = {
|
||||
'destination_ip': tr.dest_ip,
|
||||
'destination_name': tr.dest_name,
|
||||
'max_hops': tr.max_hops,
|
||||
'data_bytes': tr.data_bytes,
|
||||
'hops': hops_list
|
||||
}
|
||||
|
||||
|
||||
277
jc/parsers/traceroute_s.py
Normal file
277
jc/parsers/traceroute_s.py
Normal file
@@ -0,0 +1,277 @@
|
||||
r"""jc - JSON Convert `traceroute` command output streaming parser
|
||||
|
||||
> This streaming parser outputs JSON Lines (cli) or returns an Iterable of
|
||||
> Dictionaries (module)
|
||||
|
||||
Supports `traceroute` and `traceroute6` output.
|
||||
|
||||
> Note: On some operating systems you will need to redirect `STDERR` to
|
||||
> `STDOUT` for destination info since the header line is sent to
|
||||
> `STDERR`. A warning message will be printed to `STDERR` if the
|
||||
> header row is not found.
|
||||
>
|
||||
> e.g. `$ traceroute 8.8.8.8 2>&1 | jc --traceroute-s`
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ traceroute 1.2.3.4 | jc --traceroute-s
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('traceroute_s', traceroute_command_output.splitlines())
|
||||
for item in result:
|
||||
# do something
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
# 'header' or 'hop'
|
||||
"type": string,
|
||||
|
||||
# 'header' type has the fields below:
|
||||
"destination_ip": string,
|
||||
"destination_name": string,
|
||||
"max_hops": integer,
|
||||
"data_bytes": integer,
|
||||
|
||||
# 'hop' type has the fields below:
|
||||
"hop": integer,
|
||||
"probes": [
|
||||
{
|
||||
"annotation": string,
|
||||
"asn": integer,
|
||||
"ip": string,
|
||||
"name": string,
|
||||
"rtt": float
|
||||
}
|
||||
]
|
||||
|
||||
# below object only exists if using -qq or ignore_exceptions=True
|
||||
"_jc_meta": {
|
||||
"success": boolean, # false if error parsing
|
||||
"error": string, # exists if "success" is false
|
||||
"line": string # exists if "success" is false
|
||||
}
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ traceroute google.com | jc --traceroute-s -p
|
||||
{
|
||||
"type": "header",
|
||||
"destination_ip": "216.58.194.46",
|
||||
"destination_name": "google.com",
|
||||
"max_hops": 30,
|
||||
"data_bytes": 60
|
||||
}
|
||||
{
|
||||
"type": "hop",
|
||||
"hop": 1,
|
||||
"probes": [
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": 198.574
|
||||
},
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": null
|
||||
},
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": 198.65
|
||||
}
|
||||
]
|
||||
}
|
||||
...
|
||||
|
||||
$ traceroute google.com | jc --traceroute-s -p -r
|
||||
{
|
||||
"type": "header",
|
||||
"destination_ip": "216.58.194.46",
|
||||
"destination_name": "google.com",
|
||||
"max_hops": "30",
|
||||
"data_bytes": "60"
|
||||
}
|
||||
{
|
||||
"type": "hop",
|
||||
"hop": "1",
|
||||
"probes": [
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": "198.574"
|
||||
},
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": null
|
||||
},
|
||||
{
|
||||
"annotation": null,
|
||||
"asn": null,
|
||||
"ip": "216.230.231.141",
|
||||
"name": "216-230-231-141.static.houston.tx.oplink.net",
|
||||
"rtt": "198.650"
|
||||
}
|
||||
]
|
||||
}
|
||||
...
|
||||
"""
|
||||
from typing import Optional
|
||||
|
||||
import jc.utils
|
||||
from jc.exceptions import ParseError
|
||||
from jc.streaming import (
|
||||
add_jc_meta, streaming_input_type_check, streaming_line_input_type_check, raise_or_yield
|
||||
)
|
||||
from .traceroute import RE_HEADER, RE_HOP, RE_HEADER_HOPS_BYTES, _Hop, _loads, _process, _serialize_hop
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = '`traceroute` and `traceroute6` command streaming parser'
|
||||
author = 'Shintaro Kojima'
|
||||
author_email = 'goodies@codeout.net'
|
||||
compatible = ['linux', 'darwin', 'freebsd']
|
||||
tags = ['command']
|
||||
streaming = True
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
'''
|
||||
Copyright (C) 2015 Luis Benitez
|
||||
|
||||
Parses the output of a traceroute execution into an AST (Abstract Syntax Tree).
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Luis Benitez
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE 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 _hop_output(hop: _Hop, raw: bool):
|
||||
raw_output = {
|
||||
'type': 'hop',
|
||||
**_serialize_hop(hop),
|
||||
}
|
||||
|
||||
return raw_output if raw else _process(raw_output)
|
||||
|
||||
|
||||
@add_jc_meta
|
||||
def parse(data, raw=False, quiet=False, ignore_exceptions=False):
|
||||
"""
|
||||
Main text parsing function. Returns an iterable object.
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (iterable) line-based text data to parse
|
||||
(e.g. sys.stdin or str.splitlines())
|
||||
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
ignore_exceptions: (boolean) ignore parsing exceptions if True
|
||||
|
||||
Returns:
|
||||
|
||||
Iterable of Dictionaries
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
streaming_input_type_check(data)
|
||||
|
||||
# Estimated number of probe packets per hop. See `traceroute -q` on Linux, for example.
|
||||
queries = 0
|
||||
# Accumulated hop across multiple lines
|
||||
hop_cache: Optional[_Hop] = None
|
||||
|
||||
for line in data: # type: str
|
||||
try:
|
||||
streaming_line_input_type_check(line)
|
||||
|
||||
if RE_HEADER.search(line):
|
||||
tr = _loads(line, quiet)
|
||||
raw_output = {
|
||||
'type': 'header',
|
||||
'destination_ip': tr.dest_ip,
|
||||
'destination_name': tr.dest_name,
|
||||
'max_hops': tr.max_hops,
|
||||
'data_bytes': tr.data_bytes
|
||||
}
|
||||
|
||||
yield raw_output if raw else _process(raw_output)
|
||||
|
||||
else:
|
||||
m = RE_HOP.match(line)
|
||||
if not m:
|
||||
continue
|
||||
|
||||
# A single hop can wrap across multiple lines, e.g.:
|
||||
#
|
||||
# 6 [AS0] 94.142.122.45 (94.142.122.45) 42.790 ms 46.352 ms
|
||||
# [AS0] 94.142.122.44 (94.142.122.44) 41.479 ms
|
||||
#
|
||||
if not m.group(1):
|
||||
if not hop_cache:
|
||||
raise ParseError('No hop index found')
|
||||
|
||||
# If the hop index is not found, prepend the hop index (6) to the following lines before parsing.
|
||||
line = f"{hop_cache.idx} {line}"
|
||||
# Specify quiet=True to suppress the 'No header row found' warning for hop lines
|
||||
tr = _loads(line, quiet=True)
|
||||
if not tr.hops:
|
||||
continue
|
||||
|
||||
hop_cache.probes.extend(tr.hops[0].probes)
|
||||
|
||||
else:
|
||||
# if the hop index is found, yield the previous hop
|
||||
if hop_cache:
|
||||
yield _hop_output(hop_cache, raw)
|
||||
hop_cache = None
|
||||
|
||||
# Specify quiet=True to suppress the 'No header row found' warning for hop lines
|
||||
tr = _loads(line, quiet=True)
|
||||
if not tr.hops:
|
||||
continue
|
||||
|
||||
hop_cache = tr.hops[0]
|
||||
|
||||
except Exception as e:
|
||||
yield raise_or_yield(ignore_exceptions, e, line)
|
||||
|
||||
if hop_cache:
|
||||
yield _hop_output(hop_cache, raw)
|
||||
@@ -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
273
jc/parsers/wg_show.py
Normal 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)
|
||||
@@ -25,6 +25,7 @@ Schema:
|
||||
"user": string,
|
||||
"event": string,
|
||||
"writeable_tty": string,
|
||||
"process": string,
|
||||
"tty": string,
|
||||
"time": string,
|
||||
"epoch": integer, # [0]
|
||||
@@ -136,7 +137,7 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.8'
|
||||
version = '1.9'
|
||||
description = '`who` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@@ -254,6 +255,12 @@ def parse(data, raw=False, quiet=False):
|
||||
raw_output.append(output_line)
|
||||
continue
|
||||
|
||||
# some output contains process name between the username and pts
|
||||
# pull the process name for use later if we can find it
|
||||
user_process = None
|
||||
if re.match(r'^\S+\s+[^ +-]+\s+pts\/\d+\s', line):
|
||||
user_process = linedata.pop(1)
|
||||
|
||||
# user logins
|
||||
output_line['user'] = linedata.pop(0)
|
||||
|
||||
@@ -262,6 +269,9 @@ def parse(data, raw=False, quiet=False):
|
||||
|
||||
output_line['tty'] = linedata.pop(0)
|
||||
|
||||
if user_process:
|
||||
output_line['process'] = user_process
|
||||
|
||||
# mac
|
||||
if re.match(r'[JFMASOND][aepuco][nbrynlgptvc]', linedata[0]):
|
||||
output_line['time'] = ' '.join([linedata.pop(0),
|
||||
|
||||
@@ -413,7 +413,7 @@ from jc.parsers.asn1crypto import pem, x509, jc_global
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.3'
|
||||
version = '1.4'
|
||||
description = 'X.509 PEM and DER certificate file parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
|
||||
333
jc/parsers/x509_crl.py
Normal file
333
jc/parsers/x509_crl.py
Normal file
@@ -0,0 +1,333 @@
|
||||
r"""jc - JSON Convert X.509 Certificate Revocation List format file parser
|
||||
|
||||
This parser will convert DER and PEM encoded X.509 certificate revocation
|
||||
list files.
|
||||
|
||||
Usage (cli):
|
||||
|
||||
$ cat certificateRevocationList.pem | jc --x509-crl
|
||||
$ cat certificateRevocationList.der | jc --x509-crl
|
||||
|
||||
Usage (module):
|
||||
|
||||
import jc
|
||||
result = jc.parse('x509_crl', x509_crl_file_output)
|
||||
|
||||
Schema:
|
||||
|
||||
{
|
||||
"tbs_cert_list": {
|
||||
"version": string,
|
||||
"signature": {
|
||||
"algorithm": string,
|
||||
"parameters": string/null
|
||||
},
|
||||
"issuer": {
|
||||
"organization_name": string,
|
||||
"organizational_unit_name": string,
|
||||
"common_name": string
|
||||
},
|
||||
"this_update": integer, # [1]
|
||||
"next_update": integer, # [1]
|
||||
"revoked_certificates": [
|
||||
{
|
||||
"user_certificate": integer,
|
||||
"revocation_date": integer, # [1]
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": string,
|
||||
"critical": boolean,
|
||||
"extn_value": string,
|
||||
"extn_value_iso": string
|
||||
},
|
||||
"revocation_date_iso": string
|
||||
}
|
||||
],
|
||||
"crl_extensions": [
|
||||
{
|
||||
"extn_id": string,
|
||||
"critical": boolean,
|
||||
"extn_value": array/object/string/integer # [2]
|
||||
}
|
||||
],
|
||||
"this_update_iso": string,
|
||||
"next_update_iso": string
|
||||
},
|
||||
"signature_algorithm": {
|
||||
"algorithm": string,
|
||||
"parameters": string/null
|
||||
},
|
||||
"signature": string # [0]
|
||||
}
|
||||
|
||||
[0] in colon-delimited hex notation
|
||||
[1] time-zone-aware (UTC) epoch timestamp
|
||||
[2] See below for well-known Extension schemas:
|
||||
|
||||
Basic Constraints:
|
||||
{
|
||||
"extn_id": "basic_constraints",
|
||||
"critical": boolean,
|
||||
"extn_value": {
|
||||
"ca": boolean,
|
||||
"path_len_constraint": string/null
|
||||
}
|
||||
}
|
||||
|
||||
Key Usage:
|
||||
{
|
||||
"extn_id": "key_usage",
|
||||
"critical": boolean,
|
||||
"extn_value": [
|
||||
string
|
||||
]
|
||||
}
|
||||
|
||||
Key Identifier:
|
||||
{
|
||||
"extn_id": "key_identifier",
|
||||
"critical": boolean,
|
||||
"extn_value": string # [0]
|
||||
}
|
||||
|
||||
Authority Key Identifier:
|
||||
{
|
||||
"extn_id": "authority_key_identifier",
|
||||
"critical": boolean,
|
||||
"extn_value": {
|
||||
"key_identifier": string, # [0]
|
||||
"authority_cert_issuer": string/null,
|
||||
"authority_cert_serial_number": string/null
|
||||
}
|
||||
}
|
||||
|
||||
Subject Alternative Name:
|
||||
{
|
||||
"extn_id": "subject_alt_name",
|
||||
"critical": boolean,
|
||||
"extn_value": [
|
||||
string
|
||||
]
|
||||
}
|
||||
|
||||
Certificate Policies:
|
||||
{
|
||||
"extn_id": "certificate_policies",
|
||||
"critical": boolean,
|
||||
"extn_value": [
|
||||
{
|
||||
"policy_identifier": string,
|
||||
"policy_qualifiers": [ array or null
|
||||
{
|
||||
"policy_qualifier_id": string,
|
||||
"qualifier": string
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Signed Certificate Timestamp List:
|
||||
{
|
||||
"extn_id": "signed_certificate_timestamp_list",
|
||||
"critical": boolean,
|
||||
"extn_value": string # [0]
|
||||
}
|
||||
|
||||
Examples:
|
||||
|
||||
$ cat sample-crl.pem | jc --x509-crl -p
|
||||
{
|
||||
"tbs_cert_list": {
|
||||
"version": "v2",
|
||||
"signature": {
|
||||
"algorithm": "sha1_rsa",
|
||||
"parameters": null
|
||||
},
|
||||
"issuer": {
|
||||
"organization_name": "Sample Signer Organization",
|
||||
"organizational_unit_name": "Sample Signer Unit",
|
||||
"common_name": "Sample Signer Cert"
|
||||
},
|
||||
"this_update": 1361183520,
|
||||
"next_update": 1361184120,
|
||||
"revoked_certificates": [
|
||||
{
|
||||
"user_certificate": 1341767,
|
||||
"revocation_date": 1361182932,
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": "crl_reason",
|
||||
"critical": false,
|
||||
"extn_value": "affiliation_changed"
|
||||
},
|
||||
{
|
||||
"extn_id": "invalidity_date",
|
||||
"critical": false,
|
||||
"extn_value": 1361182920,
|
||||
"extn_value_iso": "2013-02-18T10:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revocation_date_iso": "2013-02-18T10:22:12+00:00"
|
||||
},
|
||||
{
|
||||
"user_certificate": 1341768,
|
||||
"revocation_date": 1361182942,
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": "crl_reason",
|
||||
"critical": false,
|
||||
"extn_value": "certificate_hold"
|
||||
},
|
||||
{
|
||||
"extn_id": "invalidity_date",
|
||||
"critical": false,
|
||||
"extn_value": 1361182920,
|
||||
"extn_value_iso": "2013-02-18T10:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revocation_date_iso": "2013-02-18T10:22:22+00:00"
|
||||
},
|
||||
{
|
||||
"user_certificate": 1341769,
|
||||
"revocation_date": 1361182952,
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": "crl_reason",
|
||||
"critical": false,
|
||||
"extn_value": "superseded"
|
||||
},
|
||||
{
|
||||
"extn_id": "invalidity_date",
|
||||
"critical": false,
|
||||
"extn_value": 1361182920,
|
||||
"extn_value_iso": "2013-02-18T10:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revocation_date_iso": "2013-02-18T10:22:32+00:00"
|
||||
},
|
||||
{
|
||||
"user_certificate": 1341770,
|
||||
"revocation_date": 1361182962,
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": "crl_reason",
|
||||
"critical": false,
|
||||
"extn_value": "key_compromise"
|
||||
},
|
||||
{
|
||||
"extn_id": "invalidity_date",
|
||||
"critical": false,
|
||||
"extn_value": 1361182920,
|
||||
"extn_value_iso": "2013-02-18T10:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revocation_date_iso": "2013-02-18T10:22:42+00:00"
|
||||
},
|
||||
{
|
||||
"user_certificate": 1341771,
|
||||
"revocation_date": 1361182971,
|
||||
"crl_entry_extensions": [
|
||||
{
|
||||
"extn_id": "crl_reason",
|
||||
"critical": false,
|
||||
"extn_value": "cessation_of_operation"
|
||||
},
|
||||
{
|
||||
"extn_id": "invalidity_date",
|
||||
"critical": false,
|
||||
"extn_value": 1361182920,
|
||||
"extn_value_iso": "2013-02-18T10:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revocation_date_iso": "2013-02-18T10:22:51+00:00"
|
||||
}
|
||||
],
|
||||
"crl_extensions": [
|
||||
{
|
||||
"extn_id": "authority_key_identifier",
|
||||
"critical": false,
|
||||
"extn_value": {
|
||||
"key_identifier": "be:12:01:cc:aa:ea:11:80:da:2e:ad:b2...",
|
||||
"authority_cert_issuer": null,
|
||||
"authority_cert_serial_number": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"extn_id": "crl_number",
|
||||
"critical": false,
|
||||
"extn_value": 3
|
||||
}
|
||||
],
|
||||
"this_update_iso": "2013-02-18T10:32:00+00:00",
|
||||
"next_update_iso": "2013-02-18T10:42:00+00:00"
|
||||
},
|
||||
"signature_algorithm": {
|
||||
"algorithm": "sha1_rsa",
|
||||
"parameters": null
|
||||
},
|
||||
"signature": "42:21:be:81:f1:c3:79:76:66:5b:ce:21:13:8a:68:a..."
|
||||
}
|
||||
"""
|
||||
from typing import List, Dict, Union
|
||||
import jc.utils
|
||||
from jc.parsers.asn1crypto import pem, crl, jc_global
|
||||
from jc.parsers.x509_cert import _fix_objects
|
||||
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
description = 'X.509 PEM and DER certificate revocation list file parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
details = 'Using the asn1crypto library at https://github.com/wbond/asn1crypto/releases/tag/1.5.1'
|
||||
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
|
||||
tags = ['standard', 'file', 'string', 'binary']
|
||||
|
||||
|
||||
__version__ = info.version
|
||||
|
||||
|
||||
def parse(
|
||||
data: Union[str, bytes],
|
||||
raw: bool = False,
|
||||
quiet: bool = False
|
||||
) -> Dict:
|
||||
"""
|
||||
Main text parsing function
|
||||
|
||||
Parameters:
|
||||
|
||||
data: (string or bytes) text or binary data to parse
|
||||
raw: (boolean) unprocessed output if True
|
||||
quiet: (boolean) suppress warning messages if True
|
||||
|
||||
Returns:
|
||||
|
||||
Dictionary. Raw or processed structured data.
|
||||
"""
|
||||
jc.utils.compatibility(__name__, info.compatible, quiet)
|
||||
jc_global.quiet = quiet # to inject quiet setting into asn1crypto library
|
||||
|
||||
raw_output: Dict = {}
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
# convert to bytes, if not already, for PEM detection since that's
|
||||
# what pem.detect() needs. (cli.py will auto-convert to UTF-8 if it can)
|
||||
try:
|
||||
der_bytes = bytes(data, 'utf-8') # type: ignore
|
||||
except TypeError:
|
||||
der_bytes = data # type: ignore
|
||||
|
||||
if pem.detect(der_bytes):
|
||||
for type_name, headers, der_bytes in pem.unarmor(der_bytes, multiple=True):
|
||||
if type_name == 'X509 CRL':
|
||||
crl_obj = crl.CertificateList.load(der_bytes)
|
||||
break
|
||||
else:
|
||||
crl_obj = crl.CertificateList.load(der_bytes)
|
||||
|
||||
raw_output = _fix_objects(crl_obj.native)
|
||||
|
||||
return raw_output
|
||||
@@ -249,9 +249,6 @@ Examples:
|
||||
}
|
||||
]
|
||||
"""
|
||||
# import binascii
|
||||
# from collections import OrderedDict
|
||||
# from datetime import datetime
|
||||
from typing import List, Dict, Union
|
||||
import jc.utils
|
||||
from jc.parsers.asn1crypto import pem, csr, jc_global
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""jc - JSON Convert streaming utils"""
|
||||
|
||||
from functools import wraps
|
||||
from typing import Dict, Tuple, Union, Iterable, Callable, TypeVar, cast, Any
|
||||
from typing import Tuple, Union, Iterable, Callable, TypeVar, cast, Any
|
||||
from .jc_types import JSONDictType
|
||||
|
||||
|
||||
@@ -47,8 +47,9 @@ def stream_error(e: BaseException, line: str) -> JSONDictType:
|
||||
|
||||
def add_jc_meta(func: F) -> F:
|
||||
"""
|
||||
Decorator for streaming parsers to add stream_success and stream_error
|
||||
objects. This simplifies the yield lines in the streaming parsers.
|
||||
Decorator for streaming parsers to add `stream_success` and
|
||||
`stream_error` objects. This simplifies the `yield` lines in the
|
||||
streaming parsers.
|
||||
|
||||
With the decorator on parse():
|
||||
|
||||
@@ -82,7 +83,7 @@ def add_jc_meta(func: F) -> F:
|
||||
successfully parse.
|
||||
|
||||
ignore_exceptions: (bool) continue processing lines and ignore
|
||||
exceptions if True.
|
||||
exceptions if `True`.
|
||||
"""
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
@@ -109,8 +110,8 @@ def raise_or_yield(
|
||||
line: str
|
||||
) -> Tuple[BaseException, str]:
|
||||
"""
|
||||
Return the exception object and line string if ignore_exceptions is
|
||||
True. Otherwise, re-raise the exception from the exception object with
|
||||
Return the exception object and line string if `ignore_exceptions` is
|
||||
`True`. Otherwise, re-raise the exception from the exception object with
|
||||
an annotation.
|
||||
"""
|
||||
ignore_exceptions_msg = '... Use the ignore_exceptions option (-qq) to ignore streaming parser errors.'
|
||||
|
||||
50
jc/utils.py
50
jc/utils.py
@@ -100,7 +100,7 @@ def error_message(message_lines: List[str]) -> None:
|
||||
|
||||
Returns:
|
||||
|
||||
None - just prints output to STDERR
|
||||
None - just prints output to `STDERR`
|
||||
"""
|
||||
columns = shutil.get_terminal_size().columns
|
||||
|
||||
@@ -148,7 +148,7 @@ def compatibility(mod_name: str, compatible: List[str], quiet: bool = False) ->
|
||||
the parser. compatible options:
|
||||
linux, darwin, cygwin, win32, aix, freebsd
|
||||
|
||||
quiet: (bool) suppress compatibility message if True
|
||||
quiet: (bool) suppress compatibility message if `True`
|
||||
|
||||
Returns:
|
||||
|
||||
@@ -169,7 +169,7 @@ def has_data(data: Union[str, bytes]) -> bool:
|
||||
Checks if the string input contains data. If there are any
|
||||
non-whitespace characters then return `True`, else return `False`.
|
||||
|
||||
For bytes, returns True if there is any data.
|
||||
For bytes, returns `True` if there is any data.
|
||||
|
||||
Parameters:
|
||||
|
||||
@@ -177,9 +177,9 @@ def has_data(data: Union[str, bytes]) -> bool:
|
||||
|
||||
Returns:
|
||||
|
||||
Boolean True if input string (data) contains non-whitespace
|
||||
characters, otherwise False. For bytes data, returns
|
||||
True if there is any data, otherwise False.
|
||||
Boolean `True` if input string (data) contains non-whitespace
|
||||
characters, otherwise `False`. For bytes data, returns
|
||||
`True` if there is any data, otherwise `False`.
|
||||
"""
|
||||
if isinstance(data, str):
|
||||
return bool(data and not data.isspace())
|
||||
@@ -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
|
||||
|
||||
49
man/jc.1
49
man/jc.1
@@ -1,4 +1,4 @@
|
||||
.TH jc 1 2024-06-09 1.25.3 "JSON Convert"
|
||||
.TH jc 1 2025-10-12 1.25.6 "JSON Convert"
|
||||
.SH NAME
|
||||
\fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types,
|
||||
and strings
|
||||
@@ -52,6 +52,11 @@ Parsers:
|
||||
\fB--airport-s\fP
|
||||
`airport -s` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--amixer\fP
|
||||
`amixer` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--apt-cache-show\fP
|
||||
@@ -347,6 +352,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
|
||||
@@ -477,6 +487,16 @@ M3U and M3U8 file parser
|
||||
\fB--netstat\fP
|
||||
`netstat` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--net-localgroup\fP
|
||||
`net localgroup` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--net-user\fP
|
||||
`net user` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--nmcli\fP
|
||||
@@ -507,6 +527,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
|
||||
@@ -857,6 +882,11 @@ PLIST file parser
|
||||
\fB--route\fP
|
||||
`route` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--route-print\fP
|
||||
`route print` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--rpm-qi\fP
|
||||
@@ -1012,6 +1042,11 @@ TOML file parser
|
||||
\fB--traceroute\fP
|
||||
`traceroute` and `traceroute6` command parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--traceroute-s\fP
|
||||
`traceroute` and `traceroute6` command streaming parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--tune2fs\fP
|
||||
@@ -1092,6 +1127,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
|
||||
@@ -1102,6 +1142,11 @@ Version string parser
|
||||
\fB--x509-cert\fP
|
||||
X.509 PEM and DER certificate file parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--x509-crl\fP
|
||||
X.509 PEM and DER certificate revocation list file parser
|
||||
|
||||
.TP
|
||||
.B
|
||||
\fB--x509-csr\fP
|
||||
@@ -1617,6 +1662,6 @@ Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
https://github.com/kellyjonbrazil/jc
|
||||
|
||||
.SH COPYRIGHT
|
||||
Copyright (c) 2019-2024 Kelly Brazil
|
||||
Copyright (c) 2019-2025 Kelly Brazil
|
||||
|
||||
License: MIT License
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/bin/bash
|
||||
# system should be in "America/Los_Angeles" timezone for all tests to pass
|
||||
# ensure no local plugin parsers are installed for all tests to pass
|
||||
# system should be in "America/Los_Angeles" (PST8PDT) timezone for all tests
|
||||
# to pass ensure no local plugin parsers are installed for all tests to pass
|
||||
|
||||
python3 -m unittest -v
|
||||
TZ=PST8PDT python3 -m unittest -v
|
||||
|
||||
2
setup.py
2
setup.py
@@ -5,7 +5,7 @@ with open('README.md', 'r') as f:
|
||||
|
||||
setuptools.setup(
|
||||
name='jc',
|
||||
version='1.25.3',
|
||||
version='1.25.6',
|
||||
author='Kelly Brazil',
|
||||
author_email='kellyjonbrazil@gmail.com',
|
||||
description='Converts the output of popular command-line tools and file-types to JSON.',
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
[](https://github.com/kellyjonbrazil/jc/actions)
|
||||
[](https://pypi.org/project/jc/)
|
||||
|
||||
> Check out the `jc` Python [package documentation](https://github.com/kellyjonbrazil/jc/tree/master/docs) for developers
|
||||
|
||||
2
tests/fixtures/centos-7.7/df-h.json
vendored
2
tests/fixtures/centos-7.7/df-h.json
vendored
@@ -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}]
|
||||
|
||||
File diff suppressed because one or more lines are too long
2
tests/fixtures/centos-7.7/iw-scan0.json
vendored
2
tests/fixtures/centos-7.7/iw-scan0.json
vendored
@@ -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}]
|
||||
|
||||
2
tests/fixtures/centos-7.7/iw-scan1.json
vendored
2
tests/fixtures/centos-7.7/iw-scan1.json
vendored
File diff suppressed because one or more lines are too long
1
tests/fixtures/centos-7.7/iw-scan2.json
vendored
Normal file
1
tests/fixtures/centos-7.7/iw-scan2.json
vendored
Normal 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
109
tests/fixtures/centos-7.7/iw-scan2.out
vendored
Normal 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
|
||||
2
tests/fixtures/centos-7.7/lsblk-allcols.json
vendored
2
tests/fixtures/centos-7.7/lsblk-allcols.json
vendored
@@ -1 +1 @@
|
||||
[{"name": "sda", "maj_min": "8:0", "rm": false, "size": "20G", "ro": false, "type": "disk", "mountpoint": null, "kname": "sda", "fstype": null, "label": null, "uuid": null, "partlabel": null, "partuuid": null, "ra": 4096, "model": "VMware Virtual S", "serial": null, "state": "running", "owner": "root", "group": "disk", "mode": "brw-rw----", "alignment": 0, "min_io": 512, "opt_io": 0, "phy_sec": 512, "log_sec": 512, "rota": true, "sched": "deadline", "rq_size": 128, "disc_aln": 0, "disc_gran": "0B", "disc_max": "0B", "disc_zero": false, "wsame": "32M", "wwn": null, "rand": true, "pkname": null, "hctl": "0:0:0:0", "tran": "spi", "rev": "1.0", "vendor": "VMware,"}, {"name": "sda1", "maj_min": "8:1", "rm": false, "size": "1G", "ro": false, "type": "part", "mountpoint": "/boot", "kname": "sda1", "fstype": "xfs", "label": null, "uuid": "05d927bb-5875-49e3-ada1-7f46cb31c932", "partlabel": null, "partuuid": null, "ra": 4096, "model": null, "serial": null, "state": null, "owner": "root", "group": "disk", "mode": "brw-rw----", "alignment": 0, "min_io": 512, "opt_io": 0, "phy_sec": 512, "log_sec": 512, "rota": true, "sched": "deadline", "rq_size": 128, "disc_aln": 0, "disc_gran": "0B", "disc_max": "0B", "disc_zero": false, "wsame": "32M", "wwn": null, "rand": true, "pkname": "sda", "hctl": null, "tran": null, "rev": null, "vendor": null}, {"name": "sda2", "maj_min": "8:2", "rm": false, "size": "19G", "ro": false, "type": "part", "mountpoint": null, "kname": "sda2", "fstype": "LVM2_member", "label": null, "uuid": "3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM", "partlabel": null, "partuuid": null, "ra": 4096, "model": null, "serial": null, "state": null, "owner": "root", "group": "disk", "mode": "brw-rw----", "alignment": 0, "min_io": 512, "opt_io": 0, "phy_sec": 512, "log_sec": 512, "rota": true, "sched": "deadline", "rq_size": 128, "disc_aln": 0, "disc_gran": "0B", "disc_max": "0B", "disc_zero": false, "wsame": "32M", "wwn": null, "rand": true, "pkname": "sda", "hctl": null, "tran": null, "rev": null, "vendor": null}, {"name": "centos-root", "maj_min": "253:0", "rm": false, "size": "17G", "ro": false, "type": "lvm", "mountpoint": "/", "kname": "dm-0", "fstype": "xfs", "label": null, "uuid": "07d718ef-950c-4e5b-98e0-42a1147b77d9", "partlabel": null, "partuuid": null, "ra": 4096, "model": null, "serial": null, "state": "running", "owner": "root", "group": "disk", "mode": "brw-rw----", "alignment": 0, "min_io": 512, "opt_io": 0, "phy_sec": 512, "log_sec": 512, "rota": true, "sched": null, "rq_size": 128, "disc_aln": 0, "disc_gran": "0B", "disc_max": "0B", "disc_zero": false, "wsame": "32M", "wwn": null, "rand": false, "pkname": "sda2", "hctl": null, "tran": null, "rev": null, "vendor": null}, {"name": "centos-swap", "maj_min": "253:1", "rm": false, "size": "2G", "ro": false, "type": "lvm", "mountpoint": "[SWAP]", "kname": "dm-1", "fstype": "swap", "label": null, "uuid": "615eb89d-bcbf-46ad-80e3-c483ef5c931f", "partlabel": null, "partuuid": null, "ra": 4096, "model": null, "serial": null, "state": "running", "owner": "root", "group": "disk", "mode": "brw-rw----", "alignment": 0, "min_io": 512, "opt_io": 0, "phy_sec": 512, "log_sec": 512, "rota": true, "sched": null, "rq_size": 128, "disc_aln": 0, "disc_gran": "0B", "disc_max": "0B", "disc_zero": false, "wsame": "32M", "wwn": null, "rand": false, "pkname": "sda2", "hctl": null, "tran": null, "rev": null, "vendor": null}, {"name": "sr0", "maj_min": "11:0", "rm": true, "size": "1024M", "ro": false, "type": "rom", "mountpoint": null, "kname": "sr0", "fstype": null, "label": null, "uuid": null, "partlabel": null, "partuuid": null, "ra": 128, "model": "VMware IDE CDR10", "serial": "10000000000000000001", "state": "running", "owner": "root", "group": "cdrom", "mode": "brw-rw----", "alignment": 0, "min_io": 512, "opt_io": 0, "phy_sec": 512, "log_sec": 512, "rota": true, "sched": "deadline", "rq_size": 128, "disc_aln": 0, "disc_gran": "0B", "disc_max": "0B", "disc_zero": false, "wsame": "0B", "wwn": null, "rand": true, "pkname": null, "hctl": "2:0:0:0", "tran": "ata", "rev": "1.00", "vendor": "NECVMWar"}]
|
||||
[{"name":"sda","maj_min":"8:0","rm":false,"size":"20G","ro":false,"type":"disk","mountpoint":null,"kname":"sda","fstype":null,"label":null,"uuid":null,"partlabel":null,"partuuid":null,"ra":4096,"model":"VMware Virtual S","serial":null,"state":"running","owner":"root","group":"disk","mode":"brw-rw----","alignment":0,"min_io":512,"opt_io":0,"phy_sec":512,"log_sec":512,"rota":true,"sched":"deadline","rq_size":128,"disc_aln":0,"disc_gran":"0B","disc_max":"0B","disc_zero":false,"wsame":"32M","wwn":null,"rand":true,"pkname":null,"hctl":"0:0:0:0","tran":"spi","rev":"1.0","vendor":"VMware,","size_bytes":21474836480,"disc_gran_bytes":0,"disc_max_bytes":0,"wsame_bytes":33554432},{"name":"sda1","maj_min":"8:1","rm":false,"size":"1G","ro":false,"type":"part","mountpoint":"/boot","kname":"sda1","fstype":"xfs","label":null,"uuid":"05d927bb-5875-49e3-ada1-7f46cb31c932","partlabel":null,"partuuid":null,"ra":4096,"model":null,"serial":null,"state":null,"owner":"root","group":"disk","mode":"brw-rw----","alignment":0,"min_io":512,"opt_io":0,"phy_sec":512,"log_sec":512,"rota":true,"sched":"deadline","rq_size":128,"disc_aln":0,"disc_gran":"0B","disc_max":"0B","disc_zero":false,"wsame":"32M","wwn":null,"rand":true,"pkname":"sda","hctl":null,"tran":null,"rev":null,"vendor":null,"size_bytes":1073741824,"disc_gran_bytes":0,"disc_max_bytes":0,"wsame_bytes":33554432},{"name":"sda2","maj_min":"8:2","rm":false,"size":"19G","ro":false,"type":"part","mountpoint":null,"kname":"sda2","fstype":"LVM2_member","label":null,"uuid":"3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM","partlabel":null,"partuuid":null,"ra":4096,"model":null,"serial":null,"state":null,"owner":"root","group":"disk","mode":"brw-rw----","alignment":0,"min_io":512,"opt_io":0,"phy_sec":512,"log_sec":512,"rota":true,"sched":"deadline","rq_size":128,"disc_aln":0,"disc_gran":"0B","disc_max":"0B","disc_zero":false,"wsame":"32M","wwn":null,"rand":true,"pkname":"sda","hctl":null,"tran":null,"rev":null,"vendor":null,"size_bytes":20401094656,"disc_gran_bytes":0,"disc_max_bytes":0,"wsame_bytes":33554432},{"name":"centos-root","maj_min":"253:0","rm":false,"size":"17G","ro":false,"type":"lvm","mountpoint":"/","kname":"dm-0","fstype":"xfs","label":null,"uuid":"07d718ef-950c-4e5b-98e0-42a1147b77d9","partlabel":null,"partuuid":null,"ra":4096,"model":null,"serial":null,"state":"running","owner":"root","group":"disk","mode":"brw-rw----","alignment":0,"min_io":512,"opt_io":0,"phy_sec":512,"log_sec":512,"rota":true,"sched":null,"rq_size":128,"disc_aln":0,"disc_gran":"0B","disc_max":"0B","disc_zero":false,"wsame":"32M","wwn":null,"rand":false,"pkname":"sda2","hctl":null,"tran":null,"rev":null,"vendor":null,"size_bytes":18253611008,"disc_gran_bytes":0,"disc_max_bytes":0,"wsame_bytes":33554432},{"name":"centos-swap","maj_min":"253:1","rm":false,"size":"2G","ro":false,"type":"lvm","mountpoint":"[SWAP]","kname":"dm-1","fstype":"swap","label":null,"uuid":"615eb89d-bcbf-46ad-80e3-c483ef5c931f","partlabel":null,"partuuid":null,"ra":4096,"model":null,"serial":null,"state":"running","owner":"root","group":"disk","mode":"brw-rw----","alignment":0,"min_io":512,"opt_io":0,"phy_sec":512,"log_sec":512,"rota":true,"sched":null,"rq_size":128,"disc_aln":0,"disc_gran":"0B","disc_max":"0B","disc_zero":false,"wsame":"32M","wwn":null,"rand":false,"pkname":"sda2","hctl":null,"tran":null,"rev":null,"vendor":null,"size_bytes":2147483648,"disc_gran_bytes":0,"disc_max_bytes":0,"wsame_bytes":33554432},{"name":"sr0","maj_min":"11:0","rm":true,"size":"1024M","ro":false,"type":"rom","mountpoint":null,"kname":"sr0","fstype":null,"label":null,"uuid":null,"partlabel":null,"partuuid":null,"ra":128,"model":"VMware IDE CDR10","serial":"10000000000000000001","state":"running","owner":"root","group":"cdrom","mode":"brw-rw----","alignment":0,"min_io":512,"opt_io":0,"phy_sec":512,"log_sec":512,"rota":true,"sched":"deadline","rq_size":128,"disc_aln":0,"disc_gran":"0B","disc_max":"0B","disc_zero":false,"wsame":"0B","wwn":null,"rand":true,"pkname":null,"hctl":"2:0:0:0","tran":"ata","rev":"1.00","vendor":"NECVMWar","size_bytes":1073741824,"disc_gran_bytes":0,"disc_max_bytes":0,"wsame_bytes":0}]
|
||||
|
||||
2
tests/fixtures/centos-7.7/lsblk.json
vendored
2
tests/fixtures/centos-7.7/lsblk.json
vendored
@@ -1 +1 @@
|
||||
[{"name": "sda", "maj_min": "8:0", "rm": false, "size": "20G", "ro": false, "type": "disk", "mountpoint": null}, {"name": "sda1", "maj_min": "8:1", "rm": false, "size": "1G", "ro": false, "type": "part", "mountpoint": "/boot"}, {"name": "sda2", "maj_min": "8:2", "rm": false, "size": "19G", "ro": false, "type": "part", "mountpoint": null}, {"name": "centos-root", "maj_min": "253:0", "rm": false, "size": "17G", "ro": false, "type": "lvm", "mountpoint": "/"}, {"name": "centos-swap", "maj_min": "253:1", "rm": false, "size": "2G", "ro": false, "type": "lvm", "mountpoint": "[SWAP]"}, {"name": "sr0", "maj_min": "11:0", "rm": true, "size": "1024M", "ro": false, "type": "rom", "mountpoint": null}]
|
||||
[{"name":"sda","maj_min":"8:0","rm":false,"size":"20G","ro":false,"type":"disk","mountpoint":null,"size_bytes":21474836480},{"name":"sda1","maj_min":"8:1","rm":false,"size":"1G","ro":false,"type":"part","mountpoint":"/boot","size_bytes":1073741824},{"name":"sda2","maj_min":"8:2","rm":false,"size":"19G","ro":false,"type":"part","mountpoint":null,"size_bytes":20401094656},{"name":"centos-root","maj_min":"253:0","rm":false,"size":"17G","ro":false,"type":"lvm","mountpoint":"/","size_bytes":18253611008},{"name":"centos-swap","maj_min":"253:1","rm":false,"size":"2G","ro":false,"type":"lvm","mountpoint":"[SWAP]","size_bytes":2147483648},{"name":"sr0","maj_min":"11:0","rm":true,"size":"1024M","ro":false,"type":"rom","mountpoint":null,"size_bytes":1073741824}]
|
||||
|
||||
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
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user