diff --git a/jc/parsers/vmstat_s.py b/jc/parsers/vmstat_s.py index 7c97e192..b12d1499 100644 --- a/jc/parsers/vmstat_s.py +++ b/jc/parsers/vmstat_s.py @@ -1,5 +1,7 @@ """jc - JSON CLI output utility `vmstat` command output streaming parser +Options supported: `-a`, `-w`, `-d`, `-t` + Usage (cli): $ vmstat | jc --vmstat-s @@ -14,23 +16,50 @@ Usage (module): Schema: { - "vmstat": string, - "_meta": # This object only exists if using -q or quiet=True - { - "success": booean, # true if successfully parsed, false if error - "error": string, # exists if "success" is false - "line": string # exists if "success" is false - } + 'runnable_procs': integer, + 'uninterruptible_sleeping_procs': integer, + 'virtual_mem_used': integer, + 'free_mem': integer, + 'buffer_mem': integer, + 'cache_mem': integer, + 'inactive_mem': integer, + 'active_mem': integer, + 'swap_in': integer, + 'swap_out': integer, + 'blocks_in': integer, + 'blocks_out': integer, + 'interrupts': integer, + 'context_switches': integer, + 'user_time': integer, + 'system_time': integer, + 'idle_time': integer, + 'io_wait_time': integer, + 'stolen_time': integer, + 'disk': string, + 'total_reads': integer, + 'merged_reads': integer, + 'sectors_read': integer, + 'reading_ms': integer, + 'total_writes': integer, + 'merged_writes': integer, + 'sectors_written': integer, + 'writing_ms': integer, + 'current_io': integer, + 'io_seconds': integer, + 'timestamp': string, + 'timezone': string, + 'epoch': integer, # naive timestamp if -t flag is used + 'epoch_utc': integer # aware timestamp if -t flag is used and UTC TZ } Examples: $ vmstat | jc --vmstat-s - {example output} + {"runnable_procs":2,"uninterruptible_sleeping_procs":0,"virtual_mem_used":0,"free_mem":2794468,"buffer_mem":2108,"cache_mem":741208,"inactive_mem":null,"active_mem":null,"swap_in":0,"swap_out":0,"blocks_in":1,"blocks_out":3,"interrupts":29,"context_switches":57,"user_time":0,"system_time":0,"idle_time":99,"io_wait_time":0,"stolen_time":0,"timestamp":null,"timezone":null} ... $ vmstat | jc --vmstat-s -r - {example output} + {"runnable_procs":"2","uninterruptible_sleeping_procs":"0","virtual_mem_used":"0","free_mem":"2794468","buffer_mem":"2108","cache_mem":"741208","inactive_mem":null,"active_mem":null,"swap_in":"0","swap_out":"0","blocks_in":"1","blocks_out":"3","interrupts":"29","context_switches":"57","user_time":"0","system_time":"0","idle_time":"99","io_wait_time":"0","stolen_time":"0","timestamp":null,"timezone":null} ... """ import jc.utils @@ -39,13 +68,13 @@ from jc.utils import stream_success, stream_error class info(): """Provides parser metadata (version, author, etc.)""" - version = '1.0' + version = '0.5' description = '`vmstat` command streaming parser' - author = 'John Doe' - author_email = 'johndoe@gmail.com' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' # compatible options: linux, darwin, cygwin, win32, aix, freebsd - compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + compatible = ['linux'] streaming = True @@ -64,12 +93,21 @@ def _process(proc_data): Dictionary. Structured data to conform to the schema. """ - # - # process the data here - # rebuild output for added semantic information - # use helper functions in jc.utils for int, float, bool conversions and timestamps - # + int_list = ['runnable_procs', 'uninterruptible_sleeping_procs', 'virtual_mem_used', 'free_mem', 'buffer_mem', + 'cache_mem', 'inactive_mem', 'active_mem', 'swap_in', 'swap_out', 'blocks_in', 'blocks_out', + 'interrupts', 'context_switches', 'user_time', 'system_time', 'idle_time', 'io_wait_time', + 'stolen_time', 'total_reads', 'merged_reads', 'sectors_read', 'reading_ms', 'total_writes', + 'merged_writes', 'sectors_written', 'writing_ms', 'current_io', 'io_seconds'] + for key in proc_data: + if key in int_list: + proc_data[key] = jc.utils.convert_to_int(proc_data[key]) + + if proc_data['timestamp']: + ts = jc.utils.timestamp(f'{proc_data["timestamp"]} {proc_data["timezone"]}') + proc_data['epoch'] = ts.naive + proc_data['epoch_utc'] = ts.utc + return proc_data @@ -94,11 +132,92 @@ def parse(data, raw=False, quiet=False): if not quiet: jc.utils.compatibility(__name__, info.compatible) + output_line = {} + procs = None + buff_cache = None + disk = None + tstamp = None + tz = None + for line in data: try: - # - # parse the input here - # + # skip blank lines + if line.strip() == '': + continue + + # detect output type + if not procs and not disk and line.startswith('procs'): + procs = True + tstamp = True if '-timestamp-' in line else False + continue + + if not procs and not disk and line.startswith('disk'): + disk = True + tstamp = True if '-timestamp-' in line else False + continue + + + # skip header rows + if 'swpd' in line and 'free' in line and 'buff' in line and 'cache' in line: + buff_cache = True + tz = line.strip().split()[-1] if tstamp else None + continue + + elif 'swpd' in line and 'free' in line and 'inact' in line and 'active' in line: + buff_cache = False + tz = line.strip().split()[-1] if tstamp else None + continue + + if 'total' in line and 'merged' in line and 'sectors' in line: + tz = line.strip().split()[-1] if tstamp else None + continue + + # line parsing + if procs: + line_list = line.strip().split(maxsplit=17) + + output_line = { + 'runnable_procs': line_list[0], + 'uninterruptible_sleeping_procs': line_list[1], + 'virtual_mem_used': line_list[2], + 'free_mem': line_list[3], + 'buffer_mem': line_list[4] if buff_cache else None, + 'cache_mem': line_list[5] if buff_cache else None, + 'inactive_mem': line_list[4] if not buff_cache else None, + 'active_mem': line_list[5] if not buff_cache else None, + 'swap_in': line_list[6], + 'swap_out': line_list[7], + 'blocks_in': line_list[8], + 'blocks_out': line_list[9], + 'interrupts': line_list[10], + 'context_switches': line_list[11], + 'user_time': line_list[12], + 'system_time': line_list[13], + 'idle_time': line_list[14], + 'io_wait_time': line_list[15], + 'stolen_time': line_list[16], + 'timestamp': line_list[17] if tstamp else None, + 'timezone': tz or None + } + + if disk: + line_list = line.strip().split(maxsplit=11) + + output_line = { + 'disk': line_list[0], + 'total_reads': line_list[1], + 'merged_reads': line_list[2], + 'sectors_read': line_list[3], + 'reading_ms': line_list[4], + 'total_writes': line_list[5], + 'merged_writes': line_list[6], + 'sectors_written': line_list[7], + 'writing_ms': line_list[8], + 'current_io': line_list[9], + 'io_seconds': line_list[10], + 'timestamp': line_list[11] if tstamp else None, + 'timezone': tz or None + } yield stream_success(output_line, quiet) if raw else stream_success(_process(output_line), quiet)