1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-07-13 01:20:24 +02:00

Merge pull request #381 from kellyjonbrazil/dev

Dev v1.23.1
This commit is contained in:
Kelly Brazil
2023-03-23 20:16:11 -04:00
committed by GitHub
27 changed files with 595 additions and 53 deletions

View File

@ -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

View File

@ -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()`:

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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
)

View File

@ -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 = {

View File

@ -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:

View File

@ -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('/'):

View File

@ -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+ ')

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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'

View File

@ -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.

View File

@ -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()`:

File diff suppressed because one or more lines are too long

View 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)

View 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}

View 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'.

View 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"}

View 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

View 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"}]

View 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

View File

@ -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()

View File

@ -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()

View File

@ -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()