diff --git a/CHANGELOG b/CHANGELOG index 766c9008..fdc1dc31 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ jc changelog +20210630 v1.15.7 +- Add sfdisk command parser tested on linux + 20210628 v1.15.6 - Fix issue to only load local plugin parsers that have filenames that end in .py diff --git a/README.md b/README.md index 31bb0c95..f1faa28a 100644 --- a/README.md +++ b/README.md @@ -169,6 +169,7 @@ The JSON output can be compact (default) or pretty formatted with the `-p` optio - `--ps` enables the `ps` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/ps)) - `--route` enables the `route` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/route)) - `--rpm-qi` enables the `rpm -qi` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/rpm_qi)) +- `--sfdisk` enables the `sfdisk` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/sfdisk)) - `--shadow` enables the `/etc/shadow` file parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/shadow)) - `--ss` enables the `ss` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/ss)) - `--stat` enables the `stat` command parser ([documentation](https://kellyjonbrazil.github.io/jc/docs/parsers/stat)) diff --git a/docs/parsers/sfdisk.md b/docs/parsers/sfdisk.md new file mode 100644 index 00000000..6eb8bb6f --- /dev/null +++ b/docs/parsers/sfdisk.md @@ -0,0 +1,213 @@ +[Home](https://kellyjonbrazil.github.io/jc/) + +# jc.parsers.sfdisk +jc - JSON CLI output utility `sfdisk` command output parser + +Supports the following `sfdisk` options: +- `-l` +- `-d` +- `-uM` +- `-uC` +- `-uS` +- `-uB` + +Usage (cli): + + # sfdisk -l | jc --sfdisk + + or + + # jc sfdisk -l + +Usage (module): + + import jc.parsers.sfdisk + result = jc.parsers.sfdisk.parse(sfdisk_command_output) + +Schema: + + [ + { + "disk": string, + "cylinders": integer, + "heads": integer, + "sectors_per_track": integer, + "units": string, + "partitions": [ + { + "device": string, + "boot": boolean, + "start": integer, + "end": integer, + "size": integer, + "cyls": integer, + "mib": integer, + "blocks": integer, + "sectors": integer, + "id": string, + "system": string + } + ] + } + ] + +Examples: + + $ sfdisk -l | jc --sfdisk -p + [ + { + "disk": "/dev/sda", + "cylinders": 2610, + "heads": 255, + "sectors_per_track": 63, + "units": "cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0", + "partitions": [ + { + "device": "/dev/sda1", + "boot": true, + "start": 0, + "end": null, + "cyls": null, + "blocks": 1048576, + "id": "83", + "system": "Linux" + }, + { + "device": "/dev/sda2", + "boot": false, + "start": 130, + "end": null, + "cyls": null, + "blocks": 19921920, + "id": "8e", + "system": "Linux LVM" + }, + { + "device": "/dev/sda3", + "boot": false, + "start": 0, + "end": null, + "cyls": 0, + "blocks": 0, + "id": "0", + "system": "Empty" + }, + { + "device": "/dev/sda4", + "boot": false, + "start": 0, + "end": null, + "cyls": 0, + "blocks": 0, + "id": "0", + "system": "Empty" + } + ] + }, + { + "disk": "/dev/mapper/centos-root", + "cylinders": 2218, + "heads": 255, + "sectors_per_track": 63 + }, + { + "disk": "/dev/mapper/centos-swap", + "cylinders": 261, + "heads": 255, + "sectors_per_track": 63 + } + ] + + $ sfdisk | jc --sfdisk -p -r + [ + { + "disk": "/dev/sda", + "cylinders": "2610", + "heads": "255", + "sectors_per_track": "63", + "units": "cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0", + "partitions": [ + { + "device": "/dev/sda1", + "boot": "*", + "start": "0+", + "end": "130-", + "cyls": "131-", + "blocks": "1048576", + "id": "83", + "system": "Linux" + }, + { + "device": "/dev/sda2", + "boot": null, + "start": "130+", + "end": "2610-", + "cyls": "2481-", + "blocks": "19921920", + "id": "8e", + "system": "Linux LVM" + }, + { + "device": "/dev/sda3", + "boot": null, + "start": "0", + "end": "-", + "cyls": "0", + "blocks": "0", + "id": "0", + "system": "Empty" + }, + { + "device": "/dev/sda4", + "boot": null, + "start": "0", + "end": "-", + "cyls": "0", + "blocks": "0", + "id": "0", + "system": "Empty" + } + ] + }, + { + "disk": "/dev/mapper/centos-root", + "cylinders": "2218", + "heads": "255", + "sectors_per_track": "63" + }, + { + "disk": "/dev/mapper/centos-swap", + "cylinders": "261", + "heads": "255", + "sectors_per_track": "63" + } + ] + + +## info +```python +info() +``` +Provides parser metadata (version, author, etc.) + +## 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. + +## Parser Information +Compatibility: linux + +Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com) diff --git a/jc/__init__.py b/jc/__init__.py index c1c3d337..a79d1d71 100644 --- a/jc/__init__.py +++ b/jc/__init__.py @@ -86,4 +86,4 @@ Module Example: """ name = 'jc' -__version__ = '1.15.6' +__version__ = '1.15.7' diff --git a/jc/cli.py b/jc/cli.py index e0a3db8c..4dafde69 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -94,6 +94,7 @@ parsers = [ 'ps', 'route', 'rpm-qi', + 'sfdisk', 'shadow', 'ss', 'stat', diff --git a/jc/man/jc.1.gz b/jc/man/jc.1.gz index 4e75e0f4..b160d5bb 100644 Binary files a/jc/man/jc.1.gz and b/jc/man/jc.1.gz differ diff --git a/jc/parsers/sfdisk.py b/jc/parsers/sfdisk.py new file mode 100644 index 00000000..1debe6dd --- /dev/null +++ b/jc/parsers/sfdisk.py @@ -0,0 +1,325 @@ +"""jc - JSON CLI output utility `sfdisk` command output parser + +Supports the following `sfdisk` options: +- `-l` +- `-d` +- `-uM` +- `-uC` +- `-uS` +- `-uB` + +Usage (cli): + + # sfdisk -l | jc --sfdisk + + or + + # jc sfdisk -l + +Usage (module): + + import jc.parsers.sfdisk + result = jc.parsers.sfdisk.parse(sfdisk_command_output) + +Schema: + + [ + { + "disk": string, + "cylinders": integer, + "heads": integer, + "sectors_per_track": integer, + "units": string, + "partitions": [ + { + "device": string, + "boot": boolean, + "start": integer, + "end": integer, + "size": integer, + "cyls": integer, + "mib": integer, + "blocks": integer, + "sectors": integer, + "id": string, + "system": string + } + ] + } + ] + +Examples: + + $ sfdisk -l | jc --sfdisk -p + [ + { + "disk": "/dev/sda", + "cylinders": 2610, + "heads": 255, + "sectors_per_track": 63, + "units": "cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0", + "partitions": [ + { + "device": "/dev/sda1", + "boot": true, + "start": 0, + "end": null, + "cyls": null, + "blocks": 1048576, + "id": "83", + "system": "Linux" + }, + { + "device": "/dev/sda2", + "boot": false, + "start": 130, + "end": null, + "cyls": null, + "blocks": 19921920, + "id": "8e", + "system": "Linux LVM" + }, + { + "device": "/dev/sda3", + "boot": false, + "start": 0, + "end": null, + "cyls": 0, + "blocks": 0, + "id": "0", + "system": "Empty" + }, + { + "device": "/dev/sda4", + "boot": false, + "start": 0, + "end": null, + "cyls": 0, + "blocks": 0, + "id": "0", + "system": "Empty" + } + ] + }, + { + "disk": "/dev/mapper/centos-root", + "cylinders": 2218, + "heads": 255, + "sectors_per_track": 63 + }, + { + "disk": "/dev/mapper/centos-swap", + "cylinders": 261, + "heads": 255, + "sectors_per_track": 63 + } + ] + + $ sfdisk | jc --sfdisk -p -r + [ + { + "disk": "/dev/sda", + "cylinders": "2610", + "heads": "255", + "sectors_per_track": "63", + "units": "cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0", + "partitions": [ + { + "device": "/dev/sda1", + "boot": "*", + "start": "0+", + "end": "130-", + "cyls": "131-", + "blocks": "1048576", + "id": "83", + "system": "Linux" + }, + { + "device": "/dev/sda2", + "boot": null, + "start": "130+", + "end": "2610-", + "cyls": "2481-", + "blocks": "19921920", + "id": "8e", + "system": "Linux LVM" + }, + { + "device": "/dev/sda3", + "boot": null, + "start": "0", + "end": "-", + "cyls": "0", + "blocks": "0", + "id": "0", + "system": "Empty" + }, + { + "device": "/dev/sda4", + "boot": null, + "start": "0", + "end": "-", + "cyls": "0", + "blocks": "0", + "id": "0", + "system": "Empty" + } + ] + }, + { + "disk": "/dev/mapper/centos-root", + "cylinders": "2218", + "heads": "255", + "sectors_per_track": "63" + }, + { + "disk": "/dev/mapper/centos-swap", + "cylinders": "261", + "heads": "255", + "sectors_per_track": "63" + } + ] +""" +import jc.utils +import jc.parsers.universal + + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1.0' + description = '`sfdisk` command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + compatible = ['linux'] + magic_commands = ['sfdisk'] + + +__version__ = info.version + + +def _process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + + Returns: + + List of Dictionaries. Structured to conform to the schema. + """ + int_list = ['cylinders', 'heads', 'sectors_per_track', 'start', 'end', 'size', 'cyls', 'mib', + 'blocks', 'sectors'] + bool_list = ['boot'] + + for entry in proc_data: + for key in entry: + if key in int_list: + entry[key] = jc.utils.convert_to_int(entry[key]) + + if 'partitions' in entry: + for tp in entry['partitions']: + for key in tp: + if key in int_list: + tp[key] = jc.utils.convert_to_int(tp[key]) + if key in bool_list: + tp[key] = jc.utils.convert_to_bool(tp[key]) + + 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 = [] + item = {} + partitions = [] + option = '' + section = '' + + if jc.utils.has_data(data): + + for line in data.splitlines(): + if line.startswith('# partition table of'): + if item: + raw_output.append(item) + + item = {} + partitions = [] + option = 'd' + item['disk'] = line.split()[4] + continue + + if option == 'd': + if line.startswith('unit: '): + item['units'] = line.split()[1] + section = 'partitions' + continue + + if section == 'partitions' and line: + part = {} + part['device'] = line.split()[0] + line = line.replace(',', ' ').replace('=', ' ') + part['start'] = line.split()[3] + part['size'] = line.split()[5] + part['id'] = line.split()[7] + part['boot'] = '*' if 'bootable' in line else None + partitions.append(part) + item['partitions'] = partitions + continue + + else: + if line.startswith('Disk '): + if item: + raw_output.append(item) + + item = {} + partitions = [] + line = line.replace(':', '').replace(',', '') + item['disk'] = line.split()[1] + item['cylinders'] = line.split()[2] + item['heads'] = line.split()[4] + item['sectors_per_track'] = line.split()[6] + continue + + if line.startswith('Units: '): + item['units'] = line.split(':')[1].strip() + continue + + if 'Device' in line and 'Boot' in line and 'Start' in line and 'End' in line: + section = 'partitions' + partitions.append(line.lower().replace('#', ' ')) + continue + + if section == 'partitions' and line: + partitions.append(line) + continue + + if section == 'partitions' and line == '': + item['partitions'] = jc.parsers.universal.sparse_table_parse(partitions) + section = '' + partitions = [] + continue + + if item: + raw_output.append(item) + + if raw: + return raw_output + else: + return _process(raw_output) diff --git a/jc/utils.py b/jc/utils.py index 7974ef28..3adfd69f 100644 --- a/jc/utils.py +++ b/jc/utils.py @@ -154,7 +154,7 @@ def convert_to_bool(value): # if float converts, then bool the result # if float does not convert then look for truthy string and bool True # else False - truthy = ['y', 'yes', 'true'] + truthy = ['y', 'yes', 'true', '*'] if isinstance(value, (int, float)): return bool(value) diff --git a/man/jc.1.gz b/man/jc.1.gz index 4e75e0f4..b160d5bb 100644 Binary files a/man/jc.1.gz and b/man/jc.1.gz differ diff --git a/setup.py b/setup.py index 16a536e3..e95facef 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open('README.md', 'r') as f: setuptools.setup( name='jc', - version='1.15.6', + version='1.15.7', author='Kelly Brazil', author_email='kellyjonbrazil@gmail.com', description='Converts the output of popular command-line tools and file-types to JSON.', diff --git a/tests/fixtures/centos-7.7/sfdisk-d.out b/tests/fixtures/centos-7.7/sfdisk-d.out new file mode 100644 index 00000000..c07c0a04 --- /dev/null +++ b/tests/fixtures/centos-7.7/sfdisk-d.out @@ -0,0 +1,8 @@ +# partition table of /dev/sda +unit: sectors + +/dev/sda1 : start= 2048, size= 2097152, Id=83, bootable +/dev/sda2 : start= 2099200, size= 39843840, Id=8e +/dev/sda3 : start= 0, size= 0, Id= 0 +/dev/sda4 : start= 0, size= 0, Id= 0 + diff --git a/tests/fixtures/centos-7.7/sfdisk-l.out b/tests/fixtures/centos-7.7/sfdisk-l.out new file mode 100644 index 00000000..b067b063 --- /dev/null +++ b/tests/fixtures/centos-7.7/sfdisk-l.out @@ -0,0 +1,14 @@ + +Disk /dev/sda: 2610 cylinders, 255 heads, 63 sectors/track +Units: cylinders of 8225280 bytes, blocks of 1024 bytes, counting from 0 + + Device Boot Start End #cyls #blocks Id System +/dev/sda1 * 0+ 130- 131- 1048576 83 Linux +/dev/sda2 130+ 2610- 2481- 19921920 8e Linux LVM +/dev/sda3 0 - 0 0 0 Empty +/dev/sda4 0 - 0 0 0 Empty + +Disk /dev/mapper/centos-root: 2218 cylinders, 255 heads, 63 sectors/track + +Disk /dev/mapper/centos-swap: 261 cylinders, 255 heads, 63 sectors/track + diff --git a/tests/fixtures/centos-7.7/sfdisk-luB.out b/tests/fixtures/centos-7.7/sfdisk-luB.out new file mode 100644 index 00000000..dc1141d2 --- /dev/null +++ b/tests/fixtures/centos-7.7/sfdisk-luB.out @@ -0,0 +1,14 @@ + +Disk /dev/sda: 2610 cylinders, 255 heads, 63 sectors/track +Units: blocks of 1024 bytes, counting from 0 + + Device Boot Start End #blocks Id System +/dev/sda1 * 1024 1049599 1048576 83 Linux +/dev/sda2 1049600 20971519 19921920 8e Linux LVM +/dev/sda3 0 - 0 0 Empty +/dev/sda4 0 - 0 0 Empty + +Disk /dev/mapper/centos-root: 2218 cylinders, 255 heads, 63 sectors/track + +Disk /dev/mapper/centos-swap: 261 cylinders, 255 heads, 63 sectors/track + diff --git a/tests/fixtures/centos-7.7/sfdisk-luM.out b/tests/fixtures/centos-7.7/sfdisk-luM.out new file mode 100644 index 00000000..574c5824 --- /dev/null +++ b/tests/fixtures/centos-7.7/sfdisk-luM.out @@ -0,0 +1,14 @@ + +Disk /dev/sda: 2610 cylinders, 255 heads, 63 sectors/track +Units: 1MiB = 1024*1024 bytes, blocks of 1024 bytes, counting from 0 + + Device Boot Start End MiB #blocks Id System +/dev/sda1 * 1 1024 1024 1048576 83 Linux +/dev/sda2 1025 20479 19455 19921920 8e Linux LVM +/dev/sda3 0 - 0 0 0 Empty +/dev/sda4 0 - 0 0 0 Empty + +Disk /dev/mapper/centos-root: 2218 cylinders, 255 heads, 63 sectors/track + +Disk /dev/mapper/centos-swap: 261 cylinders, 255 heads, 63 sectors/track + diff --git a/tests/fixtures/centos-7.7/sfdisk-luS.out b/tests/fixtures/centos-7.7/sfdisk-luS.out new file mode 100644 index 00000000..d2202d81 --- /dev/null +++ b/tests/fixtures/centos-7.7/sfdisk-luS.out @@ -0,0 +1,14 @@ + +Disk /dev/sda: 2610 cylinders, 255 heads, 63 sectors/track +Units: sectors of 512 bytes, counting from 0 + + Device Boot Start End #sectors Id System +/dev/sda1 * 2048 2099199 2097152 83 Linux +/dev/sda2 2099200 41943039 39843840 8e Linux LVM +/dev/sda3 0 - 0 0 Empty +/dev/sda4 0 - 0 0 Empty + +Disk /dev/mapper/centos-root: 2218 cylinders, 255 heads, 63 sectors/track + +Disk /dev/mapper/centos-swap: 261 cylinders, 255 heads, 63 sectors/track +