mirror of
https://github.com/kellyjonbrazil/jc.git
synced 2025-07-13 01:20:24 +02:00
@ -1,5 +1,13 @@
|
||||
jc changelog
|
||||
|
||||
20230323 v1.23.1
|
||||
- Fix `zpool-status` command parser for lines that start with tab
|
||||
- Fix `timedatectl` command parser when RTC set to local
|
||||
- Fix to ensure `py.typed` file is included in the package wheel
|
||||
- Fix `lsusb` command parser to support CDC MBIM and CDC MBIM Extended fields
|
||||
- Add support for the `timesync-status` for the `timedatectl` command parser
|
||||
- Fix to ignore non-parser-plugins in the parser plugin directory
|
||||
|
||||
20230227 v1.23.0
|
||||
- Add input slicing as a `jc` command-line option
|
||||
- Add `ssh` configuration file parser
|
||||
|
14
README.md
14
README.md
@ -533,20 +533,22 @@ for item in result:
|
||||
print(item["filename"])
|
||||
```
|
||||
|
||||
### Custom Parsers
|
||||
Custom local parser plugins may be placed in a `jc/jcparsers` folder in your
|
||||
local **"App data directory"**:
|
||||
### Parser Plugins
|
||||
Parser plugins may be placed in a `jc/jcparsers` folder in your local
|
||||
**"App data directory"**:
|
||||
|
||||
- Linux/unix: `$HOME/.local/share/jc/jcparsers`
|
||||
- macOS: `$HOME/Library/Application Support/jc/jcparsers`
|
||||
- Windows: `$LOCALAPPDATA\jc\jc\jcparsers`
|
||||
|
||||
Local parser plugins are standard python module files. Use the
|
||||
Parser plugins are standard python module files. Use the
|
||||
[`jc/parsers/foo.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo.py)
|
||||
or [`jc/parsers/foo_s.py (streaming)`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo_s.py)
|
||||
parser as a template and simply place a `.py` file in the `jcparsers` subfolder.
|
||||
Any dependencies can be placed in the `jc` folder above `jcparsers` and can
|
||||
be imported in the parser code.
|
||||
|
||||
Local plugin filenames must be valid python module names and therefore must
|
||||
Parser plugin filenames must be valid python module names and therefore must
|
||||
start with a letter and consist entirely of alphanumerics and underscores.
|
||||
Local plugins may override default parsers.
|
||||
|
||||
@ -604,7 +606,7 @@ they are run on an unsupported platform. To see all parser information,
|
||||
including compatibility, run `jc -ap`.
|
||||
|
||||
You may still use a parser on an unsupported platform - for example, you may
|
||||
want to parse a file with linux `lsof` output on an macOS or Windows laptop. In
|
||||
want to parse a file with linux `lsof` output on a macOS or Windows laptop. In
|
||||
that case you can suppress the warning message with the `-q` cli option or the
|
||||
`quiet=True` function parameter in `parse()`:
|
||||
|
||||
|
@ -102,6 +102,24 @@ Schema:
|
||||
]
|
||||
}
|
||||
},
|
||||
"cdc_mbim": {
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
},
|
||||
"cdc_mbim_extended": {
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
},
|
||||
"videocontrol_descriptors": [
|
||||
{
|
||||
"<item>": {
|
||||
@ -312,4 +330,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.3 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.4 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
jc - JSON Convert `timedatectl` command output parser
|
||||
|
||||
Also supports the `timesync-status` option.
|
||||
|
||||
The `epoch_utc` calculated timestamp field is timezone-aware and is only
|
||||
available if the `universal_time` field is available.
|
||||
|
||||
@ -34,7 +36,24 @@ Schema:
|
||||
"system_clock_synchronized": boolean,
|
||||
"systemd-timesyncd.service_active": boolean,
|
||||
"rtc_in_local_tz": boolean,
|
||||
"dst_active": boolean
|
||||
"dst_active": boolean,
|
||||
"server": string,
|
||||
"poll_interval": string,
|
||||
"leap": string,
|
||||
"version": integer,
|
||||
"stratum": integer,
|
||||
"reference": string,
|
||||
"precision": string,
|
||||
"root_distance": string,
|
||||
"offset": float,
|
||||
"offset_unit": string,
|
||||
"delay": float,
|
||||
"delay_unit": string,
|
||||
"jitter": float,
|
||||
"jitter_unit": string,
|
||||
"packet_count": integer,
|
||||
"frequency": float,
|
||||
"frequency_unit": string
|
||||
}
|
||||
|
||||
Examples:
|
||||
@ -87,4 +106,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux
|
||||
|
||||
Version 1.7 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.8 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
@ -160,4 +160,4 @@ Returns:
|
||||
### Parser Information
|
||||
Compatibility: linux, darwin, freebsd
|
||||
|
||||
Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com)
|
||||
|
@ -124,6 +124,14 @@ Get a list of streaming parser module names to be used in
|
||||
`parse()`, `parser_info()`, and `get_help()`. This list is a subset of
|
||||
`parser_mod_list()`.
|
||||
"""
|
||||
from .lib import (__version__, parse, parser_mod_list, plugin_parser_mod_list,
|
||||
standard_parser_mod_list, streaming_parser_mod_list,
|
||||
parser_info, all_parser_info, get_help)
|
||||
from .lib import (
|
||||
__version__ as __version__,
|
||||
parse as parse,
|
||||
parser_mod_list as parser_mod_list,
|
||||
plugin_parser_mod_list as plugin_parser_mod_list,
|
||||
standard_parser_mod_list as standard_parser_mod_list,
|
||||
streaming_parser_mod_list as streaming_parser_mod_list,
|
||||
parser_info as parser_info,
|
||||
all_parser_info as all_parser_info,
|
||||
get_help as get_help
|
||||
)
|
||||
|
12
jc/cli.py
12
jc/cli.py
@ -215,14 +215,14 @@ class JcCli():
|
||||
category_text: str = ''
|
||||
padding_char: str = ' '
|
||||
all_parsers = all_parser_info(show_hidden=True, show_deprecated=False)
|
||||
generic = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'generic' in x['tags']]
|
||||
standard = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'standard' in x['tags']]
|
||||
command = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'command' in x['tags']]
|
||||
generic = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'generic' in x.get('tags', [])]
|
||||
standard = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'standard' in x.get('tags', [])]
|
||||
command = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'command' in x.get('tags', [])]
|
||||
file_str_bin = [
|
||||
{'arg': x['argument'], 'desc': x['description']} for x in all_parsers
|
||||
if 'file' in x['tags'] or
|
||||
'string' in x['tags'] or
|
||||
'binary' in x['tags']
|
||||
if 'file' in x.get('tags', []) or
|
||||
'string' in x.get('tags', []) or
|
||||
'binary' in x.get('tags', [])
|
||||
]
|
||||
streaming = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if x.get('streaming')]
|
||||
categories: Dict = {
|
||||
|
17
jc/lib.py
17
jc/lib.py
@ -9,7 +9,7 @@ from .jc_types import ParserInfoType, JSONDictType
|
||||
from jc import appdirs
|
||||
|
||||
|
||||
__version__ = '1.23.0'
|
||||
__version__ = '1.23.1'
|
||||
|
||||
parsers: List[str] = [
|
||||
'acpi',
|
||||
@ -213,6 +213,19 @@ def _modname_to_cliname(parser_mod_name: str) -> str:
|
||||
"""Return module's cli name (underscores converted to dashes)"""
|
||||
return parser_mod_name.replace('_', '-')
|
||||
|
||||
def _is_valid_parser_plugin(name: str, local_parsers_dir: str) -> bool:
|
||||
if re.match(r'\w+\.py$', name) and os.path.isfile(os.path.join(local_parsers_dir, name)):
|
||||
try:
|
||||
parser_mod_name = _cliname_to_modname(name)[0:-3]
|
||||
modpath = 'jcparsers.'
|
||||
plugin = importlib.import_module(f'{modpath}{parser_mod_name}')
|
||||
if hasattr(plugin, 'info') and hasattr(plugin, 'parse'):
|
||||
del plugin
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
return False
|
||||
|
||||
# Create the local_parsers list. This is a list of custom or
|
||||
# override parsers from <user_data_dir>/jc/jcparsers/*.py.
|
||||
# Once this list is created, extend the parsers list with it.
|
||||
@ -222,7 +235,7 @@ local_parsers_dir = os.path.join(data_dir, 'jcparsers')
|
||||
if os.path.isdir(local_parsers_dir):
|
||||
sys.path.append(data_dir)
|
||||
for name in os.listdir(local_parsers_dir):
|
||||
if re.match(r'\w+\.py$', name) and os.path.isfile(os.path.join(local_parsers_dir, name)):
|
||||
if _is_valid_parser_plugin(name, local_parsers_dir):
|
||||
plugin_name = name[0:-3]
|
||||
local_parsers.append(_modname_to_cliname(plugin_name))
|
||||
if plugin_name not in parsers:
|
||||
|
@ -97,6 +97,24 @@ Schema:
|
||||
]
|
||||
}
|
||||
},
|
||||
"cdc_mbim": {
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
},
|
||||
"cdc_mbim_extended": {
|
||||
"<item>": {
|
||||
"value": string,
|
||||
"description": string,
|
||||
"attributes": [
|
||||
string
|
||||
]
|
||||
}
|
||||
},
|
||||
"videocontrol_descriptors": [
|
||||
{
|
||||
"<item>": {
|
||||
@ -291,7 +309,7 @@ from jc.exceptions import ParseError
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.3'
|
||||
version = '1.4'
|
||||
description = '`lsusb` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@ -500,6 +518,8 @@ class _LsUsb():
|
||||
self.cdc_call_management = _descriptor_obj('cdc_call_management')
|
||||
self.cdc_acm = _descriptor_obj('cdc_acm')
|
||||
self.cdc_union = _descriptor_obj('cdc_union')
|
||||
self.cdc_mbim = _descriptor_obj('cdc_mbim')
|
||||
self.cdc_mbim_extended = _descriptor_obj('cdc_mbim_extended')
|
||||
self.endpoint_descriptors = _descriptor_list('endpoint_descriptor')
|
||||
self.videocontrol_interface_descriptors = _descriptor_list('videocontrol_interface_descriptor')
|
||||
self.videostreaming_interface_descriptors = _descriptor_list('videostreaming_interface_descriptor')
|
||||
@ -538,7 +558,8 @@ class _LsUsb():
|
||||
section_header = self.normal_section_header
|
||||
|
||||
if self.section == 'videocontrol_interface_descriptor' \
|
||||
or self.section == 'videostreaming_interface_descriptor':
|
||||
or self.section == 'videostreaming_interface_descriptor' \
|
||||
or self.section == 'cdc_mbim_extended':
|
||||
|
||||
section_header = self.larger_section_header
|
||||
|
||||
@ -689,6 +710,8 @@ class _LsUsb():
|
||||
' CDC Union:': 'cdc_union',
|
||||
' HID Device Descriptor:': 'hid_device_descriptor',
|
||||
' Report Descriptors:': 'report_descriptors',
|
||||
' CDC MBIM:': 'cdc_mbim',
|
||||
' CDC MBIM Extended:': 'cdc_mbim_extended',
|
||||
'Hub Descriptor:': 'hub_descriptor',
|
||||
' Hub Port Status:': 'hub_port_status',
|
||||
'Device Qualifier (for other device speed):': 'device_qualifier',
|
||||
@ -713,6 +736,8 @@ class _LsUsb():
|
||||
'cdc_call_management': self.cdc_call_management.list,
|
||||
'cdc_acm': self.cdc_acm.list,
|
||||
'cdc_union': self.cdc_union.list,
|
||||
'cdc_mbim': self.cdc_mbim.list,
|
||||
'cdc_mbim_extended': self.cdc_mbim_extended.list,
|
||||
'hid_device_descriptor': self.hid_device_descriptor.list,
|
||||
# 'report_descriptors': self.report_descriptors_list, # not implemented
|
||||
'videocontrol_interface_descriptor': self.videocontrol_interface_descriptors.list,
|
||||
@ -757,6 +782,8 @@ class _LsUsb():
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_call_management'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_acm'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_union'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_mbim'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_mbim_extended'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['hid_device_descriptor'] = {}
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['endpoint_descriptors'] = []
|
||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['endpoint_descriptors'][0] = {}
|
||||
@ -847,6 +874,12 @@ class _LsUsb():
|
||||
if self.cdc_union._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.cdc_union._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
if self.cdc_mbim._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.cdc_mbim._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
if self.cdc_mbim_extended._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.cdc_mbim_extended._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
if self.hid_device_descriptor._entries_for_this_bus_and_interface_idx_exist(idx, iface_idx):
|
||||
self.hid_device_descriptor._update_output(idx, iface_idx, i_desc_obj)
|
||||
|
||||
@ -923,6 +956,10 @@ def parse(data, raw=False, quiet=False):
|
||||
lsusb = _LsUsb()
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
# fix known too-long field names
|
||||
data = data.replace('bmNetworkCapabilities', 'bmNetworkCapabilit ')
|
||||
|
||||
for line in data.splitlines():
|
||||
# only -v option or no options are supported
|
||||
if line.startswith('/'):
|
||||
|
@ -204,7 +204,7 @@ def parse(
|
||||
pid_smaps_p = re.compile(r'^[0-9a-f]{12}-[0-9a-f]{12} [rwxsp\-]{4} [0-9a-f]{8} [0-9a-f]{2}:[0-9a-f]{2} \d+ [^\n]+\nSize:\s+\d+ \S\S\n')
|
||||
pid_stat_p = re.compile(r'^\d+ \(.+\) \S \d+ \d+ \d+ \d+ -?\d+ (?:\d+ ){43}\d+$', re.DOTALL)
|
||||
pid_statm_p = re.compile(r'^\d+ \d+ \d+\s\d+\s\d+\s\d+\s\d+$')
|
||||
pid_status_p = re.compile(r'^Name:\t.+\nUmask:\t\d+\nState:\t.+\nTgid:\t\d+\n')
|
||||
pid_status_p = re.compile(r'^Name:\t.+\n(?:Umask:\t\d+\n)?State:\t.+\nTgid:\t\d+\n')
|
||||
|
||||
# scsi_device_info = re.compile(r"^'\w+' '.+' 0x\d+")
|
||||
# scsi_scsi_p = re.compile(r'^Attached devices:\nHost: \w+ ')
|
||||
|
@ -1,5 +1,7 @@
|
||||
"""jc - JSON Convert `timedatectl` command output parser
|
||||
|
||||
Also supports the `timesync-status` option.
|
||||
|
||||
The `epoch_utc` calculated timestamp field is timezone-aware and is only
|
||||
available if the `universal_time` field is available.
|
||||
|
||||
@ -29,7 +31,24 @@ Schema:
|
||||
"system_clock_synchronized": boolean,
|
||||
"systemd-timesyncd.service_active": boolean,
|
||||
"rtc_in_local_tz": boolean,
|
||||
"dst_active": boolean
|
||||
"dst_active": boolean,
|
||||
"server": string,
|
||||
"poll_interval": string,
|
||||
"leap": string,
|
||||
"version": integer,
|
||||
"stratum": integer,
|
||||
"reference": string,
|
||||
"precision": string,
|
||||
"root_distance": string,
|
||||
"offset": float,
|
||||
"offset_unit": string,
|
||||
"delay": float,
|
||||
"delay_unit": string,
|
||||
"jitter": float,
|
||||
"jitter_unit": string,
|
||||
"packet_count": integer,
|
||||
"frequency": float,
|
||||
"frequency_unit": string
|
||||
}
|
||||
|
||||
Examples:
|
||||
@ -64,7 +83,7 @@ import jc.utils
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.7'
|
||||
version = '1.8'
|
||||
description = '`timedatectl status` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@ -90,11 +109,26 @@ def _process(proc_data):
|
||||
"""
|
||||
bool_list = {'ntp_enabled', 'ntp_synchronized', 'rtc_in_local_tz', 'dst_active',
|
||||
'system_clock_synchronized', 'systemd-timesyncd.service_active'}
|
||||
int_list = {'version', 'stratum', 'packet_count'}
|
||||
float_list = {'offset', 'delay', 'jitter', 'frequency'}
|
||||
|
||||
for key in ['offset', 'delay', 'jitter']:
|
||||
if key in proc_data:
|
||||
proc_data[key + '_unit'] = proc_data[key][-2:]
|
||||
|
||||
if 'frequency' in proc_data:
|
||||
proc_data['frequency_unit'] = proc_data['frequency'][-3:]
|
||||
|
||||
for key in proc_data:
|
||||
if key in bool_list:
|
||||
proc_data[key] = jc.utils.convert_to_bool(proc_data[key])
|
||||
|
||||
if key in int_list:
|
||||
proc_data[key] = jc.utils.convert_to_int(proc_data[key])
|
||||
|
||||
if key in float_list:
|
||||
proc_data[key] = jc.utils.convert_to_float(proc_data[key])
|
||||
|
||||
if 'universal_time' in proc_data:
|
||||
ts = jc.utils.timestamp(proc_data['universal_time'], format_hint=(7300,))
|
||||
proc_data['epoch_utc'] = ts.utc
|
||||
@ -120,17 +154,27 @@ def parse(data, raw=False, quiet=False):
|
||||
jc.utils.input_type_check(data)
|
||||
|
||||
raw_output = {}
|
||||
valid_fields = {
|
||||
'local time', 'universal time', 'rtc time', 'time zone', 'ntp enabled',
|
||||
'ntp synchronized', 'rtc in local tz', 'dst active',
|
||||
'system clock synchronized', 'ntp service',
|
||||
'systemd-timesyncd.service active', 'server', 'poll interval', 'leap',
|
||||
'version', 'stratum', 'reference', 'precision', 'root distance',
|
||||
'offset', 'delay', 'jitter', 'packet count', 'frequency'
|
||||
}
|
||||
|
||||
if jc.utils.has_data(data):
|
||||
|
||||
for line in filter(None, data.splitlines()):
|
||||
linedata = line.split(':', maxsplit=1)
|
||||
raw_output[linedata[0].strip().lower().replace(' ', '_')] = linedata[1].strip()
|
||||
try:
|
||||
key, val = line.split(':', maxsplit=1)
|
||||
key = key.lower().strip()
|
||||
val = val.strip()
|
||||
except ValueError:
|
||||
continue
|
||||
|
||||
if linedata[0].strip() == 'DST active':
|
||||
break
|
||||
if key in valid_fields:
|
||||
keyname = key.replace(' ', '_')
|
||||
raw_output[keyname] = val
|
||||
|
||||
if raw:
|
||||
return raw_output
|
||||
else:
|
||||
return _process(raw_output)
|
||||
return raw_output if raw else _process(raw_output)
|
||||
|
@ -138,7 +138,7 @@ from jc.parsers.kv import parse as kv_parse
|
||||
|
||||
class info():
|
||||
"""Provides parser metadata (version, author, etc.)"""
|
||||
version = '1.0'
|
||||
version = '1.1'
|
||||
description = '`zpool status` command parser'
|
||||
author = 'Kelly Brazil'
|
||||
author_email = 'kellyjonbrazil@gmail.com'
|
||||
@ -233,7 +233,7 @@ def parse(
|
||||
continue
|
||||
|
||||
# preserve indentation in continuation lines
|
||||
if line.startswith(' '):
|
||||
if line.startswith(' ') or line.startswith('\t'):
|
||||
pool_str += line + '\n'
|
||||
continue
|
||||
|
||||
|
12
man/jc.1
12
man/jc.1
@ -1,4 +1,4 @@
|
||||
.TH jc 1 2023-02-27 1.23.0 "JSON Convert"
|
||||
.TH jc 1 2023-03-23 1.23.1 "JSON Convert"
|
||||
.SH NAME
|
||||
\fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types,
|
||||
and strings
|
||||
@ -1316,8 +1316,8 @@ etc...
|
||||
Note: Unbuffered output can be slower for large data streams.
|
||||
.RE
|
||||
|
||||
.SH CUSTOM PARSERS
|
||||
Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
||||
.SH PARSER PLUGINS
|
||||
Parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
||||
local "App data directory":
|
||||
|
||||
.RS
|
||||
@ -1328,11 +1328,13 @@ local "App data directory":
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Local parser plugins are standard python module files. Use the
|
||||
Parser plugins are standard python module files. Use the
|
||||
\fBjc/parsers/foo.py\fP or \fBjc/parsers/foo_s.py\fP (streaming) parser as a
|
||||
template and simply place a \fB.py\fP file in the \fBjcparsers\fP subfolder.
|
||||
Any dependencies can be placed in the \fBjc\fP folder above \fBjcparsers\fP
|
||||
and can be imported in the parser code.
|
||||
|
||||
Local plugin filenames must be valid python module names and therefore must
|
||||
Parser plugin filenames must be valid python module names and therefore must
|
||||
start with a letter and consist entirely of alphanumerics and underscores. Local
|
||||
plugins may override default parsers.
|
||||
|
||||
|
3
setup.py
3
setup.py
@ -5,7 +5,7 @@ with open('README.md', 'r') as f:
|
||||
|
||||
setuptools.setup(
|
||||
name='jc',
|
||||
version='1.23.0',
|
||||
version='1.23.1',
|
||||
author='Kelly Brazil',
|
||||
author_email='kellyjonbrazil@gmail.com',
|
||||
description='Converts the output of popular command-line tools and file-types to JSON.',
|
||||
@ -20,6 +20,7 @@ setuptools.setup(
|
||||
python_requires='>=3.6',
|
||||
url='https://github.com/kellyjonbrazil/jc',
|
||||
packages=setuptools.find_packages(exclude=['*.tests', '*.tests.*', 'tests.*', 'tests']),
|
||||
package_data={'jc': ['py.typed']},
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'jc=jc.cli:main'
|
||||
|
@ -366,8 +366,8 @@ etc...
|
||||
Note: Unbuffered output can be slower for large data streams.
|
||||
.RE
|
||||
|
||||
.SH CUSTOM PARSERS
|
||||
Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
||||
.SH PARSER PLUGINS
|
||||
Parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
||||
local "App data directory":
|
||||
|
||||
.RS
|
||||
@ -378,11 +378,13 @@ local "App data directory":
|
||||
.fi
|
||||
.RE
|
||||
|
||||
Local parser plugins are standard python module files. Use the
|
||||
Parser plugins are standard python module files. Use the
|
||||
\fBjc/parsers/foo.py\fP or \fBjc/parsers/foo_s.py\fP (streaming) parser as a
|
||||
template and simply place a \fB.py\fP file in the \fBjcparsers\fP subfolder.
|
||||
Any dependencies can be placed in the \fBjc\fP folder above \fBjcparsers\fP
|
||||
and can be imported in the parser code.
|
||||
|
||||
Local plugin filenames must be valid python module names and therefore must
|
||||
Parser plugin filenames must be valid python module names and therefore must
|
||||
start with a letter and consist entirely of alphanumerics and underscores. Local
|
||||
plugins may override default parsers.
|
||||
|
||||
|
@ -393,20 +393,22 @@ for item in result:
|
||||
print(item["filename"])
|
||||
```
|
||||
|
||||
### Custom Parsers
|
||||
Custom local parser plugins may be placed in a `jc/jcparsers` folder in your
|
||||
local **"App data directory"**:
|
||||
### Parser Plugins
|
||||
Parser plugins may be placed in a `jc/jcparsers` folder in your local
|
||||
**"App data directory"**:
|
||||
|
||||
- Linux/unix: `$HOME/.local/share/jc/jcparsers`
|
||||
- macOS: `$HOME/Library/Application Support/jc/jcparsers`
|
||||
- Windows: `$LOCALAPPDATA\jc\jc\jcparsers`
|
||||
|
||||
Local parser plugins are standard python module files. Use the
|
||||
Parser plugins are standard python module files. Use the
|
||||
[`jc/parsers/foo.py`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo.py)
|
||||
or [`jc/parsers/foo_s.py (streaming)`](https://github.com/kellyjonbrazil/jc/blob/master/jc/parsers/foo_s.py)
|
||||
parser as a template and simply place a `.py` file in the `jcparsers` subfolder.
|
||||
Any dependencies can be placed in the `jc` folder above `jcparsers` and can
|
||||
be imported in the parser code.
|
||||
|
||||
Local plugin filenames must be valid python module names and therefore must
|
||||
Parser plugin filenames must be valid python module names and therefore must
|
||||
start with a letter and consist entirely of alphanumerics and underscores.
|
||||
Local plugins may override default parsers.
|
||||
|
||||
@ -464,7 +466,7 @@ they are run on an unsupported platform. To see all parser information,
|
||||
including compatibility, run `jc -ap`.
|
||||
|
||||
You may still use a parser on an unsupported platform - for example, you may
|
||||
want to parse a file with linux `lsof` output on an macOS or Windows laptop. In
|
||||
want to parse a file with linux `lsof` output on a macOS or Windows laptop. In
|
||||
that case you can suppress the warning message with the `-q` cli option or the
|
||||
`quiet=True` function parameter in `parse()`:
|
||||
|
||||
|
1
tests/fixtures/generic/lsusb-cdc-mbim.json
vendored
Normal file
1
tests/fixtures/generic/lsusb-cdc-mbim.json
vendored
Normal file
File diff suppressed because one or more lines are too long
286
tests/fixtures/generic/lsusb-cdc-mbim.out
vendored
Normal file
286
tests/fixtures/generic/lsusb-cdc-mbim.out
vendored
Normal file
@ -0,0 +1,286 @@
|
||||
Bus 001 Device 003: ID 2c7c:0125 Quectel Wireless Solutions Co., Ltd. EC25 LTE modem
|
||||
Device Descriptor:
|
||||
bLength 18
|
||||
bDescriptorType 1
|
||||
bcdUSB 2.00
|
||||
bDeviceClass 239 Miscellaneous Device
|
||||
bDeviceSubClass 2
|
||||
bDeviceProtocol 1 Interface Association
|
||||
bMaxPacketSize0 64
|
||||
idVendor 0x2c7c Quectel Wireless Solutions Co., Ltd.
|
||||
idProduct 0x0125 EC25 LTE modem
|
||||
bcdDevice 3.18
|
||||
iManufacturer 1 Quectel Incorporated
|
||||
iProduct 2 LTE Module
|
||||
iSerial 0
|
||||
bNumConfigurations 1
|
||||
Configuration Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 2
|
||||
wTotalLength 0x0109
|
||||
bNumInterfaces 6
|
||||
bConfigurationValue 1
|
||||
iConfiguration 0
|
||||
bmAttributes 0xa0
|
||||
(Bus Powered)
|
||||
Remote Wakeup
|
||||
MaxPower 500mA
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 0
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 2
|
||||
bInterfaceClass 255 Vendor Specific Class
|
||||
bInterfaceSubClass 255 Vendor Specific Subclass
|
||||
bInterfaceProtocol 255 Vendor Specific Protocol
|
||||
iInterface 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x81 EP 1 IN
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x01 EP 1 OUT
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 1
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 3
|
||||
bInterfaceClass 255 Vendor Specific Class
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 0
|
||||
iInterface 0
|
||||
** UNRECOGNIZED: 05 24 00 10 01
|
||||
** UNRECOGNIZED: 05 24 01 00 00
|
||||
** UNRECOGNIZED: 04 24 02 02
|
||||
** UNRECOGNIZED: 05 24 06 00 00
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x83 EP 3 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x000a 1x 10 bytes
|
||||
bInterval 9
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x82 EP 2 IN
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x02 EP 2 OUT
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 2
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 3
|
||||
bInterfaceClass 255 Vendor Specific Class
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 0
|
||||
iInterface 0
|
||||
** UNRECOGNIZED: 05 24 00 10 01
|
||||
** UNRECOGNIZED: 05 24 01 00 00
|
||||
** UNRECOGNIZED: 04 24 02 02
|
||||
** UNRECOGNIZED: 05 24 06 00 00
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x85 EP 5 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x000a 1x 10 bytes
|
||||
bInterval 9
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x84 EP 4 IN
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x03 EP 3 OUT
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 3
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 3
|
||||
bInterfaceClass 255 Vendor Specific Class
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 0
|
||||
iInterface 0
|
||||
** UNRECOGNIZED: 05 24 00 10 01
|
||||
** UNRECOGNIZED: 05 24 01 00 00
|
||||
** UNRECOGNIZED: 04 24 02 02
|
||||
** UNRECOGNIZED: 05 24 06 00 00
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x87 EP 7 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x000a 1x 10 bytes
|
||||
bInterval 9
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x86 EP 6 IN
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x04 EP 4 OUT
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Interface Association:
|
||||
bLength 8
|
||||
bDescriptorType 11
|
||||
bFirstInterface 4
|
||||
bInterfaceCount 2
|
||||
bFunctionClass 2 Communications
|
||||
bFunctionSubClass 14
|
||||
bFunctionProtocol 0
|
||||
iFunction 0
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 4
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 1
|
||||
bInterfaceClass 2 Communications
|
||||
bInterfaceSubClass 14
|
||||
bInterfaceProtocol 0
|
||||
iInterface 6 LTE Module
|
||||
CDC Header:
|
||||
bcdCDC 1.10
|
||||
CDC Union:
|
||||
bMasterInterface 4
|
||||
bSlaveInterface 5
|
||||
CDC MBIM:
|
||||
bcdMBIMVersion 1.00
|
||||
wMaxControlMessage 4096
|
||||
bNumberFilters 32
|
||||
bMaxFilterSize 128
|
||||
wMaxSegmentSize 2048
|
||||
bmNetworkCapabilities 0x20
|
||||
8-byte ntb input size
|
||||
CDC MBIM Extended:
|
||||
bcdMBIMExtendedVersion 1.00
|
||||
bMaxOutstandingCommandMessages 64
|
||||
wMTU 1500
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x89 EP 9 IN
|
||||
bmAttributes 3
|
||||
Transfer Type Interrupt
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0040 1x 64 bytes
|
||||
bInterval 9
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 5
|
||||
bAlternateSetting 0
|
||||
bNumEndpoints 0
|
||||
bInterfaceClass 10 CDC Data
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 2
|
||||
iInterface 7 MBIM Data
|
||||
Interface Descriptor:
|
||||
bLength 9
|
||||
bDescriptorType 4
|
||||
bInterfaceNumber 5
|
||||
bAlternateSetting 1
|
||||
bNumEndpoints 2
|
||||
bInterfaceClass 10 CDC Data
|
||||
bInterfaceSubClass 0
|
||||
bInterfaceProtocol 2
|
||||
iInterface 7 MBIM Data
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x88 EP 8 IN
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Endpoint Descriptor:
|
||||
bLength 7
|
||||
bDescriptorType 5
|
||||
bEndpointAddress 0x05 EP 5 OUT
|
||||
bmAttributes 2
|
||||
Transfer Type Bulk
|
||||
Synch Type None
|
||||
Usage Type Data
|
||||
wMaxPacketSize 0x0200 1x 512 bytes
|
||||
bInterval 0
|
||||
Device Qualifier (for other device speed):
|
||||
bLength 10
|
||||
bDescriptorType 6
|
||||
bcdUSB 2.00
|
||||
bDeviceClass 239 Miscellaneous Device
|
||||
bDeviceSubClass 2
|
||||
bDeviceProtocol 1 Interface Association
|
||||
bMaxPacketSize0 64
|
||||
bNumConfigurations 1
|
||||
Device Status: 0x0000
|
||||
(Bus Powered)
|
1
tests/fixtures/generic/timedatectl-rtc-local.json
vendored
Normal file
1
tests/fixtures/generic/timedatectl-rtc-local.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"local_time":"Mon 2023-03-13 14:17:38 EET","universal_time":"Mon 2023-03-13 12:17:38 UTC","rtc_time":"Mon 2023-03-13 12:17:38","time_zone":"Europe/Athens (EET, +0200)","system_clock_synchronized":true,"ntp_service":"active","rtc_in_local_tz":true,"epoch_utc":1678709858}
|
14
tests/fixtures/generic/timedatectl-rtc-local.out
vendored
Normal file
14
tests/fixtures/generic/timedatectl-rtc-local.out
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
Local time: Mon 2023-03-13 14:17:38 EET
|
||||
Universal time: Mon 2023-03-13 12:17:38 UTC
|
||||
RTC time: Mon 2023-03-13 12:17:38
|
||||
Time zone: Europe/Athens (EET, +0200)
|
||||
System clock synchronized: yes
|
||||
NTP service: active
|
||||
RTC in local TZ: yes
|
||||
|
||||
Warning: The system is configured to read the RTC time in the local time zone.
|
||||
This mode cannot be fully supported. It will create various problems
|
||||
with time zone changes and daylight saving time adjustments. The RTC
|
||||
time is never updated, it relies on external facilities to maintain it.
|
||||
If at all possible, use RTC in UTC by calling
|
||||
'timedatectl set-local-rtc 0'.
|
1
tests/fixtures/generic/timedatectl-timesync-status.json
vendored
Normal file
1
tests/fixtures/generic/timedatectl-timesync-status.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"server":"216.239.35.8 (time.google.com)","poll_interval":"8min 32s (min: 32s; max 34min 8s)","leap":"normal","version":4,"stratum":1,"reference":"GOOG","precision":"1us (-20)","root_distance":"91us (max: 5s)","offset":-5.224,"delay":65.884,"jitter":5.386,"packet_count":5,"frequency":27.071,"offset_unit":"ms","delay_unit":"ms","jitter_unit":"ms","frequency_unit":"ppm"}
|
13
tests/fixtures/generic/timedatectl-timesync-status.out
vendored
Normal file
13
tests/fixtures/generic/timedatectl-timesync-status.out
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
Server: 216.239.35.8 (time.google.com)
|
||||
Poll interval: 8min 32s (min: 32s; max 34min 8s)
|
||||
Leap: normal
|
||||
Version: 4
|
||||
Stratum: 1
|
||||
Reference: GOOG
|
||||
Precision: 1us (-20)
|
||||
Root distance: 91us (max: 5s)
|
||||
Offset: -5.224ms
|
||||
Delay: 65.884ms
|
||||
Jitter: 5.386ms
|
||||
Packet count: 5
|
||||
Frequency: +27.071ppm
|
1
tests/fixtures/generic/zpool-status-v4.json
vendored
Normal file
1
tests/fixtures/generic/zpool-status-v4.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
[{"pool":"pool1","state":"ONLINE","status":"Some supported and requested features are not enabled on the pool.\nThe pool can still be used, but some features are unavailable.","action":"Enable all features using 'zpool upgrade'. Once this is done,\nthe pool may no longer be accessible by software that does not support\nthe features. See zpool-features(7) for details.","scan":"scrub repaired 0B in 11:16:03 with 0 errors on Sun Feb 12 11:40:04 2023","config":[{"name":"pool1","state":"ONLINE","read":0,"write":0,"checksum":0},{"name":"mirror-0","state":"ONLINE","read":0,"write":0,"checksum":0},{"name":"wwn-0x5000c500c65ac66f","state":"ONLINE","read":0,"write":0,"checksum":0},{"name":"wwn-0x5000c500c5eee542","state":"ONLINE","read":0,"write":0,"checksum":0},{"name":"mirror-1","state":"ONLINE","read":0,"write":0,"checksum":0},{"name":"wwn-0x5000c500e39e8af6","state":"ONLINE","read":0,"write":0,"checksum":0},{"name":"wwn-0x5000c500e3b3a41e","state":"ONLINE","read":0,"write":0,"checksum":0}],"errors":"No known data errors"}]
|
21
tests/fixtures/generic/zpool-status-v4.out
vendored
Normal file
21
tests/fixtures/generic/zpool-status-v4.out
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
pool: pool1
|
||||
state: ONLINE
|
||||
status: Some supported and requested features are not enabled on the pool.
|
||||
The pool can still be used, but some features are unavailable.
|
||||
action: Enable all features using 'zpool upgrade'. Once this is done,
|
||||
the pool may no longer be accessible by software that does not support
|
||||
the features. See zpool-features(7) for details.
|
||||
scan: scrub repaired 0B in 11:16:03 with 0 errors on Sun Feb 12 11:40:04 2023
|
||||
config:
|
||||
|
||||
NAME STATE READ WRITE CKSUM
|
||||
pool1 ONLINE 0 0 0
|
||||
mirror-0 ONLINE 0 0 0
|
||||
wwn-0x5000c500c65ac66f ONLINE 0 0 0
|
||||
wwn-0x5000c500c5eee542 ONLINE 0 0 0
|
||||
mirror-1 ONLINE 0 0 0
|
||||
wwn-0x5000c500e39e8af6 ONLINE 0 0 0
|
||||
wwn-0x5000c500e3b3a41e ONLINE 0 0 0
|
||||
|
||||
errors: No known data errors
|
||||
|
@ -37,6 +37,9 @@ class MyTests(unittest.TestCase):
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/lsusb-extra-hub-port-status-info.out'), 'r', encoding='utf-8') as f:
|
||||
generic_lsusb_extra_hub_port_status_info = f.read()
|
||||
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/lsusb-cdc-mbim.out'), 'r', encoding='utf-8') as f:
|
||||
generic_lsusb_cdc_mbim = f.read()
|
||||
|
||||
# output
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/lsusb.json'), 'r', encoding='utf-8') as f:
|
||||
centos_7_7_lsusb_json = json.loads(f.read())
|
||||
@ -62,6 +65,9 @@ class MyTests(unittest.TestCase):
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/lsusb-extra-hub-port-status-info.json'), 'r', encoding='utf-8') as f:
|
||||
generic_lsusb_extra_hub_port_status_info_json = json.loads(f.read())
|
||||
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/lsusb-cdc-mbim.json'), 'r', encoding='utf-8') as f:
|
||||
generic_lsusb_cdc_mbim_json = json.loads(f.read())
|
||||
|
||||
|
||||
def test_lsusb_nodata(self):
|
||||
"""
|
||||
@ -123,6 +129,12 @@ class MyTests(unittest.TestCase):
|
||||
"""
|
||||
self.assertEqual(jc.parsers.lsusb.parse(self.generic_lsusb_extra_hub_port_status_info, quiet=True), self.generic_lsusb_extra_hub_port_status_info_json)
|
||||
|
||||
def test_lsusb_cdc_mbim(self):
|
||||
"""
|
||||
Test 'lsusb -v' with CDC MBIM and CDC MBIM Extended fields
|
||||
"""
|
||||
self.assertEqual(jc.parsers.lsusb.parse(self.generic_lsusb_cdc_mbim, quiet=True), self.generic_lsusb_cdc_mbim_json)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -15,6 +15,12 @@ class MyTests(unittest.TestCase):
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/timedatectl.out'), 'r', encoding='utf-8') as f:
|
||||
ubuntu_18_4_timedatectl = f.read()
|
||||
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/timedatectl-rtc-local.out'), 'r', encoding='utf-8') as f:
|
||||
timedatectl_rtc_local = f.read()
|
||||
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/timedatectl-timesync-status.out'), 'r', encoding='utf-8') as f:
|
||||
timedatectl_timesync_status = f.read()
|
||||
|
||||
# output
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/timedatectl.json'), 'r', encoding='utf-8') as f:
|
||||
centos_7_7_timedatectl_json = json.loads(f.read())
|
||||
@ -22,6 +28,12 @@ class MyTests(unittest.TestCase):
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/ubuntu-18.04/timedatectl.json'), 'r', encoding='utf-8') as f:
|
||||
ubuntu_18_4_timedatectl_json = json.loads(f.read())
|
||||
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/timedatectl-rtc-local.json'), 'r', encoding='utf-8') as f:
|
||||
timedatectl_rtc_local_json = json.loads(f.read())
|
||||
|
||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/timedatectl-timesync-status.json'), 'r', encoding='utf-8') as f:
|
||||
timedatectl_timesync_status_json = json.loads(f.read())
|
||||
|
||||
|
||||
def test_timedatectl_nodata(self):
|
||||
"""
|
||||
@ -41,6 +53,18 @@ class MyTests(unittest.TestCase):
|
||||
"""
|
||||
self.assertEqual(jc.parsers.timedatectl.parse(self.ubuntu_18_4_timedatectl, quiet=True), self.ubuntu_18_4_timedatectl_json)
|
||||
|
||||
def test_timedatectl_rtc_local(self):
|
||||
"""
|
||||
Test 'timedatectl' with RTC set to local
|
||||
"""
|
||||
self.assertEqual(jc.parsers.timedatectl.parse(self.timedatectl_rtc_local, quiet=True), self.timedatectl_rtc_local_json)
|
||||
|
||||
def test_timedatectl_timesync_status(self):
|
||||
"""
|
||||
Test 'timedatectl timesync-status'
|
||||
"""
|
||||
self.assertEqual(jc.parsers.timedatectl.parse(self.timedatectl_timesync_status, quiet=True), self.timedatectl_timesync_status_json)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -22,7 +22,10 @@ class MyTests(unittest.TestCase):
|
||||
'fixtures/generic/zpool-status-v2.json'),
|
||||
'zpool_status3': (
|
||||
'fixtures/generic/zpool-status-v3.out',
|
||||
'fixtures/generic/zpool-status-v3.json')
|
||||
'fixtures/generic/zpool-status-v3.json'),
|
||||
'zpool_status4': (
|
||||
'fixtures/generic/zpool-status-v4.out',
|
||||
'fixtures/generic/zpool-status-v4.json')
|
||||
}
|
||||
|
||||
for file, filepaths in fixtures.items():
|
||||
@ -66,6 +69,15 @@ class MyTests(unittest.TestCase):
|
||||
self.f_json['zpool_status3']
|
||||
)
|
||||
|
||||
def test_zpool_status_v_with_tabs(self):
|
||||
"""
|
||||
Test 'zpool status -v' with tabs instead of spaces
|
||||
"""
|
||||
self.assertEqual(
|
||||
parse(self.f_in['zpool_status4'], quiet=True),
|
||||
self.f_json['zpool_status4']
|
||||
)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Reference in New Issue
Block a user