mirror of
https://github.com/kellyjonbrazil/jc.git
synced 2025-07-15 01:24:29 +02:00
@ -1,5 +1,13 @@
|
|||||||
jc changelog
|
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
|
20230227 v1.23.0
|
||||||
- Add input slicing as a `jc` command-line option
|
- Add input slicing as a `jc` command-line option
|
||||||
- Add `ssh` configuration file parser
|
- Add `ssh` configuration file parser
|
||||||
|
14
README.md
14
README.md
@ -533,20 +533,22 @@ for item in result:
|
|||||||
print(item["filename"])
|
print(item["filename"])
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Parsers
|
### Parser Plugins
|
||||||
Custom local parser plugins may be placed in a `jc/jcparsers` folder in your
|
Parser plugins may be placed in a `jc/jcparsers` folder in your local
|
||||||
local **"App data directory"**:
|
**"App data directory"**:
|
||||||
|
|
||||||
- Linux/unix: `$HOME/.local/share/jc/jcparsers`
|
- Linux/unix: `$HOME/.local/share/jc/jcparsers`
|
||||||
- macOS: `$HOME/Library/Application Support/jc/jcparsers`
|
- macOS: `$HOME/Library/Application Support/jc/jcparsers`
|
||||||
- Windows: `$LOCALAPPDATA\jc\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)
|
[`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)
|
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.
|
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.
|
start with a letter and consist entirely of alphanumerics and underscores.
|
||||||
Local plugins may override default parsers.
|
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`.
|
including compatibility, run `jc -ap`.
|
||||||
|
|
||||||
You may still use a parser on an unsupported platform - for example, you may
|
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
|
that case you can suppress the warning message with the `-q` cli option or the
|
||||||
`quiet=True` function parameter in `parse()`:
|
`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": [
|
"videocontrol_descriptors": [
|
||||||
{
|
{
|
||||||
"<item>": {
|
"<item>": {
|
||||||
@ -312,4 +330,4 @@ Returns:
|
|||||||
### Parser Information
|
### Parser Information
|
||||||
Compatibility: linux
|
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
|
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
|
The `epoch_utc` calculated timestamp field is timezone-aware and is only
|
||||||
available if the `universal_time` field is available.
|
available if the `universal_time` field is available.
|
||||||
|
|
||||||
@ -34,7 +36,24 @@ Schema:
|
|||||||
"system_clock_synchronized": boolean,
|
"system_clock_synchronized": boolean,
|
||||||
"systemd-timesyncd.service_active": boolean,
|
"systemd-timesyncd.service_active": boolean,
|
||||||
"rtc_in_local_tz": 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:
|
Examples:
|
||||||
@ -87,4 +106,4 @@ Returns:
|
|||||||
### Parser Information
|
### Parser Information
|
||||||
Compatibility: linux
|
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
|
### Parser Information
|
||||||
Compatibility: linux, darwin, freebsd
|
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
|
`parse()`, `parser_info()`, and `get_help()`. This list is a subset of
|
||||||
`parser_mod_list()`.
|
`parser_mod_list()`.
|
||||||
"""
|
"""
|
||||||
from .lib import (__version__, parse, parser_mod_list, plugin_parser_mod_list,
|
from .lib import (
|
||||||
standard_parser_mod_list, streaming_parser_mod_list,
|
__version__ as __version__,
|
||||||
parser_info, all_parser_info, get_help)
|
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 = ''
|
category_text: str = ''
|
||||||
padding_char: str = ' '
|
padding_char: str = ' '
|
||||||
all_parsers = all_parser_info(show_hidden=True, show_deprecated=False)
|
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']]
|
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['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['tags']]
|
command = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'command' in x.get('tags', [])]
|
||||||
file_str_bin = [
|
file_str_bin = [
|
||||||
{'arg': x['argument'], 'desc': x['description']} for x in all_parsers
|
{'arg': x['argument'], 'desc': x['description']} for x in all_parsers
|
||||||
if 'file' in x['tags'] or
|
if 'file' in x.get('tags', []) or
|
||||||
'string' in x['tags'] or
|
'string' in x.get('tags', []) or
|
||||||
'binary' in x['tags']
|
'binary' in x.get('tags', [])
|
||||||
]
|
]
|
||||||
streaming = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if x.get('streaming')]
|
streaming = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if x.get('streaming')]
|
||||||
categories: Dict = {
|
categories: Dict = {
|
||||||
|
17
jc/lib.py
17
jc/lib.py
@ -9,7 +9,7 @@ from .jc_types import ParserInfoType, JSONDictType
|
|||||||
from jc import appdirs
|
from jc import appdirs
|
||||||
|
|
||||||
|
|
||||||
__version__ = '1.23.0'
|
__version__ = '1.23.1'
|
||||||
|
|
||||||
parsers: List[str] = [
|
parsers: List[str] = [
|
||||||
'acpi',
|
'acpi',
|
||||||
@ -213,6 +213,19 @@ def _modname_to_cliname(parser_mod_name: str) -> str:
|
|||||||
"""Return module's cli name (underscores converted to dashes)"""
|
"""Return module's cli name (underscores converted to dashes)"""
|
||||||
return parser_mod_name.replace('_', '-')
|
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
|
# Create the local_parsers list. This is a list of custom or
|
||||||
# override parsers from <user_data_dir>/jc/jcparsers/*.py.
|
# override parsers from <user_data_dir>/jc/jcparsers/*.py.
|
||||||
# Once this list is created, extend the parsers list with it.
|
# 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):
|
if os.path.isdir(local_parsers_dir):
|
||||||
sys.path.append(data_dir)
|
sys.path.append(data_dir)
|
||||||
for name in os.listdir(local_parsers_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]
|
plugin_name = name[0:-3]
|
||||||
local_parsers.append(_modname_to_cliname(plugin_name))
|
local_parsers.append(_modname_to_cliname(plugin_name))
|
||||||
if plugin_name not in parsers:
|
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": [
|
"videocontrol_descriptors": [
|
||||||
{
|
{
|
||||||
"<item>": {
|
"<item>": {
|
||||||
@ -291,7 +309,7 @@ from jc.exceptions import ParseError
|
|||||||
|
|
||||||
class info():
|
class info():
|
||||||
"""Provides parser metadata (version, author, etc.)"""
|
"""Provides parser metadata (version, author, etc.)"""
|
||||||
version = '1.3'
|
version = '1.4'
|
||||||
description = '`lsusb` command parser'
|
description = '`lsusb` command parser'
|
||||||
author = 'Kelly Brazil'
|
author = 'Kelly Brazil'
|
||||||
author_email = 'kellyjonbrazil@gmail.com'
|
author_email = 'kellyjonbrazil@gmail.com'
|
||||||
@ -500,6 +518,8 @@ class _LsUsb():
|
|||||||
self.cdc_call_management = _descriptor_obj('cdc_call_management')
|
self.cdc_call_management = _descriptor_obj('cdc_call_management')
|
||||||
self.cdc_acm = _descriptor_obj('cdc_acm')
|
self.cdc_acm = _descriptor_obj('cdc_acm')
|
||||||
self.cdc_union = _descriptor_obj('cdc_union')
|
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.endpoint_descriptors = _descriptor_list('endpoint_descriptor')
|
||||||
self.videocontrol_interface_descriptors = _descriptor_list('videocontrol_interface_descriptor')
|
self.videocontrol_interface_descriptors = _descriptor_list('videocontrol_interface_descriptor')
|
||||||
self.videostreaming_interface_descriptors = _descriptor_list('videostreaming_interface_descriptor')
|
self.videostreaming_interface_descriptors = _descriptor_list('videostreaming_interface_descriptor')
|
||||||
@ -538,7 +558,8 @@ class _LsUsb():
|
|||||||
section_header = self.normal_section_header
|
section_header = self.normal_section_header
|
||||||
|
|
||||||
if self.section == 'videocontrol_interface_descriptor' \
|
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
|
section_header = self.larger_section_header
|
||||||
|
|
||||||
@ -689,6 +710,8 @@ class _LsUsb():
|
|||||||
' CDC Union:': 'cdc_union',
|
' CDC Union:': 'cdc_union',
|
||||||
' HID Device Descriptor:': 'hid_device_descriptor',
|
' HID Device Descriptor:': 'hid_device_descriptor',
|
||||||
' Report Descriptors:': 'report_descriptors',
|
' Report Descriptors:': 'report_descriptors',
|
||||||
|
' CDC MBIM:': 'cdc_mbim',
|
||||||
|
' CDC MBIM Extended:': 'cdc_mbim_extended',
|
||||||
'Hub Descriptor:': 'hub_descriptor',
|
'Hub Descriptor:': 'hub_descriptor',
|
||||||
' Hub Port Status:': 'hub_port_status',
|
' Hub Port Status:': 'hub_port_status',
|
||||||
'Device Qualifier (for other device speed):': 'device_qualifier',
|
'Device Qualifier (for other device speed):': 'device_qualifier',
|
||||||
@ -713,6 +736,8 @@ class _LsUsb():
|
|||||||
'cdc_call_management': self.cdc_call_management.list,
|
'cdc_call_management': self.cdc_call_management.list,
|
||||||
'cdc_acm': self.cdc_acm.list,
|
'cdc_acm': self.cdc_acm.list,
|
||||||
'cdc_union': self.cdc_union.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,
|
'hid_device_descriptor': self.hid_device_descriptor.list,
|
||||||
# 'report_descriptors': self.report_descriptors_list, # not implemented
|
# 'report_descriptors': self.report_descriptors_list, # not implemented
|
||||||
'videocontrol_interface_descriptor': self.videocontrol_interface_descriptors.list,
|
'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_call_management'] = {}
|
||||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['cdc_acm'] = {}
|
['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_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]['hid_device_descriptor'] = {}
|
||||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['endpoint_descriptors'] = []
|
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['endpoint_descriptors'] = []
|
||||||
['device_descriptor']['configuration_descriptor']['interface_descriptors'][0]['endpoint_descriptors'][0] = {}
|
['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):
|
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)
|
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):
|
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)
|
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()
|
lsusb = _LsUsb()
|
||||||
|
|
||||||
if jc.utils.has_data(data):
|
if jc.utils.has_data(data):
|
||||||
|
|
||||||
|
# fix known too-long field names
|
||||||
|
data = data.replace('bmNetworkCapabilities', 'bmNetworkCapabilit ')
|
||||||
|
|
||||||
for line in data.splitlines():
|
for line in data.splitlines():
|
||||||
# only -v option or no options are supported
|
# only -v option or no options are supported
|
||||||
if line.startswith('/'):
|
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_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_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_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_device_info = re.compile(r"^'\w+' '.+' 0x\d+")
|
||||||
# scsi_scsi_p = re.compile(r'^Attached devices:\nHost: \w+ ')
|
# scsi_scsi_p = re.compile(r'^Attached devices:\nHost: \w+ ')
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
"""jc - JSON Convert `timedatectl` command output parser
|
"""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
|
The `epoch_utc` calculated timestamp field is timezone-aware and is only
|
||||||
available if the `universal_time` field is available.
|
available if the `universal_time` field is available.
|
||||||
|
|
||||||
@ -29,7 +31,24 @@ Schema:
|
|||||||
"system_clock_synchronized": boolean,
|
"system_clock_synchronized": boolean,
|
||||||
"systemd-timesyncd.service_active": boolean,
|
"systemd-timesyncd.service_active": boolean,
|
||||||
"rtc_in_local_tz": 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:
|
Examples:
|
||||||
@ -64,7 +83,7 @@ import jc.utils
|
|||||||
|
|
||||||
class info():
|
class info():
|
||||||
"""Provides parser metadata (version, author, etc.)"""
|
"""Provides parser metadata (version, author, etc.)"""
|
||||||
version = '1.7'
|
version = '1.8'
|
||||||
description = '`timedatectl status` command parser'
|
description = '`timedatectl status` command parser'
|
||||||
author = 'Kelly Brazil'
|
author = 'Kelly Brazil'
|
||||||
author_email = 'kellyjonbrazil@gmail.com'
|
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',
|
bool_list = {'ntp_enabled', 'ntp_synchronized', 'rtc_in_local_tz', 'dst_active',
|
||||||
'system_clock_synchronized', 'systemd-timesyncd.service_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:
|
for key in proc_data:
|
||||||
if key in bool_list:
|
if key in bool_list:
|
||||||
proc_data[key] = jc.utils.convert_to_bool(proc_data[key])
|
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:
|
if 'universal_time' in proc_data:
|
||||||
ts = jc.utils.timestamp(proc_data['universal_time'], format_hint=(7300,))
|
ts = jc.utils.timestamp(proc_data['universal_time'], format_hint=(7300,))
|
||||||
proc_data['epoch_utc'] = ts.utc
|
proc_data['epoch_utc'] = ts.utc
|
||||||
@ -120,17 +154,27 @@ def parse(data, raw=False, quiet=False):
|
|||||||
jc.utils.input_type_check(data)
|
jc.utils.input_type_check(data)
|
||||||
|
|
||||||
raw_output = {}
|
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):
|
if jc.utils.has_data(data):
|
||||||
|
|
||||||
for line in filter(None, data.splitlines()):
|
for line in filter(None, data.splitlines()):
|
||||||
linedata = line.split(':', maxsplit=1)
|
try:
|
||||||
raw_output[linedata[0].strip().lower().replace(' ', '_')] = linedata[1].strip()
|
key, val = line.split(':', maxsplit=1)
|
||||||
|
key = key.lower().strip()
|
||||||
|
val = val.strip()
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
|
||||||
if linedata[0].strip() == 'DST active':
|
if key in valid_fields:
|
||||||
break
|
keyname = key.replace(' ', '_')
|
||||||
|
raw_output[keyname] = val
|
||||||
|
|
||||||
if raw:
|
return raw_output if raw else _process(raw_output)
|
||||||
return raw_output
|
|
||||||
else:
|
|
||||||
return _process(raw_output)
|
|
||||||
|
@ -138,7 +138,7 @@ from jc.parsers.kv import parse as kv_parse
|
|||||||
|
|
||||||
class info():
|
class info():
|
||||||
"""Provides parser metadata (version, author, etc.)"""
|
"""Provides parser metadata (version, author, etc.)"""
|
||||||
version = '1.0'
|
version = '1.1'
|
||||||
description = '`zpool status` command parser'
|
description = '`zpool status` command parser'
|
||||||
author = 'Kelly Brazil'
|
author = 'Kelly Brazil'
|
||||||
author_email = 'kellyjonbrazil@gmail.com'
|
author_email = 'kellyjonbrazil@gmail.com'
|
||||||
@ -233,7 +233,7 @@ def parse(
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
# preserve indentation in continuation lines
|
# preserve indentation in continuation lines
|
||||||
if line.startswith(' '):
|
if line.startswith(' ') or line.startswith('\t'):
|
||||||
pool_str += line + '\n'
|
pool_str += line + '\n'
|
||||||
continue
|
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
|
.SH NAME
|
||||||
\fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types,
|
\fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types,
|
||||||
and strings
|
and strings
|
||||||
@ -1316,8 +1316,8 @@ etc...
|
|||||||
Note: Unbuffered output can be slower for large data streams.
|
Note: Unbuffered output can be slower for large data streams.
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
.SH CUSTOM PARSERS
|
.SH PARSER PLUGINS
|
||||||
Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
Parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
||||||
local "App data directory":
|
local "App data directory":
|
||||||
|
|
||||||
.RS
|
.RS
|
||||||
@ -1328,11 +1328,13 @@ local "App data directory":
|
|||||||
.fi
|
.fi
|
||||||
.RE
|
.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
|
\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.
|
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
|
start with a letter and consist entirely of alphanumerics and underscores. Local
|
||||||
plugins may override default parsers.
|
plugins may override default parsers.
|
||||||
|
|
||||||
|
3
setup.py
3
setup.py
@ -5,7 +5,7 @@ with open('README.md', 'r') as f:
|
|||||||
|
|
||||||
setuptools.setup(
|
setuptools.setup(
|
||||||
name='jc',
|
name='jc',
|
||||||
version='1.23.0',
|
version='1.23.1',
|
||||||
author='Kelly Brazil',
|
author='Kelly Brazil',
|
||||||
author_email='kellyjonbrazil@gmail.com',
|
author_email='kellyjonbrazil@gmail.com',
|
||||||
description='Converts the output of popular command-line tools and file-types to JSON.',
|
description='Converts the output of popular command-line tools and file-types to JSON.',
|
||||||
@ -20,6 +20,7 @@ setuptools.setup(
|
|||||||
python_requires='>=3.6',
|
python_requires='>=3.6',
|
||||||
url='https://github.com/kellyjonbrazil/jc',
|
url='https://github.com/kellyjonbrazil/jc',
|
||||||
packages=setuptools.find_packages(exclude=['*.tests', '*.tests.*', 'tests.*', 'tests']),
|
packages=setuptools.find_packages(exclude=['*.tests', '*.tests.*', 'tests.*', 'tests']),
|
||||||
|
package_data={'jc': ['py.typed']},
|
||||||
entry_points={
|
entry_points={
|
||||||
'console_scripts': [
|
'console_scripts': [
|
||||||
'jc=jc.cli:main'
|
'jc=jc.cli:main'
|
||||||
|
@ -366,8 +366,8 @@ etc...
|
|||||||
Note: Unbuffered output can be slower for large data streams.
|
Note: Unbuffered output can be slower for large data streams.
|
||||||
.RE
|
.RE
|
||||||
|
|
||||||
.SH CUSTOM PARSERS
|
.SH PARSER PLUGINS
|
||||||
Custom local parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
Parser plugins may be placed in a \fBjc/jcparsers\fP folder in your
|
||||||
local "App data directory":
|
local "App data directory":
|
||||||
|
|
||||||
.RS
|
.RS
|
||||||
@ -378,11 +378,13 @@ local "App data directory":
|
|||||||
.fi
|
.fi
|
||||||
.RE
|
.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
|
\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.
|
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
|
start with a letter and consist entirely of alphanumerics and underscores. Local
|
||||||
plugins may override default parsers.
|
plugins may override default parsers.
|
||||||
|
|
||||||
|
@ -393,20 +393,22 @@ for item in result:
|
|||||||
print(item["filename"])
|
print(item["filename"])
|
||||||
```
|
```
|
||||||
|
|
||||||
### Custom Parsers
|
### Parser Plugins
|
||||||
Custom local parser plugins may be placed in a `jc/jcparsers` folder in your
|
Parser plugins may be placed in a `jc/jcparsers` folder in your local
|
||||||
local **"App data directory"**:
|
**"App data directory"**:
|
||||||
|
|
||||||
- Linux/unix: `$HOME/.local/share/jc/jcparsers`
|
- Linux/unix: `$HOME/.local/share/jc/jcparsers`
|
||||||
- macOS: `$HOME/Library/Application Support/jc/jcparsers`
|
- macOS: `$HOME/Library/Application Support/jc/jcparsers`
|
||||||
- Windows: `$LOCALAPPDATA\jc\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)
|
[`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)
|
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.
|
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.
|
start with a letter and consist entirely of alphanumerics and underscores.
|
||||||
Local plugins may override default parsers.
|
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`.
|
including compatibility, run `jc -ap`.
|
||||||
|
|
||||||
You may still use a parser on an unsupported platform - for example, you may
|
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
|
that case you can suppress the warning message with the `-q` cli option or the
|
||||||
`quiet=True` function parameter in `parse()`:
|
`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:
|
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()
|
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
|
# output
|
||||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/lsusb.json'), 'r', encoding='utf-8') as f:
|
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())
|
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:
|
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())
|
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):
|
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)
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.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:
|
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()
|
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
|
# output
|
||||||
with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/centos-7.7/timedatectl.json'), 'r', encoding='utf-8') as f:
|
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())
|
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:
|
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())
|
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):
|
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)
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
@ -22,7 +22,10 @@ class MyTests(unittest.TestCase):
|
|||||||
'fixtures/generic/zpool-status-v2.json'),
|
'fixtures/generic/zpool-status-v2.json'),
|
||||||
'zpool_status3': (
|
'zpool_status3': (
|
||||||
'fixtures/generic/zpool-status-v3.out',
|
'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():
|
for file, filepaths in fixtures.items():
|
||||||
@ -66,6 +69,15 @@ class MyTests(unittest.TestCase):
|
|||||||
self.f_json['zpool_status3']
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Reference in New Issue
Block a user