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

Compare commits

..

89 Commits

Author SHA1 Message Date
Kelly Brazil
c2450b27b0 Merge pull request #22 from kellyjonbrazil/dev
Dev 1.7.3
2020-02-13 21:26:18 -05:00
Kelly Brazil
14d6d8b84f version bump to 1.7.3 2020-02-13 18:24:53 -08:00
Kelly Brazil
f0e3846c03 formatting 2020-02-13 18:20:22 -08:00
Kelly Brazil
6ba64f1128 usage update 2020-02-13 18:19:19 -08:00
Kelly Brazil
13bcdbc6c9 doc update 2020-02-13 17:50:51 -08:00
Kelly Brazil
cfba62db20 correct parser search in magic() 2020-02-13 17:48:21 -08:00
Kelly Brazil
18fb69e36e docs/parsers link 2020-02-13 17:29:45 -08:00
Kelly Brazil
474eb0f3fd doc updates 2020-02-13 17:24:10 -08:00
Kelly Brazil
7f47b53370 add alternate magic syntax 2020-02-13 17:20:00 -08:00
Kelly Brazil
dc2907d3ce doc update 2020-02-13 16:58:25 -08:00
Kelly Brazil
1af85811e0 remove magic_command info 2020-02-13 16:57:57 -08:00
Kelly Brazil
1c1b19a478 doc update 2020-02-13 16:57:30 -08:00
Kelly Brazil
66942d64ba changelog update 2020-02-13 16:56:48 -08:00
Kelly Brazil
2fb6ae08d7 fix shlex usage 2020-02-13 12:17:41 -08:00
Kelly Brazil
bf8811e03e add comments 2020-02-13 10:25:41 -05:00
Kelly Brazil
c8b502c571 remove unnecessary join and add comments 2020-02-13 10:14:32 -05:00
Kelly Brazil
81c11a975c added docstrings 2020-02-13 10:08:43 -05:00
Kelly Brazil
0d370eb403 doc update 2020-02-13 10:03:11 -05:00
Kelly Brazil
7492c3f1e3 changelog update 2020-02-13 09:48:42 -05:00
Kelly Brazil
515a8a84b7 add "command" to description 2020-02-13 09:47:40 -05:00
Kelly Brazil
dd6680efb2 allow condensed options (-prdq is equivalent to -p -r -d -q) 2020-02-13 09:47:16 -05:00
Kelly Brazil
a7158373cd comment update 2020-02-12 00:16:17 -05:00
Kelly Brazil
6d50ec7199 add try/except to fix bare jc command condition 2020-02-12 00:11:48 -05:00
Kelly Brazil
95dbf98e8e allow options in magic syntax 2020-02-11 19:14:51 -08:00
Kelly Brazil
d49323e4eb add magic_commands list to info 2020-02-11 18:09:21 -08:00
Kelly Brazil
08c1e2aec9 add magic syntax 2020-02-11 18:08:59 -08:00
Kelly Brazil
a2c137df2e better magic command syntax logic using introspection information from parser modules 2020-02-11 18:08:37 -08:00
Kelly Brazil
fe27dcdb8f proof of concept for magic syntax (e.g. jc ls -al) 2020-02-11 12:16:23 -08:00
Kelly Brazil
028e136161 bump to 1.7.2: add test fixtures to package 2020-02-08 12:46:42 -08:00
Kelly Brazil
9a85a0a4d5 fix doc 2020-02-08 12:46:14 -08:00
Kelly Brazil
3a1cbc4d50 move info class to top 2020-02-05 22:26:47 -08:00
Kelly Brazil
77d334f7f3 Merge pull request #19 from kellyjonbrazil/dev-1.7.1
Dev v1.7.1
2020-02-05 16:59:52 -08:00
Kelly Brazil
53cdf863ac changelog update 2020-02-05 16:53:17 -08:00
Kelly Brazil
7b7e7fe0fe changelog update 2020-02-05 16:50:55 -08:00
Kelly Brazil
0c03132847 fix error codes using sys.exit() 2020-02-05 16:18:58 -08:00
Kelly Brazil
3b81f7e2a1 exit code on ctrl-c exit 2020-02-05 16:12:09 -08:00
Kelly Brazil
3d76437b43 doc fix 2020-02-05 16:00:23 -08:00
Kelly Brazil
4bc54c78ce fix compatibility list 2020-02-05 16:00:10 -08:00
Kelly Brazil
3d303a96b9 crontab bug fix and tests 2020-02-05 15:52:39 -08:00
Kelly Brazil
33c99d031d fix line clobbering bug 2020-02-05 15:12:59 -08:00
Kelly Brazil
caf7e9f69a fix line clobbering bug and add user field to shortcuts 2020-02-05 15:11:51 -08:00
Kelly Brazil
9449f1f5d5 crontab bugfix: inserting header row was clobbering the first data row 2020-02-05 14:44:49 -08:00
Kelly Brazil
6bad164b5e simplify by removing unnecessary getattr calls 2020-02-05 14:10:22 -08:00
Kelly Brazil
bb5ba7ddb1 add indent variable to helptext 2020-02-05 13:57:34 -08:00
Kelly Brazil
8b2e01d540 doc update 2020-02-05 13:50:12 -08:00
Kelly Brazil
ff1159b1de exit codes on error 2020-02-05 11:45:17 -08:00
Kelly Brazil
a2fd3202a0 description formatting change 2020-02-05 11:34:33 -08:00
Kelly Brazil
7b53715b91 change description 2020-02-05 11:08:58 -08:00
Kelly Brazil
e05fc0a510 change padding of helptext 2020-02-05 11:08:47 -08:00
Kelly Brazil
43604c33f6 doc update 2020-02-05 10:59:51 -08:00
Kelly Brazil
eb67c484ff add crontab-u to parsers list 2020-02-05 10:58:26 -08:00
Kelly Brazil
a7b7bdd467 load parser modules 'just in time' so we don't need to load all modules at startup 2020-02-05 10:55:08 -08:00
Kelly Brazil
ab06989a18 description updates 2020-02-04 21:46:52 -08:00
Kelly Brazil
657b722f94 ini to INI 2020-02-04 21:44:10 -08:00
Kelly Brazil
dd2aecad27 description update 2020-02-04 21:37:07 -08:00
Kelly Brazil
c82c5c5c64 changelog update 2020-02-04 21:34:57 -08:00
Kelly Brazil
a1761cd68f id tests 2020-02-04 21:25:33 -08:00
Kelly Brazil
d618a7f583 doc update 2020-02-04 21:17:03 -08:00
Kelly Brazil
831a42f660 id formatting 2020-02-04 21:12:32 -08:00
Kelly Brazil
3b36022e5a add id parser 2020-02-04 21:09:42 -08:00
Kelly Brazil
d01dfa25f1 changelog updates 2020-02-04 15:22:36 -08:00
Kelly Brazil
395a99037b crontab-u and history doc updates 2020-02-04 14:36:03 -08:00
Kelly Brazil
025986c51d change history 'line' to integer 2020-02-04 14:31:28 -08:00
Kelly Brazil
c56b83093f doc update 2020-02-04 14:19:33 -08:00
Kelly Brazil
7c712a4133 doc update 2020-02-04 14:18:14 -08:00
Kelly Brazil
9a0cfe6dfa minor formatting 2020-02-04 14:18:01 -08:00
Kelly Brazil
a116cdbcec tests for crontab-u 2020-02-04 14:11:34 -08:00
Kelly Brazil
f2d616c98e add crontab with user parser 2020-02-04 14:02:27 -08:00
Kelly Brazil
42cbd1777d add xml and yaml tests 2020-02-04 13:53:45 -08:00
Kelly Brazil
ebf375aac0 add ini tests 2020-02-04 12:18:31 -08:00
Kelly Brazil
1f9050267e add ini, xml, and yaml test files 2020-02-04 12:02:18 -08:00
Kelly Brazil
d7f9707a15 minor formatting 2020-02-04 11:18:32 -08:00
Kelly Brazil
ab589ee3ed add __version__ variable 2020-02-04 11:18:15 -08:00
Kelly Brazil
c84ec0361f xml example update 2020-02-03 22:25:30 -08:00
Kelly Brazil
47d2f8968a doc update 2020-02-03 22:21:40 -08:00
Kelly Brazil
019c480bcc update acknowledgments 2020-02-03 22:17:13 -08:00
Kelly Brazil
547c6d3d59 add xml parser 2020-02-03 22:13:06 -08:00
Kelly Brazil
b5ebf8b76a add ruamel.yaml ack 2020-02-03 21:41:53 -08:00
Kelly Brazil
c690e328f2 add examples 2020-02-03 21:38:21 -08:00
Kelly Brazil
cbb92c1a95 add ini and yaml 2020-02-03 21:38:08 -08:00
Kelly Brazil
beb41997c9 doc update 2020-02-03 21:28:11 -08:00
Kelly Brazil
755a6faf11 clean up multi-document support 2020-02-03 21:22:30 -08:00
Kelly Brazil
021f8350a3 update doc 2020-02-03 19:11:36 -08:00
Kelly Brazil
76583dcd2f add ini file parser 2020-02-03 19:07:31 -08:00
Kelly Brazil
bf033239a7 doc update 2020-02-03 16:20:38 -08:00
Kelly Brazil
eb37fccd37 doc update 2020-02-03 16:17:29 -08:00
Kelly Brazil
d04ad45331 setup for 1.7.1 2020-02-03 16:12:45 -08:00
Kelly Brazil
db157b8ca7 add yaml file parser 2020-02-03 16:12:32 -08:00
Kelly Brazil
68f277bb20 add __version__ 2020-02-03 16:11:58 -08:00
82 changed files with 3078 additions and 255 deletions

1
MANIFEST.in Normal file
View File

@@ -0,0 +1 @@
graft tests/fixtures

422
README.md
View File

@@ -1,7 +1,7 @@
# JC
JSON CLI output utility
`jc` is used to JSONify the output of many standard linux cli tools for easier parsing in scripts. See the **Parsers** section for supported commands.
`jc` is used to JSONify the output of many standard linux cli tools and file types for easier parsing in scripts. See the **Parsers** section for supported commands.
This allows further command line processing of output with tools like `jq` simply by piping commands:
@@ -17,6 +17,20 @@ $ ls -l /usr/bin | jc --ls | jq '.[] | select(.size > 50000000)'
"date": "Aug 14 19:41"
}
```
or using the alternative "magic" syntax:
```
$ jc ls -l /usr/bin | jq '.[] | select(.size > 50000000)'
{
"filename": "docker",
"flags": "-rwxr-xr-x",
"links": 1,
"owner": "root",
"group": "root",
"size": 68677120,
"date": "Aug 14 19:41"
}
```
For more information on the motivations for this project, please see my blog post at https://blog.kellybrazil.com/2019/11/26/bringing-the-unix-philosophy-to-the-21st-century/.
The `jc` parsers can also be used as python modules. In this case the output will be a python dictionary instead of JSON:
@@ -49,7 +63,7 @@ Two representations of the data are possible. The default representation uses a
To access the raw, pre-processed JSON, use the `-r` cli option or the `raw=True` function parameter in `parse()`.
Schemas for each parser can be found in the `docs/parsers` folder.
Schemas for each parser can be found in the [`docs/parsers`](https://github.com/kellyjonbrazil/jc/tree/dev/docs/parsers) folder.
## Installation
```
@@ -57,45 +71,58 @@ $ pip3 install --upgrade jc
```
## Usage
`jc` accepts piped input from `STDIN` and outputs a JSON representation of the previous command's output to `STDOUT`.
```
jc PARSER [OPTIONS]
COMMAND | jc PARSER [OPTIONS]
```
`jc` accepts piped input from `STDIN` and outputs a JSON representation of the previous command's output to `STDOUT`. The JSON output can be compact or pretty formatted.
or
```
COMMAND | jc [OPTIONS] PARSER
```
Alternatively, the "magic" syntax can be used by prepending `jc` to the command to be converted. Options can be passed to `jc` immediately before the command is given.
```
jc [OPTIONS] COMMAND
```
The JSON output can be compact (default) or pretty formatted with the `-p` option.
### Parsers
- `--arp` enables the `arp` parser
- `--crontab` enables the `crontab` file parser
- `--df` enables the `df` parser
- `--dig` enables the `dig` parser
- `--du` enables the `du` parser
- `--env` enables the `env` parser
- `--free` enables the `free` parser
- `--arp` enables the `arp` command parser
- `--crontab` enables the `crontab` command and file parser
- `--crontab-u` enables the `crontab` file parser with user support
- `--df` enables the `df` command parser
- `--dig` enables the `dig` command parser
- `--du` enables the `du` command parser
- `--env` enables the `env` command parser
- `--free` enables the `free` command parser
- `--fstab` enables the `/etc/fstab` file parser
- `--history` enables the `history` parser
- `--history` enables the `history` command parser
- `--hosts` enables the `/etc/hosts` file parser
- `--ifconfig` enables the `ifconfig` parser
- `--iptables` enables the `iptables` parser
- `--jobs` enables the `jobs` parser
- `--ls` enables the `ls` parser
- `--lsblk` enables the `lsblk` parser
- `--lsmod` enables the `lsmod` parser
- `--lsof` enables the `lsof` parser
- `--mount` enables the `mount` parser
- `--netstat` enables the `netstat` parser
- `--pip-list` enables the `pip list` parser
- `--pip-show` enables the `pip show` parser
- `--ps` enables the `ps` parser
- `--route` enables the `route` parser
- `--ss` enables the `ss` parser
- `--stat` enables the `stat` parser
- `--systemctl` enables the `systemctl` parser
- `--systemctl-lj` enables the `systemctl list-jobs` parser
- `--systemctl-ls` enables the `systemctl list-sockets` parser
- `--systemctl-luf` enables the `systemctl list-unit-files` parser
- `--uname` enables the `uname -a` parser
- `--uptime` enables the `uptime` parser
- `--w` enables the `w` parser
- `--id` enables the `id` command parser
- `--ifconfig` enables the `ifconfig` command parser
- `--ini` enables the `INI` file parser
- `--iptables` enables the `iptables` command parser
- `--jobs` enables the `jobs` command parser
- `--ls` enables the `ls` command parser
- `--lsblk` enables the `lsblk` command parser
- `--lsmod` enables the `lsmod` command parser
- `--lsof` enables the `lsof` command parser
- `--mount` enables the `mount` command parser
- `--netstat` enables the `netstat` command parser
- `--pip-list` enables the `pip list` command parser
- `--pip-show` enables the `pip show` command parser
- `--ps` enables the `ps` command parser
- `--route` enables the `route` command parser
- `--ss` enables the `ss` command parser
- `--stat` enables the `stat` command parser
- `--systemctl` enables the `systemctl` command parser
- `--systemctl-lj` enables the `systemctl list-jobs` command parser
- `--systemctl-ls` enables the `systemctl list-sockets` command parser
- `--systemctl-luf` enables the `systemctl list-unit-files` command parser
- `--uname` enables the `uname -a` command parser
- `--uptime` enables the `uptime` command parser
- `--w` enables the `w` command parser
- `--xml` enables the `XML` file parser
- `--yaml` enables the `YAML` file parser
### Options
- `-a` about `jc`. Prints information about `jc` and the parsers (in JSON, of course!)
@@ -107,7 +134,7 @@ jc PARSER [OPTIONS]
## Examples
### arp
```
$ arp | jc --arp -p
$ arp | jc --arp -p # or: jc -p arp
[
{
"address": "gateway",
@@ -133,7 +160,7 @@ $ arp | jc --arp -p
]
```
```
$ arp -a | jc --arp -p
$ arp -a | jc --arp -p # or: jc -p arp -a
[
{
"name": null,
@@ -160,7 +187,7 @@ $ arp -a | jc --arp -p
```
### crontab
```
$ cat /etc/crontab | jc --crontab -p
$ cat /etc/crontab | jc --crontab -p # or: jc -p crontab -l
{
"variables": [
{
@@ -229,9 +256,85 @@ $ cat /etc/crontab | jc --crontab -p
]
}
```
### crontab-u (with user support)
```
$ cat /etc/crontab | jc --crontab-u -p
{
"variables": [
{
"name": "MAILTO",
"value": "root"
},
{
"name": "PATH",
"value": "/sbin:/bin:/usr/sbin:/usr/bin"
},
{
"name": "SHELL",
"value": "/bin/bash"
}
],
"schedule": [
{
"minute": [
"5"
],
"hour": [
"10-11",
"22"
],
"day_of_month": [
"*"
],
"month": [
"*"
],
"day_of_week": [
"*"
],
"user": "root",
"command": "/var/www/devdaily.com/bin/mk-new-links.php"
},
{
"minute": [
"30"
],
"hour": [
"4/2"
],
"day_of_month": [
"*"
],
"month": [
"*"
],
"day_of_week": [
"*"
],
"user": "root",
"command": "/var/www/devdaily.com/bin/create-all-backups.sh"
},
{
"occurrence": "yearly",
"user": "root",
"command": "/home/maverick/bin/annual-maintenance"
},
{
"occurrence": "reboot",
"user": "root",
"command": "/home/cleanup"
},
{
"occurrence": "monthly",
"user": "root",
"command": "/home/maverick/bin/tape-backup"
}
]
}
```
### df
```
$ df | jc --df -p
$ df | jc --df -p # or: jc -p df
[
{
"filesystem": "devtmpfs",
@@ -254,7 +357,7 @@ $ df | jc --df -p
```
### dig
```
$ dig cnn.com www.cnn.com @205.251.194.64 | jc --dig -p
$ dig cnn.com www.cnn.com @205.251.194.64 | jc --dig -p # or: jc -p dig cnn.com www.cnn.com @205.251.194.64
[
{
"id": 5509,
@@ -374,7 +477,7 @@ $ dig cnn.com www.cnn.com @205.251.194.64 | jc --dig -p
]
```
```
$ dig -x 1.1.1.1 | jc --dig -p
$ dig -x 1.1.1.1 | jc --dig -p # or: jc -p dig -x 1.1.1.1
[
{
"id": 50324,
@@ -412,7 +515,7 @@ $ dig -x 1.1.1.1 | jc --dig -p
```
### du
```
$ du /usr | jc --du -p
$ du /usr | jc --du -p # or: jc -p du /usr
[
{
"size": 104608,
@@ -443,7 +546,7 @@ $ du /usr | jc --du -p
```
### env
```
$ env | jc --env -p
$ env | jc --env -p # or: jc -p env
[
{
"name": "XDG_SESSION_ID",
@@ -470,7 +573,7 @@ $ env | jc --env -p
```
### free
```
$ free | jc --free -p
$ free | jc --free -p # or: jc -p free
[
{
"type": "Mem",
@@ -489,7 +592,7 @@ $ free | jc --free -p
}
]
```
### /etc/fstab
### /etc/fstab file
```
$ cat /etc/fstab | jc --fstab -p
[
@@ -521,28 +624,28 @@ $ cat /etc/fstab | jc --fstab -p
```
### history
```
$ history | jc --history -p
$ history | jc --history -p # or: jc -p history
[
{
"line": "118",
"line": 118,
"command": "sleep 100"
},
{
"line": "119",
"line": 119,
"command": "ls /bin"
},
{
"line": "120",
"line": 120,
"command": "echo \"hello\""
},
{
"line": "121",
"line": 121,
"command": "docker images"
},
...
]
```
### /etc/hosts
### /etc/hosts file
```
$ cat /etc/hosts | jc --hosts -p
[
@@ -591,9 +694,39 @@ $ cat /etc/hosts | jc --hosts -p
}
]
```
### id
```
$ id | jc --id -p # or: jc -p id
{
"uid": {
"id": 1000,
"name": "joeuser"
},
"gid": {
"id": 1000,
"name": "joeuser"
},
"groups": [
{
"id": 1000,
"name": "joeuser"
},
{
"id": 10,
"name": "wheel"
}
],
"context": {
"user": "unconfined_u",
"role": "unconfined_r",
"type": "unconfined_t",
"level": "s0-s0:c0.c1023"
}
}
```
### ifconfig
```
$ ifconfig | jc --ifconfig -p
$ ifconfig | jc --ifconfig -p # or: jc -p ifconfig
[
{
"name": "ens33",
@@ -662,9 +795,43 @@ $ ifconfig | jc --ifconfig -p
}
]
```
### INI files
```
$ cat example.ini
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
Port = 50022
ForwardX11 = no
$ cat example.ini | jc --ini -p
{
"bitbucket.org": {
"serveraliveinterval": "45",
"compression": "yes",
"compressionlevel": "9",
"forwardx11": "yes",
"user": "hg"
},
"topsecret.server.com": {
"serveraliveinterval": "45",
"compression": "yes",
"compressionlevel": "9",
"forwardx11": "no",
"port": "50022"
}
}
```
### iptables
```
$ sudo iptables --line-numbers -v -L -t nat | jc --iptables -p
$ sudo iptables --line-numbers -v -L -t nat | jc --iptables -p # or: sudo jc -p iptables --line-numbers -v -L -t nat
[
{
"chain": "PREROUTING",
@@ -725,7 +892,7 @@ $ sudo iptables --line-numbers -v -L -t nat | jc --iptables -p
```
### jobs
```
$ jobs -l | jc --jobs -p
$ jobs -l | jc --jobs -p # or: jc -p jobs
[
{
"job_number": 1,
@@ -757,7 +924,7 @@ $ jobs -l | jc --jobs -p
```
### ls
```
$ ls -l /usr/bin | jc --ls -p
$ ls -l /usr/bin | jc --ls -p # or: jc -p ls -l /usr/bin
[
{
"filename": "apropos",
@@ -792,7 +959,7 @@ $ ls -l /usr/bin | jc --ls -p
```
### lsblk
```
$ lsblk | jc --lsblk -p
$ lsblk | jc --lsblk -p # or: jc -p lsblk
[
{
"name": "sda",
@@ -817,7 +984,7 @@ $ lsblk | jc --lsblk -p
```
### lsmod
```
$ lsmod | jc --lsmod -p
$ lsmod | jc --lsmod -p # or: jc -p lsmod
[
...
{
@@ -864,7 +1031,7 @@ $ lsmod | jc --lsmod -p
```
### lsof
```
$ sudo lsof | jc --lsof -p
$ sudo lsof | jc --lsof -p # or: sudo jc -p lsof
[
{
"command": "systemd",
@@ -907,7 +1074,7 @@ $ sudo lsof | jc --lsof -p
```
### mount
```
$ mount | jc --mount -p
$ mount | jc --mount -p # or: jc -p mount
[
{
"filesystem": "sysfs",
@@ -951,7 +1118,7 @@ $ mount | jc --mount -p
```
### netstat
```
$ sudo netstat -apee | jc --netstat -p
$ sudo netstat -apee | jc --netstat -p # or: sudo jc -p netstat -apee
[
{
"proto": "tcp",
@@ -1103,7 +1270,7 @@ $ sudo netstat -apee | jc --netstat -p
```
### pip list
```
$ pip list | jc --pip-list -p
$ pip list | jc --pip-list -p # or: jc -p pip list # or: jc -p pip3 list
[
{
"package": "ansible",
@@ -1123,7 +1290,7 @@ $ pip list | jc --pip-list -p
```
### pip show
```
$ pip show wrapt wheel | jc --pip-show -p
$ pip show wrapt wheel | jc --pip-show -p # or: jc -p pip show wrapt wheel # or: jc -p pip3 show wrapt wheel
[
{
"name": "wrapt",
@@ -1153,7 +1320,7 @@ $ pip show wrapt wheel | jc --pip-show -p
```
### ps
```
$ ps -ef | jc --ps -p
$ ps -ef | jc --ps -p # or: jc -p ps -ef
[
{
"uid": "root",
@@ -1189,7 +1356,7 @@ $ ps -ef | jc --ps -p
]
```
```
$ ps axu | jc --ps -p
$ ps axu | jc --ps -p # or: jc -p ps axu
[
{
"user": "root",
@@ -1235,7 +1402,7 @@ $ ps axu | jc --ps -p
```
### route
```
$ route -ee | jc --route -p
$ route -ee | jc --route -p # or: jc -p route -ee
[
{
"destination": "default",
@@ -1280,7 +1447,7 @@ $ route -ee | jc --route -p
```
### ss
```
$ sudo ss -a | jc --ss -p
$ sudo ss -a | jc --ss -p # or: sudo jc -p ss -a
[
{
"netid": "nl",
@@ -1399,7 +1566,7 @@ $ sudo ss -a | jc --ss -p
```
### stat
```
$ stat /bin/* | jc --stat -p
$ stat /bin/* | jc --stat -p # or: jc -p stat /bin/*
[
{
"file": "/bin/bash",
@@ -1446,7 +1613,7 @@ $ stat /bin/* | jc --stat -p
```
### systemctl
```
$ systemctl -a | jc --systemctl -p
$ systemctl -a | jc --systemctl -p # or: jc -p systemctl -a
[
{
"unit": "proc-sys-fs-binfmt_misc.automount",
@@ -1474,7 +1641,7 @@ $ systemctl -a | jc --systemctl -p
```
### systemctl list-jobs
```
$ systemctl list-jobs| jc --systemctl-lj -p
$ systemctl list-jobs | jc --systemctl-lj -p # or: jc -p systemctl list-jobs
[
{
"job": 3543,
@@ -1498,7 +1665,7 @@ $ systemctl list-jobs| jc --systemctl-lj -p
```
### systemctl list-sockets
```
$ systemctl list-sockets | jc --systemctl-ls -p
$ systemctl list-sockets | jc --systemctl-ls -p # or: jc -p systemctl list-sockets
[
{
"listen": "/dev/log",
@@ -1520,7 +1687,7 @@ $ systemctl list-sockets | jc --systemctl-ls -p
```
### systemctl list-unit-files
```
$ systemctl list-unit-files | jc --systemctl-luf -p
$ systemctl list-unit-files | jc --systemctl-luf -p # or: jc -p systemctl list-unit-files
[
{
"unit_file": "proc-sys-fs-binfmt_misc.automount",
@@ -1539,7 +1706,7 @@ $ systemctl list-unit-files | jc --systemctl-luf -p
```
### uname -a
```
$ uname -a | jc --uname -p
$ uname -a | jc --uname -p # or: jc -p uname -a
{
"kernel_name": "Linux",
"node_name": "user-ubuntu",
@@ -1553,7 +1720,7 @@ $ uname -a | jc --uname -p
```
### uptime
```
$ uptime | jc --uptime -p
$ uptime | jc --uptime -p # or: jc -p uptime
{
"time": "11:30:44",
"uptime": "1 day, 21:17",
@@ -1565,7 +1732,7 @@ $ uptime | jc --uptime -p
```
### w
```
$ w | jc --w -p
$ w | jc --w -p # or: jc -p w
[
{
"user": "root",
@@ -1599,6 +1766,110 @@ $ w | jc --w -p
}
]
```
### XML files
```
$ cat cd_catalog.xml
<?xml version="1.0" encoding="UTF-8"?>
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
...
$ cat cd_catalog.xml | jc --xml -p
{
"CATALOG": {
"CD": [
{
"TITLE": "Empire Burlesque",
"ARTIST": "Bob Dylan",
"COUNTRY": "USA",
"COMPANY": "Columbia",
"PRICE": "10.90",
"YEAR": "1985"
},
{
"TITLE": "Hide your heart",
"ARTIST": "Bonnie Tyler",
"COUNTRY": "UK",
"COMPANY": "CBS Records",
"PRICE": "9.90",
"YEAR": "1988"
},
...
}
```
### YAML files
```
$ cat istio.yaml
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "default"
namespace: "default"
spec:
peers:
- mtls: {}
---
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "default"
namespace: "default"
spec:
host: "*.default.svc.cluster.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
$ cat istio.yaml | jc --yaml -p
[
{
"apiVersion": "authentication.istio.io/v1alpha1",
"kind": "Policy",
"metadata": {
"name": "default",
"namespace": "default"
},
"spec": {
"peers": [
{
"mtls": {}
}
]
}
},
{
"apiVersion": "networking.istio.io/v1alpha3",
"kind": "DestinationRule",
"metadata": {
"name": "default",
"namespace": "default"
},
"spec": {
"host": "*.default.svc.cluster.local",
"trafficPolicy": {
"tls": {
"mode": "ISTIO_MUTUAL"
}
}
}
}
]
```
## TODO
Future parsers:
- /proc files
@@ -1625,4 +1896,7 @@ Tested on:
## Acknowledgments
- `ifconfig-parser` module from https://github.com/KnightWhoSayNi/ifconfig-parser
- `xmltodict` module from https://github.com/martinblech/xmltodict by Martín Blech
- `ruamel.yaml` library from https://pypi.org/project/ruamel.yaml by Anthon van der Neut
- Parsing code from Conor Heine at https://gist.github.com/cahna/43a1a3ff4d075bcd71f9d7120037a501 adapted for some parsers
- Excellent constructive feedback from Ilya Sher (https://github.com/ilyash-b)

View File

@@ -1,5 +1,25 @@
jc changelog
20200211 v1.7.3
- Add alternative 'magic' syntax: e.g. `jc ls -al`
- Options can now be condensed (e.g. -prq is equivalant to -p -r -q)
20200208 v1.7.2
- Include test fixtures in wheel and sdist
20200205 v1.7.1
- Add YAML file parser
- Add INI file parser
- Add XML file parser
- Add id parser (tested on linux and OSX)
- Add crontab file parser with user support (tested on linux)
- Add __version__ variable to parser modules
- Add exit code on error
- Updated history parser to output "line" as an integer
- Updated compatibility list for some parsers
- Bugfix in crontab file parser: header insertion was clobbering first row
- Just-in-time loading of parser modules instead of loading all at start
20191217 v1.6.1
- Add du parser (tested on linux and OSX)
- Add crontab parser (tested on linux and OSX)

View File

@@ -6,6 +6,7 @@ pydocmd simple jc+ > ../docs/readme.md
pydocmd simple utils+ > ../docs/utils.md
pydocmd simple jc.parsers.arp+ > ../docs/parsers/arp.md
pydocmd simple jc.parsers.crontab+ > ../docs/parsers/crontab.md
pydocmd simple jc.parsers.crontab_u+ > ../docs/parsers/crontab_u.md
pydocmd simple jc.parsers.df+ > ../docs/parsers/df.md
pydocmd simple jc.parsers.dig+ > ../docs/parsers/dig.md
pydocmd simple jc.parsers.du+ > ../docs/parsers/du.md
@@ -14,7 +15,9 @@ pydocmd simple jc.parsers.free+ > ../docs/parsers/free.md
pydocmd simple jc.parsers.fstab+ > ../docs/parsers/fstab.md
pydocmd simple jc.parsers.history+ > ../docs/parsers/history.md
pydocmd simple jc.parsers.hosts+ > ../docs/parsers/hosts.md
pydocmd simple jc.parsers.id+ > ../docs/parsers/id.md
pydocmd simple jc.parsers.ifconfig+ > ../docs/parsers/ifconfig.md
pydocmd simple jc.parsers.ini+ > ../docs/parsers/ini.md
pydocmd simple jc.parsers.iptables+ > ../docs/parsers/iptables.md
pydocmd simple jc.parsers.jobs+ > ../docs/parsers/jobs.md
pydocmd simple jc.parsers.ls+ > ../docs/parsers/ls.md
@@ -36,3 +39,5 @@ pydocmd simple jc.parsers.systemctl_luf+ > ../docs/parsers/systemctl_luf.md
pydocmd simple jc.parsers.uname+ > ../docs/parsers/uname.md
pydocmd simple jc.parsers.uptime+ > ../docs/parsers/uptime.md
pydocmd simple jc.parsers.w+ > ../docs/parsers/w.md
pydocmd simple jc.parsers.xml+ > ../docs/parsers/xml.md
pydocmd simple jc.parsers.yaml+ > ../docs/parsers/yaml.md

View File

@@ -1,9 +1,9 @@
# jc.parsers.crontab
jc - JSON CLI output utility crontab file Parser
jc - JSON CLI output utility crontab command and file Parser
Usage:
specify --crontab as the first argument if the piped input is coming from a crontab file
specify --crontab as the first argument if the piped input is coming from crontab -l or a crontab file
Compatibility:
@@ -11,7 +11,7 @@ Compatibility:
Examples:
$ cat /etc/crontab | jc --crontab -p
$ crontab -l | jc --crontab -p
{
"variables": [
{
@@ -150,8 +150,8 @@ Returns:
{
"variables": [
"name": string,
"value": string
"name": string,
"value": string
],
"schedule": [
{

199
docs/parsers/crontab_u.md Normal file
View File

@@ -0,0 +1,199 @@
# jc.parsers.crontab_u
jc - JSON CLI output utility crontab file Parser
Usage:
specify --crontab-u as the first argument if the piped input is coming from a crontab file with User specified
Compatibility:
'linux', 'darwin', 'aix', 'freebsd'
Examples:
$ cat /etc/crontab | jc --crontab-u -p
{
"variables": [
{
"name": "PATH",
"value": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
},
{
"name": "SHELL",
"value": "/bin/sh"
}
],
"schedule": [
{
"minute": [
"25"
],
"hour": [
"6"
],
"day_of_month": [
"*"
],
"month": [
"*"
],
"day_of_week": [
"*"
],
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )"
},
{
"minute": [
"47"
],
"hour": [
"6"
],
"day_of_month": [
"*"
],
"month": [
"*"
],
"day_of_week": [
"7"
],
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )"
},
{
"minute": [
"52"
],
"hour": [
"6"
],
"day_of_month": [
"1"
],
"month": [
"*"
],
"day_of_week": [
"*"
],
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )"
}
]
}
$ cat /etc/crontab | jc --crontab-u -p -r
{
"variables": [
{
"name": "PATH",
"value": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
},
{
"name": "SHELL",
"value": "/bin/sh"
}
],
"schedule": [
{
"minute": "25",
"hour": "6",
"day_of_month": "*",
"month": "*",
"day_of_week": "*",
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )"
},
{
"minute": "47",
"hour": "6",
"day_of_month": "*",
"month": "*",
"day_of_week": "7",
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )"
},
{
"minute": "52",
"hour": "6",
"day_of_month": "1",
"month": "*",
"day_of_week": "*",
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )"
}
]
}
## info
```python
info(self, /, *args, **kwargs)
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"variables": [
"name": string,
"value": string
],
"schedule": [
{
"occurrence" string,
"minute": [
string
],
"hour": [
string
],
"day_of_month": [
string
],
"month": [
string
],
"day_of_week": [
string
],
"occurrence": string,
"user": string,
"command": string
}
]
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.

View File

@@ -7,7 +7,7 @@ Usage:
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
'linux', 'darwin', 'aix', 'freebsd'
Examples:

View File

@@ -7,26 +7,26 @@ Usage:
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
'linux', 'darwin', 'cygwin', 'aix', 'freebsd'
Examples:
$ history | jc --history -p
[
{
"line": "118",
"line": 118,
"command": "sleep 100"
},
{
"line": "119",
"line": 119,
"command": "ls /bin"
},
{
"line": "120",
"line": 120,
"command": "echo "hello""
},
{
"line": "121",
"line": 121,
"command": "docker images"
},
...
@@ -63,7 +63,7 @@ Returns:
[
{
"line": string,
"line": integer,
"command": string
}
]

133
docs/parsers/id.md Normal file
View File

@@ -0,0 +1,133 @@
# jc.parsers.id
jc - JSON CLI output utility id Parser
Usage:
specify --id as the first argument if the piped input is coming from id
Compatibility:
'linux', 'darwin', 'aix', 'freebsd'
Examples:
$ id | jc --id -p
{
"uid": {
"id": 1000,
"name": "joeuser"
},
"gid": {
"id": 1000,
"name": "joeuser"
},
"groups": [
{
"id": 1000,
"name": "joeuser"
},
{
"id": 10,
"name": "wheel"
}
],
"context": {
"user": "unconfined_u",
"role": "unconfined_r",
"type": "unconfined_t",
"level": "s0-s0:c0.c1023"
}
}
$ id | jc --id -p -r
{
"uid": {
"id": "1000",
"name": "joeuser"
},
"gid": {
"id": "1000",
"name": "joeuser"
},
"groups": [
{
"id": "1000",
"name": "joeuser"
},
{
"id": "10",
"name": "wheel"
}
],
"context": {
"user": "unconfined_u",
"role": "unconfined_r",
"type": "unconfined_t",
"level": "s0-s0:c0.c1023"
}
}
## info
```python
info(self, /, *args, **kwargs)
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"uid": {
"id": integer,
"name": string
},
"gid": {
"id": integer,
"name": string
},
"groups": [
{
"id": integer,
"name": string
},
{
"id": integer,
"name": string
}
],
"context": {
"user": string,
"role": string,
"type": string,
"level": string
}
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

87
docs/parsers/ini.md Normal file
View File

@@ -0,0 +1,87 @@
# jc.parsers.ini
jc - JSON CLI output utility INI Parser
Usage:
specify --ini as the first argument if the piped input is coming from an INI file
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
Examples:
$ cat example.ini
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
Port = 50022
ForwardX11 = no
$ cat example.ini | jc --ini -p
{
"bitbucket.org": {
"serveraliveinterval": "45",
"compression": "yes",
"compressionlevel": "9",
"forwardx11": "yes",
"user": "hg"
},
"topsecret.server.com": {
"serveraliveinterval": "45",
"compression": "yes",
"compressionlevel": "9",
"forwardx11": "no",
"port": "50022"
}
}
## info
```python
info(self, /, *args, **kwargs)
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary representing an ini document:
{
ini document converted to a dictionary
see configparser standard library documentation for more details
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary representing the ini file

99
docs/parsers/xml.md Normal file
View File

@@ -0,0 +1,99 @@
# jc.parsers.xml
jc - JSON CLI output utility XML Parser
Usage:
specify --xml as the first argument if the piped input is coming from an XML file
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
Examples:
$ cat cd_catalog.xml
<?xml version="1.0" encoding="UTF-8"?>
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
...
$ cat cd_catalog.xml | jc --xml -p
{
"CATALOG": {
"CD": [
{
"TITLE": "Empire Burlesque",
"ARTIST": "Bob Dylan",
"COUNTRY": "USA",
"COMPANY": "Columbia",
"PRICE": "10.90",
"YEAR": "1985"
},
{
"TITLE": "Hide your heart",
"ARTIST": "Bonnie Tyler",
"COUNTRY": "UK",
"COMPANY": "CBS Records",
"PRICE": "9.90",
"YEAR": "1988"
},
...
}
## info
```python
info(self, /, *args, **kwargs)
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary representing an XML document:
{
XML Document converted to a Dictionary
See https://github.com/martinblech/xmltodict for details
}
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.

113
docs/parsers/yaml.md Normal file
View File

@@ -0,0 +1,113 @@
# jc.parsers.yaml
jc - JSON CLI output utility YAML Parser
Usage:
specify --yaml as the first argument if the piped input is coming from a YAML file
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
Examples:
$ cat istio-mtls-permissive.yaml
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "default"
namespace: "default"
spec:
peers:
- mtls: {}
---
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "default"
namespace: "default"
spec:
host: "*.default.svc.cluster.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
$ cat istio-mtls-permissive.yaml | jc --yaml -p
[
{
"apiVersion": "authentication.istio.io/v1alpha1",
"kind": "Policy",
"metadata": {
"name": "default",
"namespace": "default"
},
"spec": {
"peers": [
{
"mtls": {}
}
]
}
},
{
"apiVersion": "networking.istio.io/v1alpha3",
"kind": "DestinationRule",
"metadata": {
"name": "default",
"namespace": "default"
},
"spec": {
"host": "*.default.svc.cluster.local",
"trafficPolicy": {
"tls": {
"mode": "ISTIO_MUTUAL"
}
}
}
}
]
## info
```python
info(self, /, *args, **kwargs)
```
## process
```python
process(proc_data)
```
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Each dictionary represents a YAML document:
[
{
YAML Document converted to a Dictionary
See https://pypi.org/project/ruamel.yaml for details
}
]
## parse
```python
parse(data, raw=False, quiet=False)
```
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.

307
jc/cli.py
View File

@@ -3,113 +3,126 @@
JC cli module
"""
import sys
import os
import shlex
import importlib
import textwrap
import signal
import json
import jc.utils
import jc.parsers.arp
import jc.parsers.crontab
import jc.parsers.df
import jc.parsers.dig
import jc.parsers.du
import jc.parsers.env
import jc.parsers.free
import jc.parsers.fstab
import jc.parsers.history
import jc.parsers.hosts
import jc.parsers.ifconfig
import jc.parsers.iptables
import jc.parsers.jobs
import jc.parsers.ls
import jc.parsers.lsblk
import jc.parsers.lsmod
import jc.parsers.lsof
import jc.parsers.mount
import jc.parsers.netstat
import jc.parsers.pip_list
import jc.parsers.pip_show
import jc.parsers.ps
import jc.parsers.route
import jc.parsers.ss
import jc.parsers.stat
import jc.parsers.systemctl
import jc.parsers.systemctl_lj
import jc.parsers.systemctl_ls
import jc.parsers.systemctl_luf
import jc.parsers.uname
import jc.parsers.uptime
import jc.parsers.w
parser_map = {
'--arp': jc.parsers.arp,
'--crontab': jc.parsers.crontab,
'--df': jc.parsers.df,
'--dig': jc.parsers.dig,
'--du': jc.parsers.du,
'--env': jc.parsers.env,
'--free': jc.parsers.free,
'--fstab': jc.parsers.fstab,
'--history': jc.parsers.history,
'--hosts': jc.parsers.hosts,
'--ifconfig': jc.parsers.ifconfig,
'--iptables': jc.parsers.iptables,
'--jobs': jc.parsers.jobs,
'--ls': jc.parsers.ls,
'--lsblk': jc.parsers.lsblk,
'--lsmod': jc.parsers.lsmod,
'--lsof': jc.parsers.lsof,
'--mount': jc.parsers.mount,
'--netstat': jc.parsers.netstat,
'--pip-list': jc.parsers.pip_list,
'--pip-show': jc.parsers.pip_show,
'--ps': jc.parsers.ps,
'--route': jc.parsers.route,
'--ss': jc.parsers.ss,
'--stat': jc.parsers.stat,
'--systemctl': jc.parsers.systemctl,
'--systemctl-lj': jc.parsers.systemctl_lj,
'--systemctl-ls': jc.parsers.systemctl_ls,
'--systemctl-luf': jc.parsers.systemctl_luf,
'--uname': jc.parsers.uname,
'--uptime': jc.parsers.uptime,
'--w': jc.parsers.w
}
class info():
version = '1.6.1'
version = '1.7.3'
description = 'jc cli output JSON conversion tool'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
__version__ = info.version
parsers = [
'arp',
'crontab',
'crontab-u',
'df',
'dig',
'du',
'env',
'free',
'fstab',
'history',
'hosts',
'id',
'ifconfig',
'ini',
'iptables',
'jobs',
'ls',
'lsblk',
'lsmod',
'lsof',
'mount',
'netstat',
'pip-list',
'pip-show',
'ps',
'route',
'ss',
'stat',
'systemctl',
'systemctl-lj',
'systemctl-ls',
'systemctl-luf',
'uname',
'uptime',
'w',
'xml',
'yaml'
]
def ctrlc(signum, frame):
exit()
"""exit with error on SIGINT"""
sys.exit(1)
def parsers_text():
def parser_shortname(parser_argument):
"""short name of the parser with dashes and no -- prefix"""
return parser_argument[2:]
def parser_argument(parser):
"""short name of the parser with dashes and with -- prefix"""
return f'--{parser}'
def parser_mod_shortname(parser):
"""short name of the parser's module name (no -- prefix and dashes converted to underscores)"""
return parser.replace('--', '').replace('-', '_')
def parser_module(parser):
"""import the module just in time and return the module object"""
importlib.import_module('jc.parsers.' + parser_mod_shortname(parser))
return getattr(jc.parsers, parser_mod_shortname(parser))
def parsers_text(indent=0, pad=0):
"""return the argument and description information from each parser"""
ptext = ''
for parser in parser_map:
if hasattr(parser_map[parser], 'info'):
padding = 16 - len(parser)
for parser in parsers:
parser_arg = parser_argument(parser)
parser_mod = parser_module(parser)
if hasattr(parser_mod, 'info'):
parser_desc = parser_mod.info.description
padding = pad - len(parser_arg)
padding_char = ' '
indent_text = padding_char * indent
padding_text = padding_char * padding
ptext += ' ' + parser + padding_text + parser_map[parser].info.description + '\n'
ptext += indent_text + parser_arg + padding_text + parser_desc + '\n'
return ptext
def about_jc():
"""return jc info and the contents of each parser.info as a dictionary"""
parser_list = []
for parser in parser_map:
if hasattr(parser_map[parser], 'info'):
for parser in parsers:
parser_mod = parser_module(parser)
if hasattr(parser_mod, 'info'):
info_dict = {}
info_dict['name'] = parser_map[parser].__name__.split('.')[-1]
info_dict['argument'] = parser
parser_entry = vars(parser_map[parser].info)
info_dict['name'] = parser_mod.__name__.split('.')[-1]
info_dict['argument'] = parser_argument(parser)
parser_entry = vars(parser_mod.info)
for k, v in parser_entry.items():
if not k.startswith('__'):
info_dict[k] = v
parser_list.append(info_dict)
return {
@@ -124,24 +137,37 @@ def about_jc():
def helptext(message):
parsers_string = parsers_text()
"""return the help text with the list of parsers"""
parsers_string = parsers_text(indent=12, pad=17)
helptext_string = f'''
jc: {message}
Usage: jc PARSER [OPTIONS]
Usage: COMMAND | jc PARSER [OPTIONS]
or
COMMAND | jc [OPTIONS] PARSER
or magic syntax:
jc [OPTIONS] COMMAND
Parsers:
{parsers_string}
Options:
-a about jc
-d debug - show trace messages
-p pretty print output
-q quiet - suppress warnings
-r raw JSON output
-a about jc
-d debug - show trace messages
-p pretty print output
-q quiet - suppress warnings
-r raw JSON output
Example:
ls -al | jc --ls -p
or using the magic syntax:
jc -p ls -al
'''
print(textwrap.dedent(helptext_string), file=sys.stderr)
@@ -153,34 +179,108 @@ def json_out(data, pretty=False):
print(json.dumps(data))
def magic():
"""Parse with magic syntax: jc -p ls -al"""
if len(sys.argv) > 1 and not sys.argv[1].startswith('--'):
parser_info = about_jc()['parsers']
# correctly parse escape characters and spaces with shlex
args_given = " ".join(map(shlex.quote, sys.argv[1:])).split()
options = []
found_parser = None
# find the options
if args_given[0].startswith('-'):
p = 0
for i, arg in list(enumerate(args_given)):
# parser found - use standard syntax
if arg.startswith('--'):
return
# option found - populate option list
elif arg.startswith('-'):
options.append(args_given.pop(i - p)[1:])
p = p + 1
# command found if iterator didn't already stop - stop iterating
else:
break
# find the command and parser
for parser in parser_info:
if 'magic_commands' in parser:
# first pass for two word commands: e.g. 'pip list'
for magic_command in parser['magic_commands']:
try:
if ' '.join(args_given[0:2]) == magic_command:
found_parser = parser['argument']
break
# No command found - go to next loop (for cases like 'jc -a')
except Exception:
break
# second pass for one word commands: e.g. 'ls'
if not found_parser:
for magic_command in parser['magic_commands']:
try:
if args_given[0] == magic_command:
found_parser = parser['argument']
break
# No command found - use standard syntax (for cases like 'jc -a')
except Exception:
return
# construct a new command line using the standard syntax: COMMAND | jc --PARSER -OPTIONS
run_command = ' '.join(args_given)
if found_parser:
if options:
cmd_options = '-' + ''.join(options)
else:
cmd_options = ''
whole_command = ' '.join([run_command, '|', 'jc', found_parser, cmd_options])
os.system(whole_command)
exit()
else:
helptext(f'parser not found for "{run_command}"')
sys.exit(1)
def main():
# break on ctrl-c keyboard interrupt
signal.signal(signal.SIGINT, ctrlc)
# try magic syntax first: e.g. jc -p ls -al
magic()
options = []
debug = False
pretty = False
quiet = False
raw = False
# options
if '-d' in sys.argv:
for opt in sys.argv:
if opt.startswith('-') and not opt.startswith('--'):
for flag in opt[1:]:
options.append(flag)
if 'd' in options:
debug = True
if '-p' in sys.argv:
if 'p' in options:
pretty = True
if '-q' in sys.argv:
if 'q' in options:
quiet = True
if '-r' in sys.argv:
if 'r' in options:
raw = True
if '-a' in sys.argv:
if 'a' in options:
json_out(about_jc(), pretty=pretty)
exit()
if sys.stdin.isatty():
helptext('missing piped data')
exit()
sys.exit(1)
data = sys.stdin.read()
@@ -188,25 +288,32 @@ def main():
if debug:
for arg in sys.argv:
if arg in parser_map:
result = parser_map[arg].parse(data, raw=raw, quiet=quiet)
parser_name = parser_shortname(arg)
if parser_name in parsers:
# load parser module just in time so we don't need to load all modules
parser = parser_module(arg)
result = parser.parse(data, raw=raw, quiet=quiet)
found = True
break
else:
for arg in sys.argv:
if arg in parser_map:
parser_name = parser_shortname(arg)
if parser_name in parsers:
# load parser module just in time so we don't need to load all modules
parser = parser_module(arg)
try:
result = parser_map[arg].parse(data, raw=raw, quiet=quiet)
result = parser.parse(data, raw=raw, quiet=quiet)
found = True
break
except:
parser_name = parser_map[arg].__name__.split('.')[-1]
except Exception:
jc.utils.error_message(f'{parser_name} parser could not parse the input data. Did you use the correct parser?\n For details use the -d option.')
exit(1)
sys.exit(1)
if not found:
helptext('missing or incorrect arguments')
exit()
sys.exit(1)
json_out(result, pretty=pretty)

View File

@@ -92,12 +92,16 @@ import jc.parsers.universal
class info():
version = '1.1'
description = 'arp parser'
description = 'arp command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'aix', 'freebsd', 'darwin']
magic_commands = ['arp']
__version__ = info.version
def process(proc_data):

View File

@@ -1,8 +1,8 @@
"""jc - JSON CLI output utility crontab file Parser
"""jc - JSON CLI output utility crontab command and file Parser
Usage:
specify --crontab as the first argument if the piped input is coming from a crontab file
specify --crontab as the first argument if the piped input is coming from crontab -l or a crontab file
Compatibility:
@@ -10,7 +10,7 @@ Compatibility:
Examples:
$ cat /etc/crontab | jc --crontab -p
$ crontab -l | jc --crontab -p
{
"variables": [
{
@@ -132,14 +132,18 @@ import jc.parsers.universal
class info():
version = '1.0'
description = 'crontab file parser'
version = '1.1'
description = 'crontab command and file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'aix', 'freebsd']
magic_commands = ['crontab']
__version__ = info.version
def process(proc_data):
@@ -156,8 +160,8 @@ def process(proc_data):
{
"variables": [
"name": string,
"value": string
"name": string,
"value": string
],
"schedule": [
{
@@ -249,12 +253,12 @@ def parse(data, raw=False, quiet=False):
'command': cmd})
# Add header row for parsing
cleandata[0] = 'minute hour day_of_month month day_of_week command'
cleandata[:0] = ['minute hour day_of_month month day_of_week command']
if len(cleandata) > 1:
cron_list = jc.parsers.universal.simple_table_parse(cleandata)
raw_output['schedule'] = cron_list
raw_output['schedule'] = cron_list
# Add shortcut entries back in
for item in shortcut_list:

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

@@ -0,0 +1,273 @@
"""jc - JSON CLI output utility crontab file Parser
Usage:
specify --crontab-u as the first argument if the piped input is coming from a crontab file with User specified
Compatibility:
'linux', 'darwin', 'aix', 'freebsd'
Examples:
$ cat /etc/crontab | jc --crontab-u -p
{
"variables": [
{
"name": "PATH",
"value": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
},
{
"name": "SHELL",
"value": "/bin/sh"
}
],
"schedule": [
{
"minute": [
"25"
],
"hour": [
"6"
],
"day_of_month": [
"*"
],
"month": [
"*"
],
"day_of_week": [
"*"
],
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )"
},
{
"minute": [
"47"
],
"hour": [
"6"
],
"day_of_month": [
"*"
],
"month": [
"*"
],
"day_of_week": [
"7"
],
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )"
},
{
"minute": [
"52"
],
"hour": [
"6"
],
"day_of_month": [
"1"
],
"month": [
"*"
],
"day_of_week": [
"*"
],
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )"
}
]
}
$ cat /etc/crontab | jc --crontab-u -p -r
{
"variables": [
{
"name": "PATH",
"value": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"
},
{
"name": "SHELL",
"value": "/bin/sh"
}
],
"schedule": [
{
"minute": "25",
"hour": "6",
"day_of_month": "*",
"month": "*",
"day_of_week": "*",
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )"
},
{
"minute": "47",
"hour": "6",
"day_of_month": "*",
"month": "*",
"day_of_week": "7",
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )"
},
{
"minute": "52",
"hour": "6",
"day_of_month": "1",
"month": "*",
"day_of_week": "*",
"user": "root",
"command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )"
}
]
}
"""
import jc.utils
import jc.parsers.universal
class info():
version = '1.0'
description = 'crontab file parser with user support'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'aix', 'freebsd']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"variables": [
"name": string,
"value": string
],
"schedule": [
{
"occurrence" string,
"minute": [
string
],
"hour": [
string
],
"day_of_month": [
string
],
"month": [
string
],
"day_of_week": [
string
],
"occurrence": string,
"user": string,
"command": string
}
]
}
"""
# put itmes in lists
try:
for entry in proc_data['schedule']:
entry['minute'] = entry['minute'].split(',')
entry['hour'] = entry['hour'].split(',')
entry['day_of_month'] = entry['day_of_month'].split(',')
entry['month'] = entry['month'].split(',')
entry['day_of_week'] = entry['day_of_week'].split(',')
except (KeyError):
pass
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = {}
cleandata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, cleandata))
# Clear any commented lines
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().find('#') == 0:
cleandata.pop(i)
# Pop any variable assignment lines
cron_var = []
for i, line in reversed(list(enumerate(cleandata))):
if line.find('=') != -1:
var_line = cleandata.pop(i)
var_name = var_line.split('=', maxsplit=1)[0].strip()
var_value = var_line.split('=', maxsplit=1)[1].strip()
cron_var.append({'name': var_name,
'value': var_value})
raw_output['variables'] = cron_var
# Pop any shortcut lines
shortcut_list = []
for i, line in reversed(list(enumerate(cleandata))):
if line.strip().startswith('@'):
shortcut_line = cleandata.pop(i)
occurrence = shortcut_line.split(maxsplit=1)[0].strip().lstrip('@')
usr = shortcut_line.split(maxsplit=2)[1].strip()
cmd = shortcut_line.split(maxsplit=2)[2].strip()
shortcut_list.append({'occurrence': occurrence,
'user': usr,
'command': cmd})
# Add header row for parsing
cleandata[:0] = ['minute hour day_of_month month day_of_week user command']
if len(cleandata) > 1:
cron_list = jc.parsers.universal.simple_table_parse(cleandata)
raw_output['schedule'] = cron_list
# Add shortcut entries back in
for item in shortcut_list:
raw_output['schedule'].append(item)
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -74,12 +74,16 @@ import jc.parsers.universal
class info():
version = '1.1'
description = 'df parser'
description = 'df command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin']
magic_commands = ['df']
__version__ = info.version
def process(proc_data):

View File

@@ -325,12 +325,16 @@ import jc.utils
class info():
version = '1.0'
description = 'dig parser'
description = 'dig command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'aix', 'freebsd', 'darwin']
magic_commands = ['dig']
__version__ = info.version
def process(proc_data):

View File

@@ -6,7 +6,7 @@ Usage:
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
'linux', 'darwin', 'aix', 'freebsd'
Examples:
@@ -73,14 +73,18 @@ import jc.parsers.universal
class info():
version = '1.0'
description = 'du parser'
version = '1.1'
description = 'du command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
compatible = ['linux', 'darwin', 'aix', 'freebsd']
magic_commands = ['du']
__version__ = info.version
def process(proc_data):

View File

@@ -53,12 +53,16 @@ import jc.utils
class info():
version = '1.1'
description = 'env parser'
description = 'env command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
magic_commands = ['env']
__version__ = info.version
def process(proc_data):

View File

@@ -21,13 +21,17 @@ import jc.utils
class info():
version = '1.0'
description = 'foo parser'
description = 'foo command parser'
author = 'John Doe'
author_email = 'johndoe@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
magic_commands = ['foo']
__version__ = info.version
def process(proc_data):

View File

@@ -54,12 +54,16 @@ import jc.parsers.universal
class info():
version = '1.0'
description = 'free parser'
description = 'free command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['free']
__version__ = info.version
def process(proc_data):

View File

@@ -71,7 +71,7 @@ import jc.utils
class info():
version = '1.0'
description = '/etc/fstab file parser'
description = 'fstab file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
@@ -79,6 +79,9 @@ class info():
compatible = ['linux']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.

View File

@@ -6,26 +6,26 @@ Usage:
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
'linux', 'darwin', 'cygwin', 'aix', 'freebsd'
Examples:
$ history | jc --history -p
[
{
"line": "118",
"line": 118,
"command": "sleep 100"
},
{
"line": "119",
"line": 119,
"command": "ls /bin"
},
{
"line": "120",
"line": 120,
"command": "echo \"hello\""
},
{
"line": "121",
"line": 121,
"command": "docker images"
},
...
@@ -45,12 +45,16 @@ import jc
class info():
version = '1.1'
description = 'history parser'
description = 'history command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
magic_commands = ['history']
__version__ = info.version
def process(proc_data):
@@ -67,7 +71,7 @@ def process(proc_data):
[
{
"line": string,
"line": integer,
"command": string
}
]
@@ -81,6 +85,16 @@ def process(proc_data):
proc_line['command'] = v
processed.append(proc_line)
for entry in processed:
int_list = ['line']
for key in int_list:
if key in entry:
try:
key_int = int(entry[key])
entry[key] = key_int
except (ValueError):
entry[key] = None
return processed

View File

@@ -70,6 +70,9 @@ class info():
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.

215
jc/parsers/id.py Normal file
View File

@@ -0,0 +1,215 @@
"""jc - JSON CLI output utility id Parser
Usage:
specify --id as the first argument if the piped input is coming from id
Compatibility:
'linux', 'darwin', 'aix', 'freebsd'
Examples:
$ id | jc --id -p
{
"uid": {
"id": 1000,
"name": "joeuser"
},
"gid": {
"id": 1000,
"name": "joeuser"
},
"groups": [
{
"id": 1000,
"name": "joeuser"
},
{
"id": 10,
"name": "wheel"
}
],
"context": {
"user": "unconfined_u",
"role": "unconfined_r",
"type": "unconfined_t",
"level": "s0-s0:c0.c1023"
}
}
$ id | jc --id -p -r
{
"uid": {
"id": "1000",
"name": "joeuser"
},
"gid": {
"id": "1000",
"name": "joeuser"
},
"groups": [
{
"id": "1000",
"name": "joeuser"
},
{
"id": "10",
"name": "wheel"
}
],
"context": {
"user": "unconfined_u",
"role": "unconfined_r",
"type": "unconfined_t",
"level": "s0-s0:c0.c1023"
}
}
"""
import jc.utils
class info():
version = '1.0'
description = 'id command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# details = 'enter any other details here'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'aix', 'freebsd']
magic_commands = ['id']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary. Structured data with the following schema:
{
"uid": {
"id": integer,
"name": string
},
"gid": {
"id": integer,
"name": string
},
"groups": [
{
"id": integer,
"name": string
},
{
"id": integer,
"name": string
}
],
"context": {
"user": string,
"role": string,
"type": string,
"level": string
}
}
"""
if 'uid' in proc_data:
if 'id' in proc_data['uid']:
try:
proc_data['uid']['id'] = int(proc_data['uid']['id'])
except (ValueError):
proc_data['uid']['id'] = None
if 'gid' in proc_data:
if 'id' in proc_data['gid']:
try:
proc_data['gid']['id'] = int(proc_data['gid']['id'])
except (ValueError):
proc_data['gid']['id'] = None
if 'groups' in proc_data:
for group in proc_data['groups']:
if 'id' in group:
try:
group['id'] = int(group['id'])
except (ValueError):
group['id'] = None
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = {}
cleandata = data.split()
# Clear any blank lines
cleandata = list(filter(None, cleandata))
if cleandata:
for section in cleandata:
if section.startswith('uid'):
uid_parsed = section.replace('(', '=').replace(')', '=')
uid_parsed = uid_parsed.split('=')
raw_output['uid'] = {}
raw_output['uid']['id'] = uid_parsed[1]
raw_output['uid']['name'] = uid_parsed[2]
if section.startswith('gid'):
gid_parsed = section.replace('(', '=').replace(')', '=')
gid_parsed = gid_parsed.split('=')
raw_output['gid'] = {}
raw_output['gid']['id'] = gid_parsed[1]
raw_output['gid']['name'] = gid_parsed[2]
if section.startswith('groups'):
groups_parsed = section.replace('(', '=').replace(')', '=')
groups_parsed = groups_parsed.replace('groups=', '')
groups_parsed = groups_parsed.split(',')
raw_output['groups'] = []
for group in groups_parsed:
group_dict = {}
grp_parsed = group.split('=')
group_dict['id'] = grp_parsed[0]
group_dict['name'] = grp_parsed[1]
raw_output['groups'].append(group_dict)
if section.startswith('context'):
context_parsed = section.replace('context=', '')
context_parsed = context_parsed.split(':', maxsplit=3)
raw_output['context'] = {}
raw_output['context']['user'] = context_parsed[0]
raw_output['context']['role'] = context_parsed[1]
raw_output['context']['type'] = context_parsed[2]
raw_output['context']['level'] = context_parsed[3]
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -147,13 +147,17 @@ from ifconfigparser import IfconfigParser
class info():
version = '1.5'
description = 'ifconfig parser'
description = 'ifconfig command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using ifconfig-parser package from https://github.com/KnightWhoSayNi/ifconfig-parser'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'aix', 'freebsd', 'darwin']
magic_commands = ['ifconfig']
__version__ = info.version
def process(proc_data):

112
jc/parsers/ini.py Normal file
View File

@@ -0,0 +1,112 @@
"""jc - JSON CLI output utility INI Parser
Usage:
specify --ini as the first argument if the piped input is coming from an INI file
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
Examples:
$ cat example.ini
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
Port = 50022
ForwardX11 = no
$ cat example.ini | jc --ini -p
{
"bitbucket.org": {
"serveraliveinterval": "45",
"compression": "yes",
"compressionlevel": "9",
"forwardx11": "yes",
"user": "hg"
},
"topsecret.server.com": {
"serveraliveinterval": "45",
"compression": "yes",
"compressionlevel": "9",
"forwardx11": "no",
"port": "50022"
}
}
"""
import jc.utils
import configparser
class info():
version = '1.0'
description = 'INI file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using configparser from the standard library'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary representing an ini document:
{
ini document converted to a dictionary
see configparser standard library documentation for more details
}
"""
# No further processing
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary representing the ini file
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = {}
if data:
ini = configparser.ConfigParser()
ini.read_string(data)
raw_output = {s: dict(ini.items(s)) for s in ini.sections()}
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -135,12 +135,16 @@ import jc.utils
class info():
version = '1.1'
description = 'iptables parser'
description = 'iptables command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['iptables']
__version__ = info.version
def process(proc_data):

View File

@@ -78,12 +78,16 @@ import jc.utils
class info():
version = '1.0'
description = 'jobs parser'
description = 'jobs command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
magic_commands = ['jobs']
__version__ = info.version
def process(proc_data):

View File

@@ -145,12 +145,16 @@ import jc.utils
class info():
version = '1.0'
description = 'ls parser'
description = 'ls command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
magic_commands = ['ls']
__version__ = info.version
def process(proc_data):

View File

@@ -217,12 +217,16 @@ import jc.parsers.universal
class info():
version = '1.3'
description = 'lsblk parser'
description = 'lsblk command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['lsblk']
__version__ = info.version
def process(proc_data):

View File

@@ -108,12 +108,16 @@ import jc.parsers.universal
class info():
version = '1.1'
description = 'lsmod parser'
description = 'lsmod command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['lsmod']
__version__ = info.version
def process(proc_data):

View File

@@ -98,12 +98,16 @@ import jc.parsers.universal
class info():
version = '1.0'
description = 'lsof parser'
description = 'lsof command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['lsof']
__version__ = info.version
def process(proc_data):

View File

@@ -57,12 +57,16 @@ import jc.utils
class info():
version = '1.1'
description = 'mount parser'
description = 'mount command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin']
magic_commands = ['mount']
__version__ = info.version
def process(proc_data):

View File

@@ -314,12 +314,16 @@ import jc.utils
class info():
version = '1.2'
description = 'netstat parser'
description = 'netstat command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['netstat']
__version__ = info.version
def process(proc_data):

View File

@@ -33,12 +33,16 @@ import jc.parsers.universal
class info():
version = '1.0'
description = 'pip-list parser'
description = 'pip list command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
magic_commands = ['pip list', 'pip3 list']
__version__ = info.version
def process(proc_data):

View File

@@ -43,12 +43,16 @@ import jc.utils
class info():
version = '1.0'
description = 'pip-show parser'
description = 'pip show command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
magic_commands = ['pip show', 'pip3 show']
__version__ = info.version
def process(proc_data):

View File

@@ -178,12 +178,16 @@ import jc.parsers.universal
class info():
version = '1.1'
description = 'ps parser'
description = 'ps command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
magic_commands = ['ps']
__version__ = info.version
def process(proc_data):

View File

@@ -102,12 +102,16 @@ import jc.parsers.universal
class info():
version = '1.0'
description = 'route parser'
description = 'route command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['route']
__version__ = info.version
def process(proc_data):

View File

@@ -252,12 +252,16 @@ import jc.utils
class info():
version = '1.0'
description = 'ss parser'
description = 'ss command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['ss']
__version__ = info.version
def process(proc_data):

View File

@@ -105,12 +105,16 @@ import jc.utils
class info():
version = '1.0'
description = 'stat parser'
description = 'stat command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['stat']
__version__ = info.version
def process(proc_data):

View File

@@ -41,12 +41,16 @@ import jc.utils
class info():
version = '1.0'
description = 'systemctl parser'
description = 'systemctl command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['systemctl']
__version__ = info.version
def process(proc_data):

View File

@@ -60,12 +60,16 @@ import jc.utils
class info():
version = '1.0'
description = 'systemctl list-jobs parser'
description = 'systemctl list-jobs command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['systemctl list-jobs']
__version__ = info.version
def process(proc_data):

View File

@@ -35,12 +35,16 @@ import jc.utils
class info():
version = '1.0'
description = 'systemctl list-sockets parser'
description = 'systemctl list-sockets command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['systemctl list-sockets']
__version__ = info.version
def process(proc_data):

View File

@@ -32,12 +32,16 @@ import jc.utils
class info():
version = '1.0'
description = 'systemctl list-unit-files parser'
description = 'systemctl list-unit-files command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux']
magic_commands = ['systemctl list-unit-files']
__version__ = info.version
def process(proc_data):

View File

@@ -31,12 +31,16 @@ import jc.utils
class info():
version = '1.1'
description = 'uname -a parser'
description = 'uname -a command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin']
magic_commands = ['uname']
__version__ = info.version
def process(proc_data):

View File

@@ -35,12 +35,16 @@ import jc.utils
class info():
version = '1.0'
description = 'uptime parser'
description = 'uptime command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
magic_commands = ['uptime']
__version__ = info.version
def process(proc_data):

View File

@@ -84,12 +84,16 @@ import jc.utils
class info():
version = '1.0'
description = 'w parser'
description = 'w command parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd']
magic_commands = ['w']
__version__ = info.version
def process(proc_data):

120
jc/parsers/xml.py Normal file
View File

@@ -0,0 +1,120 @@
"""jc - JSON CLI output utility XML Parser
Usage:
specify --xml as the first argument if the piped input is coming from an XML file
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
Examples:
$ cat cd_catalog.xml
<?xml version="1.0" encoding="UTF-8"?>
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
...
$ cat cd_catalog.xml | jc --xml -p
{
"CATALOG": {
"CD": [
{
"TITLE": "Empire Burlesque",
"ARTIST": "Bob Dylan",
"COUNTRY": "USA",
"COMPANY": "Columbia",
"PRICE": "10.90",
"YEAR": "1985"
},
{
"TITLE": "Hide your heart",
"ARTIST": "Bonnie Tyler",
"COUNTRY": "UK",
"COMPANY": "CBS Records",
"PRICE": "9.90",
"YEAR": "1988"
},
...
}
"""
import jc.utils
import xmltodict
class info():
version = '1.0'
description = 'XML file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using the xmltodict library at https://github.com/martinblech/xmltodict'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
Dictionary representing an XML document:
{
XML Document converted to a Dictionary
See https://github.com/martinblech/xmltodict for details
}
"""
# No further processing
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
Dictionary. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
if data:
raw_output = xmltodict.parse(data)
if raw:
return raw_output
else:
return process(raw_output)

137
jc/parsers/yaml.py Normal file
View File

@@ -0,0 +1,137 @@
"""jc - JSON CLI output utility YAML Parser
Usage:
specify --yaml as the first argument if the piped input is coming from a YAML file
Compatibility:
'linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'
Examples:
$ cat istio-mtls-permissive.yaml
apiVersion: "authentication.istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "default"
namespace: "default"
spec:
peers:
- mtls: {}
---
apiVersion: "networking.istio.io/v1alpha3"
kind: "DestinationRule"
metadata:
name: "default"
namespace: "default"
spec:
host: "*.default.svc.cluster.local"
trafficPolicy:
tls:
mode: ISTIO_MUTUAL
$ cat istio-mtls-permissive.yaml | jc --yaml -p
[
{
"apiVersion": "authentication.istio.io/v1alpha1",
"kind": "Policy",
"metadata": {
"name": "default",
"namespace": "default"
},
"spec": {
"peers": [
{
"mtls": {}
}
]
}
},
{
"apiVersion": "networking.istio.io/v1alpha3",
"kind": "DestinationRule",
"metadata": {
"name": "default",
"namespace": "default"
},
"spec": {
"host": "*.default.svc.cluster.local",
"trafficPolicy": {
"tls": {
"mode": "ISTIO_MUTUAL"
}
}
}
}
]
"""
import jc.utils
from ruamel.yaml import YAML
class info():
version = '1.0'
description = 'YAML file parser'
author = 'Kelly Brazil'
author_email = 'kellyjonbrazil@gmail.com'
details = 'Using the ruamel.yaml library at https://pypi.org/project/ruamel.yaml'
# compatible options: linux, darwin, cygwin, win32, aix, freebsd
compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd']
__version__ = info.version
def process(proc_data):
"""
Final processing to conform to the schema.
Parameters:
proc_data: (dictionary) raw structured data to process
Returns:
List of dictionaries. Each dictionary represents a YAML document:
[
{
YAML Document converted to a Dictionary
See https://pypi.org/project/ruamel.yaml for details
}
]
"""
# No further processing
return proc_data
def parse(data, raw=False, quiet=False):
"""
Main text parsing function
Parameters:
data: (string) text data to parse
raw: (boolean) output preprocessed JSON if True
quiet: (boolean) suppress warning messages if True
Returns:
List of dictionaries. Raw or processed structured data.
"""
if not quiet:
jc.utils.compatibility(__name__, info.compatible)
raw_output = []
yaml = YAML(typ='safe')
for document in yaml.load_all(data):
raw_output.append(document)
if raw:
return raw_output
else:
return process(raw_output)

View File

@@ -5,12 +5,14 @@ with open('README.md', 'r') as f:
setuptools.setup(
name='jc',
version='1.6.1',
version='1.7.3',
author='Kelly Brazil',
author_email='kellyjonbrazil@gmail.com',
description='This tool serializes the output of popular command line tools to structured JSON output.',
description='This tool serializes the output of popular command line tools and filetypes to structured JSON output.',
install_requires=[
'ifconfig-parser>=0.0.5'
'ifconfig-parser>=0.0.5',
'ruamel.yaml>=0.15.0',
'xmltodict>=0.12.0'
],
license='MIT',
long_description=long_description,
@@ -18,6 +20,7 @@ setuptools.setup(
python_requires='>=3.6',
url='https://github.com/kellyjonbrazil/jc',
packages=setuptools.find_packages(),
include_package_data=True,
entry_points={
'console_scripts': [
'jc=jc.cli:main'

View File

@@ -0,0 +1 @@
{"variables": [{"name": "MAILTO", "value": "root"}, {"name": "PATH", "value": "/sbin:/bin:/usr/sbin:/usr/bin"}, {"name": "SHELL", "value": "/bin/bash"}], "schedule": [{"minute": ["01"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "user": "root", "command": "run-parts /etc/cron.hourly"}, {"occurrence": "hourly", "user": "root", "command": "/usr/local/bin/backup"}]}

View File

@@ -0,0 +1,6 @@
# Run the hourly jobs
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
@hourly root /usr/local/bin/backup
01 * * * * root run-parts /etc/cron.hourly

View File

@@ -1 +1 @@
{"variables": [{"name": "MAILTO", "value": "root"}, {"name": "PATH", "value": "/sbin:/bin:/usr/sbin:/usr/bin"}, {"name": "SHELL", "value": "/bin/bash"}], "schedule": [{"minute": ["*"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/devdaily.com/bin/check-apache.sh"}, {"minute": ["5"], "hour": ["10-11", "22"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/devdaily.com/bin/mk-new-links.php"}, {"minute": ["30"], "hour": ["4/2"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/devdaily.com/bin/create-all-backups.sh"}, {"minute": ["5"], "hour": ["0", "4", "10", "16"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/devdaily.com/bin/create-cat-list.sh"}, {"minute": ["5"], "hour": ["0"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/devdaily.com/bin/resetContactForm.sh"}, {"minute": ["0", "20", "40"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/bin/ads/freshMint.sh"}, {"minute": ["5", "25", "45"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/bin/ads/greenTaffy.sh"}, {"minute": ["10", "30", "50"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/bin/ads/raspberry.sh"}, {"minute": ["15", "35", "55"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/bin/ads/robinsEgg.sh"}, {"occurrence": "yearly", "command": "/home/maverick/bin/annual-maintenance"}, {"occurrence": "reboot", "command": "/home/cleanup"}, {"occurrence": "monthly", "command": "/home/maverick/bin/tape-backup"}]}
{"variables": [{"name": "MAILTO", "value": "root"}, {"name": "PATH", "value": "/sbin:/bin:/usr/sbin:/usr/bin"}, {"name": "SHELL", "value": "/bin/bash"}], "schedule": [{"minute": ["0"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/usr/bin/wget -O - -q -t 1 http://localhost/cron.php"}, {"minute": ["*"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/devdaily.com/bin/check-apache.sh"}, {"minute": ["5"], "hour": ["10-11", "22"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/devdaily.com/bin/mk-new-links.php"}, {"minute": ["30"], "hour": ["4/2"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/devdaily.com/bin/create-all-backups.sh"}, {"minute": ["5"], "hour": ["0", "4", "10", "16"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/devdaily.com/bin/create-cat-list.sh"}, {"minute": ["5"], "hour": ["0"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/devdaily.com/bin/resetContactForm.sh"}, {"minute": ["0", "20", "40"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/bin/ads/freshMint.sh"}, {"minute": ["5", "25", "45"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/bin/ads/greenTaffy.sh"}, {"minute": ["10", "30", "50"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/bin/ads/raspberry.sh"}, {"minute": ["15", "35", "55"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "/var/www/bin/ads/robinsEgg.sh"}, {"occurrence": "yearly", "command": "/home/maverick/bin/annual-maintenance"}, {"occurrence": "reboot", "command": "/home/cleanup"}, {"occurrence": "monthly", "command": "/home/maverick/bin/tape-backup"}]}

File diff suppressed because one or more lines are too long

1
tests/fixtures/centos-7.7/id.json vendored Normal file
View File

@@ -0,0 +1 @@
{"uid": {"id": 1000, "name": "kbrazil"}, "gid": {"id": 1000, "name": "kbrazil"}, "groups": [{"id": 1000, "name": "kbrazil"}, {"id": 10, "name": "wheel"}], "context": {"user": "unconfined_u", "role": "unconfined_r", "type": "unconfined_t", "level": "s0-s0:c0.c1023"}}

1
tests/fixtures/centos-7.7/id.out vendored Normal file
View File

@@ -0,0 +1 @@
uid=1000(kbrazil) gid=1000(kbrazil) groups=1000(kbrazil),10(wheel) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

View File

@@ -0,0 +1,133 @@
[Settings]
#======================================================================
# Set detailed log for additional debugging info
DetailedLog=1
RunStatus=1
StatusPort=6090
StatusRefresh=10
Archive=1
# Sets the location of the MV_FTP log file
LogFile=/opt/ecs/mvuser/MV_IPTel/log/MV_IPTel.log
#======================================================================
Version=0.9 Build 4 Created July 11 2004 14:00
ServerName=Unknown
[FTP]
#======================================================================
# set the FTP server active
RunFTP=1
# defines the FTP control port
FTPPort=21
# defines the FTP data port
FTPDataPort=20
# Sets the location of the FTP data directory to catch terminal backups
FTPDir=/opt/ecs/mvuser/MV_IPTel/data/FTPdata
# FTP Timeout (secs)
FTP_TimeOut=5
# Enable SuperUser
EnableSU=1
# set the SuperUser Name
SUUserName=mvuser
# set the SuperUser Password
SUPassword=Avaya
#
#======================================================================
[FTPS]
#======================================================================
# set the FTPS server active
RunFTPS=0
# defines the FTP control port
FTPPort=990
# defines the FTP data port
FTPDataPort=889
#======================================================================
[TFTP]
#======================================================================
# set the Trivial FTP server active
RunTrivialFTP=1
# defines the Trivial FTP port
TrivialFTPPort=69
# Sets the location of the TFTP data directory for terminal downloads
TFTPDir=/opt/ecs/mvuser/MV_IPTel/data/TFTPdata
#======================================================================
[HTTP]
#======================================================================
# set the HTTP download server active
RunHTTP=1
# defines the HTTP download port
HTTPPort=81
# Sets the location of the HTTP data directory for downloads
HTTPDir=/opt/ecs/mvuser/MV_IPTel/data/HTTPdata
#======================================================================
[HTTPS]
#======================================================================
# set the HTTPS download server active
RunHTTPS=0
# defines the HTTPS download port
HTTPSPort=411
# Sets the location of the HTTPS data directory for downloads
HTTPSDir=/opt/ecs/mvuser/MV_IPTel/data/HTTPSdata
# Sets the location of the CertFile
CertFile=/opt/ecs/mvuser/MV_IPTel/certs/IPTelcert.pem
# Sets the location of the KeyFile
KeyFile=/opt/ecs/mvuser/MV_IPTel/certs/IPTelkey.pem
# Use Client Authorization
ClientAuth=0
# narrow config for Avaya IPTel (TLSV1 using RSA_NULL_SHA)
IPTel=0
# sets the SSL variants if not Avaya IPtel (IPTel=0)
SSLV2=0
SSLV3=0
TLSV1=1
UseProxy=0
ProxyAddr=simon.avaya.com
ProxyPort=9000
#======================================================================
[BACKUP_SERVERS]
#======================================================================
# Run as FileServer for Backup & Update requests - Note this process uses HTTPS
FileServer=0
# sets whether to download Firmware updates from the primary/secondary file servers
RequestUpdates=0
# sets whether to upload FTP files to the primary/secondary file servers
RequestBackup=0
# Enable use of the Primary file server
UsePrimarySvr=0
# Primary file server IP address ( or resolvable DNS)
PrimaryIP=192.168.0.13
# Enable use of the Secondary file server
UseSecondarySvr=0
# Secondary file server IP address ( or resolvable DNS)
SecondaryIP=192.168.0.10
# Sets the update interval for Backups & updates ; 1 = min; 2 =hour, 3=day, 4 =month
UpdateInterval=2
#Send FTP backup to the customer sever
CustomFTP=1
# FTP backup directory customer sever
CustomFTPDir=home/mvuser/backup
# FTP backup directory user login name
CustomFTPUName=tom
# FTP backup directory user password
CustomFTPPwd=jerry
# Enable CDR Backup - enable=1 on both File Server & Client
CDRBackup=0
# Enable BCMS Backup - enable=1 on both File Server & Client
BCMSBackup=0
# Retain CDR / BCMS copy data for x days ( Receiver always + 1 week)
RetainDays=7.0
#======================================================================
[SNMP]
#================================================================
#
# Validate FTP store with SNMP check
UseSNMP=1
# In case the SNMPGET syntax changes you can redefine the commands
# Uncomment the relevant line to override the internal command
#the syntax is "Command + IPADDR + ExtObj + Awk
# the IPADRR is derived from the connection
# Note there are relavant spaces at the start/end of the component - omit and it will fail
#Command=/usr/bin/snmpget
#Params= -v2c -cpublic
#ExtObject=.1.3.6.1.4.1.6889.2.69.1.4.9.0
#TypeObject=.1.3.6.1.4.1.6889.2.69.1.1.2.0
#Awk=| awk -F \" '' {print $2 } ''
#================================================================

View File

@@ -0,0 +1 @@
{"Settings": {"detailedlog": "1", "runstatus": "1", "statusport": "6090", "statusrefresh": "10", "archive": "1", "logfile": "/opt/ecs/mvuser/MV_IPTel/log/MV_IPTel.log", "version": "0.9 Build 4 Created July 11 2004 14:00", "servername": "Unknown"}, "FTP": {"runftp": "1", "ftpport": "21", "ftpdataport": "20", "ftpdir": "/opt/ecs/mvuser/MV_IPTel/data/FTPdata", "ftp_timeout": "5", "enablesu": "1", "suusername": "mvuser", "supassword": "Avaya"}, "FTPS": {"runftps": "0", "ftpport": "990", "ftpdataport": "889"}, "TFTP": {"runtrivialftp": "1", "trivialftpport": "69", "tftpdir": "/opt/ecs/mvuser/MV_IPTel/data/TFTPdata"}, "HTTP": {"runhttp": "1", "httpport": "81", "httpdir": "/opt/ecs/mvuser/MV_IPTel/data/HTTPdata"}, "HTTPS": {"runhttps": "0", "httpsport": "411", "httpsdir": "/opt/ecs/mvuser/MV_IPTel/data/HTTPSdata", "certfile": "/opt/ecs/mvuser/MV_IPTel/certs/IPTelcert.pem", "keyfile": "/opt/ecs/mvuser/MV_IPTel/certs/IPTelkey.pem", "clientauth": "0", "iptel": "0", "sslv2": "0", "sslv3": "0", "tlsv1": "1", "useproxy": "0", "proxyaddr": "simon.avaya.com", "proxyport": "9000"}, "BACKUP_SERVERS": {"fileserver": "0", "requestupdates": "0", "requestbackup": "0", "useprimarysvr": "0", "primaryip": "192.168.0.13", "usesecondarysvr": "0", "secondaryip": "192.168.0.10", "updateinterval": "2", "customftp": "1", "customftpdir": "home/mvuser/backup", "customftpuname": "tom", "customftppwd": "jerry", "cdrbackup": "0", "bcmsbackup": "0", "retaindays": "7.0"}, "SNMP": {"usesnmp": "1"}}

12
tests/fixtures/generic/ini-test.ini vendored Normal file
View File

@@ -0,0 +1,12 @@
[DEFAULT]
ServerAliveInterval = 45
Compression = yes
CompressionLevel = 9
ForwardX11 = yes
[bitbucket.org]
User = hg
[topsecret.server.com]
Port = 50022
ForwardX11 = no

1
tests/fixtures/generic/ini-test.json vendored Normal file
View File

@@ -0,0 +1 @@
{"bitbucket.org": {"serveraliveinterval": "45", "compression": "yes", "compressionlevel": "9", "forwardx11": "yes", "user": "hg"}, "topsecret.server.com": {"serveraliveinterval": "45", "compression": "yes", "compressionlevel": "9", "forwardx11": "no", "port": "50022"}}

View File

@@ -0,0 +1 @@
{"CATALOG": {"CD": [{"TITLE": "Empire Burlesque", "ARTIST": "Bob Dylan", "COUNTRY": "USA", "COMPANY": "Columbia", "PRICE": "10.90", "YEAR": "1985"}, {"TITLE": "Hide your heart", "ARTIST": "Bonnie Tyler", "COUNTRY": "UK", "COMPANY": "CBS Records", "PRICE": "9.90", "YEAR": "1988"}, {"TITLE": "Greatest Hits", "ARTIST": "Dolly Parton", "COUNTRY": "USA", "COMPANY": "RCA", "PRICE": "9.90", "YEAR": "1982"}, {"TITLE": "Still got the blues", "ARTIST": "Gary Moore", "COUNTRY": "UK", "COMPANY": "Virgin records", "PRICE": "10.20", "YEAR": "1990"}, {"TITLE": "Eros", "ARTIST": "Eros Ramazzotti", "COUNTRY": "EU", "COMPANY": "BMG", "PRICE": "9.90", "YEAR": "1997"}, {"TITLE": "One night only", "ARTIST": "Bee Gees", "COUNTRY": "UK", "COMPANY": "Polydor", "PRICE": "10.90", "YEAR": "1998"}, {"TITLE": "Sylvias Mother", "ARTIST": "Dr.Hook", "COUNTRY": "UK", "COMPANY": "CBS", "PRICE": "8.10", "YEAR": "1973"}, {"TITLE": "Maggie May", "ARTIST": "Rod Stewart", "COUNTRY": "UK", "COMPANY": "Pickwick", "PRICE": "8.50", "YEAR": "1990"}, {"TITLE": "Romanza", "ARTIST": "Andrea Bocelli", "COUNTRY": "EU", "COMPANY": "Polydor", "PRICE": "10.80", "YEAR": "1996"}, {"TITLE": "When a man loves a woman", "ARTIST": "Percy Sledge", "COUNTRY": "USA", "COMPANY": "Atlantic", "PRICE": "8.70", "YEAR": "1987"}, {"TITLE": "Black angel", "ARTIST": "Savage Rose", "COUNTRY": "EU", "COMPANY": "Mega", "PRICE": "10.90", "YEAR": "1995"}, {"TITLE": "1999 Grammy Nominees", "ARTIST": "Many", "COUNTRY": "USA", "COMPANY": "Grammy", "PRICE": "10.20", "YEAR": "1999"}, {"TITLE": "For the good times", "ARTIST": "Kenny Rogers", "COUNTRY": "UK", "COMPANY": "Mucik Master", "PRICE": "8.70", "YEAR": "1995"}, {"TITLE": "Big Willie style", "ARTIST": "Will Smith", "COUNTRY": "USA", "COMPANY": "Columbia", "PRICE": "9.90", "YEAR": "1997"}, {"TITLE": "Tupelo Honey", "ARTIST": "Van Morrison", "COUNTRY": "UK", "COMPANY": "Polydor", "PRICE": "8.20", "YEAR": "1971"}, {"TITLE": "Soulsville", "ARTIST": "Jorn Hoel", "COUNTRY": "Norway", "COMPANY": "WEA", "PRICE": "7.90", "YEAR": "1996"}, {"TITLE": "The very best of", "ARTIST": "Cat Stevens", "COUNTRY": "UK", "COMPANY": "Island", "PRICE": "8.90", "YEAR": "1990"}, {"TITLE": "Stop", "ARTIST": "Sam Brown", "COUNTRY": "UK", "COMPANY": "A and M", "PRICE": "8.90", "YEAR": "1988"}, {"TITLE": "Bridge of Spies", "ARTIST": "T'Pau", "COUNTRY": "UK", "COMPANY": "Siren", "PRICE": "7.90", "YEAR": "1987"}, {"TITLE": "Private Dancer", "ARTIST": "Tina Turner", "COUNTRY": "UK", "COMPANY": "Capitol", "PRICE": "8.90", "YEAR": "1983"}, {"TITLE": "Midt om natten", "ARTIST": "Kim Larsen", "COUNTRY": "EU", "COMPANY": "Medley", "PRICE": "7.80", "YEAR": "1983"}, {"TITLE": "Pavarotti Gala Concert", "ARTIST": "Luciano Pavarotti", "COUNTRY": "UK", "COMPANY": "DECCA", "PRICE": "9.90", "YEAR": "1991"}, {"TITLE": "The dock of the bay", "ARTIST": "Otis Redding", "COUNTRY": "USA", "COMPANY": "Stax Records", "PRICE": "7.90", "YEAR": "1968"}, {"TITLE": "Picture book", "ARTIST": "Simply Red", "COUNTRY": "EU", "COMPANY": "Elektra", "PRICE": "7.20", "YEAR": "1985"}, {"TITLE": "Red", "ARTIST": "The Communards", "COUNTRY": "UK", "COMPANY": "London", "PRICE": "7.80", "YEAR": "1987"}, {"TITLE": "Unchain my heart", "ARTIST": "Joe Cocker", "COUNTRY": "USA", "COMPANY": "EMI", "PRICE": "8.20", "YEAR": "1987"}]}}

View File

@@ -0,0 +1,211 @@
<?xml version="1.0" encoding="UTF-8"?>
<CATALOG>
<CD>
<TITLE>Empire Burlesque</TITLE>
<ARTIST>Bob Dylan</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Hide your heart</TITLE>
<ARTIST>Bonnie Tyler</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS Records</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1988</YEAR>
</CD>
<CD>
<TITLE>Greatest Hits</TITLE>
<ARTIST>Dolly Parton</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>RCA</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1982</YEAR>
</CD>
<CD>
<TITLE>Still got the blues</TITLE>
<ARTIST>Gary Moore</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Virgin records</COMPANY>
<PRICE>10.20</PRICE>
<YEAR>1990</YEAR>
</CD>
<CD>
<TITLE>Eros</TITLE>
<ARTIST>Eros Ramazzotti</ARTIST>
<COUNTRY>EU</COUNTRY>
<COMPANY>BMG</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1997</YEAR>
</CD>
<CD>
<TITLE>One night only</TITLE>
<ARTIST>Bee Gees</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Polydor</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1998</YEAR>
</CD>
<CD>
<TITLE>Sylvias Mother</TITLE>
<ARTIST>Dr.Hook</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>CBS</COMPANY>
<PRICE>8.10</PRICE>
<YEAR>1973</YEAR>
</CD>
<CD>
<TITLE>Maggie May</TITLE>
<ARTIST>Rod Stewart</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Pickwick</COMPANY>
<PRICE>8.50</PRICE>
<YEAR>1990</YEAR>
</CD>
<CD>
<TITLE>Romanza</TITLE>
<ARTIST>Andrea Bocelli</ARTIST>
<COUNTRY>EU</COUNTRY>
<COMPANY>Polydor</COMPANY>
<PRICE>10.80</PRICE>
<YEAR>1996</YEAR>
</CD>
<CD>
<TITLE>When a man loves a woman</TITLE>
<ARTIST>Percy Sledge</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Atlantic</COMPANY>
<PRICE>8.70</PRICE>
<YEAR>1987</YEAR>
</CD>
<CD>
<TITLE>Black angel</TITLE>
<ARTIST>Savage Rose</ARTIST>
<COUNTRY>EU</COUNTRY>
<COMPANY>Mega</COMPANY>
<PRICE>10.90</PRICE>
<YEAR>1995</YEAR>
</CD>
<CD>
<TITLE>1999 Grammy Nominees</TITLE>
<ARTIST>Many</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Grammy</COMPANY>
<PRICE>10.20</PRICE>
<YEAR>1999</YEAR>
</CD>
<CD>
<TITLE>For the good times</TITLE>
<ARTIST>Kenny Rogers</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Mucik Master</COMPANY>
<PRICE>8.70</PRICE>
<YEAR>1995</YEAR>
</CD>
<CD>
<TITLE>Big Willie style</TITLE>
<ARTIST>Will Smith</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Columbia</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1997</YEAR>
</CD>
<CD>
<TITLE>Tupelo Honey</TITLE>
<ARTIST>Van Morrison</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Polydor</COMPANY>
<PRICE>8.20</PRICE>
<YEAR>1971</YEAR>
</CD>
<CD>
<TITLE>Soulsville</TITLE>
<ARTIST>Jorn Hoel</ARTIST>
<COUNTRY>Norway</COUNTRY>
<COMPANY>WEA</COMPANY>
<PRICE>7.90</PRICE>
<YEAR>1996</YEAR>
</CD>
<CD>
<TITLE>The very best of</TITLE>
<ARTIST>Cat Stevens</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Island</COMPANY>
<PRICE>8.90</PRICE>
<YEAR>1990</YEAR>
</CD>
<CD>
<TITLE>Stop</TITLE>
<ARTIST>Sam Brown</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>A and M</COMPANY>
<PRICE>8.90</PRICE>
<YEAR>1988</YEAR>
</CD>
<CD>
<TITLE>Bridge of Spies</TITLE>
<ARTIST>T'Pau</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Siren</COMPANY>
<PRICE>7.90</PRICE>
<YEAR>1987</YEAR>
</CD>
<CD>
<TITLE>Private Dancer</TITLE>
<ARTIST>Tina Turner</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>Capitol</COMPANY>
<PRICE>8.90</PRICE>
<YEAR>1983</YEAR>
</CD>
<CD>
<TITLE>Midt om natten</TITLE>
<ARTIST>Kim Larsen</ARTIST>
<COUNTRY>EU</COUNTRY>
<COMPANY>Medley</COMPANY>
<PRICE>7.80</PRICE>
<YEAR>1983</YEAR>
</CD>
<CD>
<TITLE>Pavarotti Gala Concert</TITLE>
<ARTIST>Luciano Pavarotti</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>DECCA</COMPANY>
<PRICE>9.90</PRICE>
<YEAR>1991</YEAR>
</CD>
<CD>
<TITLE>The dock of the bay</TITLE>
<ARTIST>Otis Redding</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>Stax Records</COMPANY>
<PRICE>7.90</PRICE>
<YEAR>1968</YEAR>
</CD>
<CD>
<TITLE>Picture book</TITLE>
<ARTIST>Simply Red</ARTIST>
<COUNTRY>EU</COUNTRY>
<COMPANY>Elektra</COMPANY>
<PRICE>7.20</PRICE>
<YEAR>1985</YEAR>
</CD>
<CD>
<TITLE>Red</TITLE>
<ARTIST>The Communards</ARTIST>
<COUNTRY>UK</COUNTRY>
<COMPANY>London</COMPANY>
<PRICE>7.80</PRICE>
<YEAR>1987</YEAR>
</CD>
<CD>
<TITLE>Unchain my heart</TITLE>
<ARTIST>Joe Cocker</ARTIST>
<COUNTRY>USA</COUNTRY>
<COMPANY>EMI</COMPANY>
<PRICE>8.20</PRICE>
<YEAR>1987</YEAR>
</CD>
</CATALOG>

View File

@@ -0,0 +1 @@
{"breakfast_menu": {"food": [{"name": "Belgian Waffles", "price": "$5.95", "description": "Two of our famous Belgian Waffles with plenty of real maple syrup", "calories": "650"}, {"name": "Strawberry Belgian Waffles", "price": "$7.95", "description": "Light Belgian waffles covered with strawberries and whipped cream", "calories": "900"}, {"name": "Berry-Berry Belgian Waffles", "price": "$8.95", "description": "Light Belgian waffles covered with an assortment of fresh berries and whipped cream", "calories": "900"}, {"name": "French Toast", "price": "$4.50", "description": "Thick slices made from our homemade sourdough bread", "calories": "600"}, {"name": "Homestyle Breakfast", "price": "$6.95", "description": "Two eggs, bacon or sausage, toast, and our ever-popular hash browns", "calories": "950"}]}}

33
tests/fixtures/generic/xml-foodmenu.xml vendored Normal file
View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<breakfast_menu>
<food>
<name>Belgian Waffles</name>
<price>$5.95</price>
<description>Two of our famous Belgian Waffles with plenty of real maple syrup</description>
<calories>650</calories>
</food>
<food>
<name>Strawberry Belgian Waffles</name>
<price>$7.95</price>
<description>Light Belgian waffles covered with strawberries and whipped cream</description>
<calories>900</calories>
</food>
<food>
<name>Berry-Berry Belgian Waffles</name>
<price>$8.95</price>
<description>Light Belgian waffles covered with an assortment of fresh berries and whipped cream</description>
<calories>900</calories>
</food>
<food>
<name>French Toast</name>
<price>$4.50</price>
<description>Thick slices made from our homemade sourdough bread</description>
<calories>600</calories>
</food>
<food>
<name>Homestyle Breakfast</name>
<price>$6.95</price>
<description>Two eggs, bacon or sausage, toast, and our ever-popular hash browns</description>
<calories>950</calories>
</food>
</breakfast_menu>

View File

@@ -0,0 +1 @@
[{"apiVersion": "networking.istio.io/v1alpha3", "kind": "Sidecar", "metadata": {"name": "sidecar-config", "namespace": "default", "testboolean": true}, "spec": {"ingress": [{"port": {"number": 8080, "protocol": "HTTP", "name": "waf"}, "defaultEndpoint": "127.0.0.1:80", "captureMode": "IPTABLES"}], "egress": [{"hosts": ["*/*"]}]}}]

View File

@@ -0,0 +1,17 @@
apiVersion: networking.istio.io/v1alpha3
kind: Sidecar
metadata:
name: sidecar-config
namespace: default
testboolean: true
spec:
ingress:
- port:
number: 8080
protocol: HTTP
name: waf
defaultEndpoint: 127.0.0.1:80
captureMode: IPTABLES
egress:
- hosts:
- "*/*"

View File

@@ -0,0 +1 @@
[{"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "www", "testboolean": true}}, {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "db", "testboolean": false}}, {"apiVersion": "v1", "kind": "ServiceAccount", "metadata": {"name": "auth"}}, {"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "www"}, "spec": {"replicas": 3, "selector": {"matchLabels": {"app": "www"}}, "template": {"metadata": {"labels": {"app": "www", "version": "v1.0"}}, "spec": {"serviceAccountName": "www", "containers": [{"name": "modsecurity", "image": "owasp/modsecurity-crs:v3.2-modsec2-apache", "ports": [{"containerPort": 80}], "env": [{"name": "SETPROXY", "value": "True"}, {"name": "PROXYLOCATION", "value": "http://127.0.0.1:8080/"}]}, {"name": "microsimserver", "image": "kellybrazil/microsimserver", "ports": [{"containerPort": 8080}], "env": [{"name": "STATS_PORT", "value": "5000"}]}, {"name": "microsimclient", "image": "kellybrazil/microsimclient", "env": [{"name": "STATS_PORT", "value": "5001"}, {"name": "REQUEST_URLS", "value": "http://auth.default.svc.cluster.local:8080/,http://db.default.svc.cluster.local:8080/"}, {"name": "SEND_SQLI", "value": "True"}]}]}}}}, {"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "auth"}, "spec": {"replicas": 3, "selector": {"matchLabels": {"app": "auth"}}, "template": {"metadata": {"labels": {"app": "auth", "version": "v1.0"}}, "spec": {"serviceAccountName": "auth", "containers": [{"name": "modsecurity", "image": "owasp/modsecurity-crs:v3.2-modsec2-apache", "ports": [{"containerPort": 80}], "env": [{"name": "SETPROXY", "value": "True"}, {"name": "PROXYLOCATION", "value": "http://127.0.0.1:8080/"}]}, {"name": "microsimserver", "image": "kellybrazil/microsimserver", "ports": [{"containerPort": 8080}], "env": [{"name": "STATS_PORT", "value": "5000"}]}]}}}}, {"apiVersion": "apps/v1", "kind": "Deployment", "metadata": {"name": "db"}, "spec": {"replicas": 3, "selector": {"matchLabels": {"app": "db"}}, "template": {"metadata": {"labels": {"app": "db", "version": "v1.0"}}, "spec": {"serviceAccountName": "db", "containers": [{"name": "modsecurity", "image": "owasp/modsecurity-crs:v3.2-modsec2-apache", "ports": [{"containerPort": 80}], "env": [{"name": "SETPROXY", "value": "True"}, {"name": "PROXYLOCATION", "value": "http://127.0.0.1:8080/"}]}, {"name": "microsimserver", "image": "kellybrazil/microsimserver", "ports": [{"containerPort": 8080}], "env": [{"name": "STATS_PORT", "value": "5000"}]}]}}}}, {"apiVersion": "v1", "kind": "Service", "metadata": {"labels": {"app": "www"}, "name": "www"}, "spec": {"ports": [{"port": 8080, "targetPort": 80, "name": "http"}], "selector": {"app": "www"}, "sessionAffinity": "None"}}, {"apiVersion": "v1", "kind": "Service", "metadata": {"labels": {"app": "auth"}, "name": "auth"}, "spec": {"ports": [{"port": 8080, "targetPort": 80, "name": "http"}], "selector": {"app": "auth"}, "sessionAffinity": "None"}}, {"apiVersion": "v1", "kind": "Service", "metadata": {"labels": {"app": "db"}, "name": "db"}, "spec": {"ports": [{"port": 8080, "targetPort": 80, "name": "http"}], "selector": {"app": "db"}, "sessionAffinity": "None"}}]

View File

@@ -0,0 +1,174 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: www
testboolean: true
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: db
testboolean: false
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: auth
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: www
spec:
replicas: 3
selector:
matchLabels:
app: www
template:
metadata:
labels:
app: www
version: v1.0 # add version
spec:
serviceAccountName: www # add serviceAccountName
containers:
- name: modsecurity
image: owasp/modsecurity-crs:v3.2-modsec2-apache
ports:
- containerPort: 80
env:
- name: SETPROXY
value: "True"
- name: PROXYLOCATION
value: "http://127.0.0.1:8080/"
- name: microsimserver
image: kellybrazil/microsimserver
ports:
- containerPort: 8080 # add microsimserver port
env:
- name: STATS_PORT
value: "5000"
- name: microsimclient
image: kellybrazil/microsimclient
env:
- name: STATS_PORT
value: "5001"
- name: REQUEST_URLS
value: "http://auth.default.svc.cluster.local:8080/,http://db.default.svc.cluster.local:8080/"
- name: SEND_SQLI
value: "True"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: auth
spec:
replicas: 3
selector:
matchLabels:
app: auth
template:
metadata:
labels:
app: auth
version: v1.0 # add version
spec:
serviceAccountName: auth # add serviceAccountName
containers:
- name: modsecurity
image: owasp/modsecurity-crs:v3.2-modsec2-apache
ports:
- containerPort: 80
env:
- name: SETPROXY
value: "True"
- name: PROXYLOCATION
value: "http://127.0.0.1:8080/"
- name: microsimserver
image: kellybrazil/microsimserver
ports:
- containerPort: 8080 # add microsimserver port
env:
- name: STATS_PORT
value: "5000"
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: db
spec:
replicas: 3
selector:
matchLabels:
app: db
template:
metadata:
labels:
app: db
version: v1.0 # add version
spec:
serviceAccountName: db # add serviceAccountName
containers:
- name: modsecurity
image: owasp/modsecurity-crs:v3.2-modsec2-apache
ports:
- containerPort: 80
env:
- name: SETPROXY
value: "True"
- name: PROXYLOCATION
value: "http://127.0.0.1:8080/"
- name: microsimserver
image: kellybrazil/microsimserver
ports:
- containerPort: 8080 # add microsimserver port
env:
- name: STATS_PORT
value: "5000"
---
apiVersion: v1
kind: Service
metadata:
labels:
app: www
name: www
spec:
# externalTrafficPolicy: Local # remove externalTrafficPolicy
ports:
- port: 8080
targetPort: 80
name: http # add port name
selector:
app: www
sessionAffinity: None
# type: LoadBalancer # remove LoadBalancer type
---
apiVersion: v1
kind: Service
metadata:
labels:
app: auth
name: auth
spec:
ports:
- port: 8080
targetPort: 80
name: http # add port name
selector:
app: auth
sessionAffinity: None
---
apiVersion: v1
kind: Service
metadata:
labels:
app: db
name: db
spec:
ports:
- port: 8080
targetPort: 80
name: http # add port name
selector:
app: db
sessionAffinity: None

1
tests/fixtures/osx-10.14.6/id.json vendored Normal file
View File

@@ -0,0 +1 @@
{"uid": {"id": 501, "name": "kbrazil"}, "gid": {"id": 20, "name": "staff"}, "groups": [{"id": 20, "name": "staff"}, {"id": 501, "name": "access_bpf"}, {"id": 12, "name": "everyone"}, {"id": 61, "name": "localaccounts"}, {"id": 79, "name": "_appserverusr"}, {"id": 80, "name": "admin"}, {"id": 81, "name": "_appserveradm"}, {"id": 98, "name": "_lpadmin"}, {"id": 703, "name": "com.apple.sharepoint.group.3"}, {"id": 701, "name": "com.apple.sharepoint.group.1"}, {"id": 702, "name": "com.apple.sharepoint.group.2"}, {"id": 33, "name": "_appstore"}, {"id": 100, "name": "_lpoperator"}, {"id": 204, "name": "_developer"}, {"id": 250, "name": "_analyticsusers"}, {"id": 395, "name": "com.apple.access_ftp"}, {"id": 398, "name": "com.apple.access_screensharing"}]}

1
tests/fixtures/osx-10.14.6/id.out vendored Normal file
View File

@@ -0,0 +1 @@
uid=501(kbrazil) gid=20(staff) groups=20(staff),501(access_bpf),12(everyone),61(localaccounts),79(_appserverusr),80(admin),81(_appserveradm),98(_lpadmin),703(com.apple.sharepoint.group.3),701(com.apple.sharepoint.group.1),702(com.apple.sharepoint.group.2),33(_appstore),100(_lpoperator),204(_developer),250(_analyticsusers),395(com.apple.access_ftp),398(com.apple.access_screensharing)

View File

@@ -0,0 +1 @@
{"variables": [{"name": "PATH", "value": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"}, {"name": "SHELL", "value": "/bin/sh"}], "schedule": [{"minute": ["17"], "hour": ["*"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "user": "root", "command": "cd / && run-parts --report /etc/cron.hourly"}, {"minute": ["25"], "hour": ["6"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "user": "root", "command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )"}, {"minute": ["47"], "hour": ["6"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["7"], "user": "root", "command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )"}, {"minute": ["52"], "hour": ["6"], "day_of_month": ["1"], "month": ["*"], "day_of_week": ["*"], "user": "root", "command": "test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )"}]}

View File

@@ -1 +0,0 @@
{"variables": [{"name": "PATH", "value": "/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin"}, {"name": "SHELL", "value": "/bin/sh"}], "schedule": [{"minute": ["25"], "hour": ["6"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["*"], "command": "root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )"}, {"minute": ["47"], "hour": ["6"], "day_of_month": ["*"], "month": ["*"], "day_of_week": ["7"], "command": "root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )"}, {"minute": ["52"], "hour": ["6"], "day_of_month": ["1"], "month": ["*"], "day_of_week": ["*"], "command": "root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )"}]}

File diff suppressed because one or more lines are too long

View File

@@ -13,28 +13,16 @@ class MyTests(unittest.TestCase):
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/crontab.out'), 'r') as f:
self.centos_7_7_crontab = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/crontab.out'), 'r') as f:
self.ubuntu_18_4_crontab = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/crontab.json'), 'r') as f:
self.centos_7_7_crontab_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/crontab.json'), 'r') as f:
self.ubuntu_18_4_crontab_json = json.loads(f.read())
def test_crontab_centos_7_7(self):
"""
Test 'crontab' on Centos 7.7
"""
self.assertEqual(jc.parsers.crontab.parse(self.centos_7_7_crontab, quiet=True), self.centos_7_7_crontab_json)
def test_crontab_ubuntu_18_4(self):
"""
Test 'crontab' on Ubuntu 18.4
"""
self.assertEqual(jc.parsers.crontab.parse(self.ubuntu_18_4_crontab, quiet=True), self.ubuntu_18_4_crontab_json)
if __name__ == '__main__':
unittest.main()

40
tests/test_crontab_u.py Normal file
View File

@@ -0,0 +1,40 @@
import os
import json
import unittest
import jc.parsers.crontab_u
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
class MyTests(unittest.TestCase):
def setUp(self):
# input
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/crontab-u.out'), 'r') as f:
self.ubuntu_18_4_crontab_u = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/crontab-u.out'), 'r') as f:
self.centos_7_7_crontab_u = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/crontab-u.json'), 'r') as f:
self.ubuntu_18_4_crontab_u_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/crontab-u.json'), 'r') as f:
self.centos_7_7_crontab_u_json = json.loads(f.read())
def test_crontab_u_ubuntu_18_4(self):
"""
Test 'crontab' on Ubuntu 18.4 (has a user field)
"""
self.assertEqual(jc.parsers.crontab_u.parse(self.ubuntu_18_4_crontab_u, quiet=True), self.ubuntu_18_4_crontab_u_json)
def test_crontab_u_centos_7_7(self):
"""
Test 'crontab' on Centos 7.7 (has a user field)
"""
self.assertEqual(jc.parsers.crontab_u.parse(self.centos_7_7_crontab_u, quiet=True), self.centos_7_7_crontab_u_json)
if __name__ == '__main__':
unittest.main()

40
tests/test_id.py Normal file
View File

@@ -0,0 +1,40 @@
import os
import json
import unittest
import jc.parsers.id
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
class MyTests(unittest.TestCase):
def setUp(self):
# input
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/id.out'), 'r') as f:
self.centos_7_7_id = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/id.out'), 'r') as f:
self.osx_10_14_6_id = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/id.json'), 'r') as f:
self.centos_7_7_id_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/osx-10.14.6/id.json'), 'r') as f:
self.osx_10_14_6_id_json = json.loads(f.read())
def test_id_centos_7_7(self):
"""
Test 'id' on Centos 7.7
"""
self.assertEqual(jc.parsers.id.parse(self.centos_7_7_id, quiet=True), self.centos_7_7_id_json)
def test_id_osx_10_14_6(self):
"""
Test 'id' on OSX 10.14.6
"""
self.assertEqual(jc.parsers.id.parse(self.osx_10_14_6_id, quiet=True), self.osx_10_14_6_id_json)
if __name__ == '__main__':
unittest.main()

40
tests/test_ini.py Normal file
View File

@@ -0,0 +1,40 @@
import os
import unittest
import json
import jc.parsers.ini
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
class MyTests(unittest.TestCase):
def setUp(self):
# input
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/ini-test.ini'), 'r') as f:
self.generic_ini_test = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/ini-iptelserver.ini'), 'r') as f:
self.generic_ini_iptelserver = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/ini-test.json'), 'r') as f:
self.generic_ini_test_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/ini-iptelserver.json'), 'r') as f:
self.generic_ini_iptelserver_json = json.loads(f.read())
def test_ini_test(self):
"""
Test the test ini file
"""
self.assertEqual(jc.parsers.ini.parse(self.generic_ini_test, quiet=True), self.generic_ini_test_json)
def test_ini_iptelserver(self):
"""
Test the iptelserver ini file
"""
self.assertEqual(jc.parsers.ini.parse(self.generic_ini_iptelserver, quiet=True), self.generic_ini_iptelserver_json)
if __name__ == '__main__':
unittest.main()

40
tests/test_xml.py Normal file
View File

@@ -0,0 +1,40 @@
import os
import unittest
import json
import jc.parsers.xml
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
class MyTests(unittest.TestCase):
def setUp(self):
# input
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-cd_catalog.xml'), 'r') as f:
self.generic_xml_cd_catalog = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-foodmenu.xml'), 'r') as f:
self.generic_xml_foodmenu = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-cd_catalog.json'), 'r') as f:
self.generic_xml_cd_catalog_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/xml-foodmenu.json'), 'r') as f:
self.generic_xml_foodmenu_json = json.loads(f.read())
def test_xml_cd_catalog(self):
"""
Test the cd catalog xml file
"""
self.assertEqual(jc.parsers.xml.parse(self.generic_xml_cd_catalog, quiet=True), self.generic_xml_cd_catalog_json)
def test_xml_foodmenu(self):
"""
Test the food menu xml file
"""
self.assertEqual(jc.parsers.xml.parse(self.generic_xml_foodmenu, quiet=True), self.generic_xml_foodmenu_json)
if __name__ == '__main__':
unittest.main()

40
tests/test_yaml.py Normal file
View File

@@ -0,0 +1,40 @@
import os
import unittest
import json
import jc.parsers.yaml
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
class MyTests(unittest.TestCase):
def setUp(self):
# input
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/yaml-istio-sc.yaml'), 'r') as f:
self.generic_yaml_istio_sc = f.read()
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/yaml-istio-sidecar.yaml'), 'r') as f:
self.generic_yaml_istio_sidecar = f.read()
# output
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/yaml-istio-sc.json'), 'r') as f:
self.generic_yaml_istio_sc_json = json.loads(f.read())
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/yaml-istio-sidecar.json'), 'r') as f:
self.generic_yaml_istio_sidecar_json = json.loads(f.read())
def test_yaml_istio_sc(self):
"""
Test the Istio SC yaml file
"""
self.assertEqual(jc.parsers.yaml.parse(self.generic_yaml_istio_sc, quiet=True), self.generic_yaml_istio_sc_json)
def test_yaml_istio_sidecar(self):
"""
Test the Istio Sidecar yaml file
"""
self.assertEqual(jc.parsers.yaml.parse(self.generic_yaml_istio_sidecar, quiet=True), self.generic_yaml_istio_sidecar_json)
if __name__ == '__main__':
unittest.main()