diff --git a/docs/parsers/proc_net_igmp.md b/docs/parsers/proc_net_igmp.md new file mode 100644 index 00000000..9672f7ab --- /dev/null +++ b/docs/parsers/proc_net_igmp.md @@ -0,0 +1,152 @@ +[Home](https://kellyjonbrazil.github.io/jc/) + + +# jc.parsers.proc\_net\_igmp + +jc - JSON Convert `/proc/net/igmp` file parser + +Usage (cli): + + $ cat /proc/net/igmp | jc --proc + +or + + $ jc /proc/net/igmp + +or + + $ cat /proc/net/igmp | jc --proc-net-igmp + +Usage (module): + + import jc + result = jc.parse('proc', proc_net_igmp_file) + +or + + import jc + result = jc.parse('proc_net_igmp', proc_net_igmp_file) + +Schema: + + [ + { + "index": integer, + "device": string, + "count": integer, + "querier": string, + "groups": [ + { + "address": string, + "users": integer, + "timer": string, + "reporter": integer + } + ] + } + ] + +Examples: + + $ cat /proc/net/igmp | jc --proc -p + [ + { + "index": 0, + "device": "lo", + "count": 0, + "querier": "V3", + "groups": [ + { + "address": "010000E0", + "users": 1, + "timer": "0:00000000", + "reporter": 0 + } + ] + }, + { + "index": 2, + "device": "eth0", + "count": 26, + "querier": "V2", + "groups": [ + { + "address": "260301E0", + "users": 1, + "timer": "0:00000000", + "reporter": 1 + }, + { + "address": "9B0101E0", + "users": 1, + "timer": "0:00000000", + "reporter": 1 + }, + ] + } + ... + ] + + $ cat /proc/net/igmp | jc --proc -p -r + [ + { + "index": "0", + "device": "lo", + "count": "0", + "querier": "V3", + "groups": [ + { + "address": "010000E0", + "users": "1", + "timer": "0:00000000", + "reporter": "0" + } + ] + }, + { + "index": "2", + "device": "eth0", + "count": "26", + "querier": "V2", + "groups": [ + { + "address": "260301E0", + "users": "1", + "timer": "0:00000000", + "reporter": "1" + }, + { + "address": "9B0101E0", + "users": "1", + "timer": "0:00000000", + "reporter": "1" + }, + ] + } + ... + } + + + +### parse + +```python +def parse(data: str, raw: bool = False, quiet: bool = False) -> List[Dict] +``` + +Main text parsing function + +Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + +Returns: + + List of Dictionaries. Raw or processed structured data. + +### Parser Information +Compatibility: linux + +Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com) diff --git a/jc/lib.py b/jc/lib.py index b60efebd..454f5811 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -125,6 +125,7 @@ parsers = [ 'proc-net-dev', 'proc-net-dev-mcast', 'proc-net-if-inet6', + 'proc-net-igmp', 'ps', 'route', 'rpm-qi', diff --git a/jc/parsers/proc_net_igmp.py b/jc/parsers/proc_net_igmp.py new file mode 100644 index 00000000..b230abfd --- /dev/null +++ b/jc/parsers/proc_net_igmp.py @@ -0,0 +1,231 @@ +"""jc - JSON Convert `/proc/net/igmp` file parser + +Usage (cli): + + $ cat /proc/net/igmp | jc --proc + +or + + $ jc /proc/net/igmp + +or + + $ cat /proc/net/igmp | jc --proc-net-igmp + +Usage (module): + + import jc + result = jc.parse('proc', proc_net_igmp_file) + +or + + import jc + result = jc.parse('proc_net_igmp', proc_net_igmp_file) + +Schema: + + [ + { + "index": integer, + "device": string, + "count": integer, + "querier": string, + "groups": [ + { + "address": string, + "users": integer, + "timer": string, + "reporter": integer + } + ] + } + ] + +Examples: + + $ cat /proc/net/igmp | jc --proc -p + [ + { + "index": 0, + "device": "lo", + "count": 0, + "querier": "V3", + "groups": [ + { + "address": "010000E0", + "users": 1, + "timer": "0:00000000", + "reporter": 0 + } + ] + }, + { + "index": 2, + "device": "eth0", + "count": 26, + "querier": "V2", + "groups": [ + { + "address": "260301E0", + "users": 1, + "timer": "0:00000000", + "reporter": 1 + }, + { + "address": "9B0101E0", + "users": 1, + "timer": "0:00000000", + "reporter": 1 + }, + ] + } + ... + ] + + $ cat /proc/net/igmp | jc --proc -p -r + [ + { + "index": "0", + "device": "lo", + "count": "0", + "querier": "V3", + "groups": [ + { + "address": "010000E0", + "users": "1", + "timer": "0:00000000", + "reporter": "0" + } + ] + }, + { + "index": "2", + "device": "eth0", + "count": "26", + "querier": "V2", + "groups": [ + { + "address": "260301E0", + "users": "1", + "timer": "0:00000000", + "reporter": "1" + }, + { + "address": "9B0101E0", + "users": "1", + "timer": "0:00000000", + "reporter": "1" + }, + ] + } + ... + } +""" +from typing import List, Dict +import jc.utils + + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1.0' + description = '`/proc/net/igmp` file parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + compatible = ['linux'] + hidden = True + + +__version__ = info.version + + +def _process(proc_data: List[Dict]) -> List[Dict]: + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + + Returns: + + List of Dictionaries. Structured to conform to the schema. + """ + int_list = {'index', 'count', 'users', 'reporter'} + + for item in proc_data: + for key, val in item.items(): + if key in int_list: + item[key] = int(val) + + if 'groups' in item: + for group in item['groups']: + for key, val in group.items(): + if key in int_list: + group[key] = int(val) + + return proc_data + + +def parse( + data: str, + raw: bool = False, + quiet: bool = False +) -> List[Dict]: + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of Dictionaries. Raw or processed structured data. + """ + jc.utils.compatibility(__name__, info.compatible, quiet) + jc.utils.input_type_check(data) + + raw_output: List = [] + output_line: Dict = {} + groups: List = [] + group: Dict = {} + + if jc.utils.has_data(data): + + for line in filter(None, data.splitlines()[1:]): + if not line.startswith('\t'): + if output_line: + if groups: + output_line['groups'] = groups + raw_output.append(output_line) + output_line = {} + groups = [] + group = {} + + index, device, _, count, querier = line.split() + output_line = { + 'index': index, + 'device': device, + 'count': count, + 'querier': querier + } + continue + + address, users, timer, reporter = line.split() + group = { + 'address': address, + 'users': users, + 'timer': timer, + 'reporter': reporter + } + groups.append(group) + continue + + if output_line: + if groups: + output_line['groups'] = groups + raw_output.append(output_line) + + return raw_output if raw else _process(raw_output) diff --git a/tests/fixtures/linux-proc/net_igmp.json b/tests/fixtures/linux-proc/net_igmp.json new file mode 100644 index 00000000..50647e4c --- /dev/null +++ b/tests/fixtures/linux-proc/net_igmp.json @@ -0,0 +1 @@ +[{"index":1,"device":"lo","count":1,"querier":"V3","groups":[{"address":"010000E0","users":1,"timer":"0:00000000","reporter":0}]},{"index":2,"device":"ens33","count":1,"querier":"V3","groups":[{"address":"010000E0","users":1,"timer":"0:00000000","reporter":0}]}] diff --git a/tests/fixtures/linux-proc/net_igmp_more b/tests/fixtures/linux-proc/net_igmp_more new file mode 100644 index 00000000..11d8a94d --- /dev/null +++ b/tests/fixtures/linux-proc/net_igmp_more @@ -0,0 +1,30 @@ +Idx Device : Count Querier Group Users Timer Reporter +0 lo : 0 V3 + 010000E0 1 0:00000000 0 +2 eth0 : 26 V2 + 260301E0 1 0:00000000 1 + 9B0101E0 1 0:00000000 1 + 439501E0 1 0:00000000 1 + 990101E0 1 0:00000000 1 + 580101E0 1 0:00000000 1 + 2A0301E0 1 0:00000000 1 + 290301E0 1 0:00000000 1 + 280301E0 1 0:00000000 1 + 9BD901E0 1 0:00000000 1 + 2D0301E0 1 0:00000000 1 + 2C0301E0 1 0:00000000 1 + 370301E0 1 0:00000000 1 + 050101E0 1 0:00000000 1 + 620201E0 1 0:00000000 1 + 040201E0 1 0:00000000 1 + 5F0101E0 1 0:00000000 1 + 520101E0 1 0:00000000 1 + 2BEF01E0 1 0:00000000 1 + 4D0101E0 1 0:00000000 1 + E00C01E0 1 0:00000000 1 + 400301E0 1 0:00000000 1 + FB0000E0 1 0:00000000 1 + 010000E0 1 0:00000000 0 +3 eth1 : 5 V2 + FB0000E0 1 0:00000000 1 + 010000E0 1 0:00000000 0 diff --git a/tests/fixtures/linux-proc/net_igmp_more.json b/tests/fixtures/linux-proc/net_igmp_more.json new file mode 100644 index 00000000..e7f9ec97 --- /dev/null +++ b/tests/fixtures/linux-proc/net_igmp_more.json @@ -0,0 +1 @@ +[{"index":0,"device":"lo","count":0,"querier":"V3","groups":[{"address":"010000E0","users":1,"timer":"0:00000000","reporter":0}]},{"index":2,"device":"eth0","count":26,"querier":"V2","groups":[{"address":"260301E0","users":1,"timer":"0:00000000","reporter":1},{"address":"9B0101E0","users":1,"timer":"0:00000000","reporter":1},{"address":"439501E0","users":1,"timer":"0:00000000","reporter":1},{"address":"990101E0","users":1,"timer":"0:00000000","reporter":1},{"address":"580101E0","users":1,"timer":"0:00000000","reporter":1},{"address":"2A0301E0","users":1,"timer":"0:00000000","reporter":1},{"address":"290301E0","users":1,"timer":"0:00000000","reporter":1},{"address":"280301E0","users":1,"timer":"0:00000000","reporter":1},{"address":"9BD901E0","users":1,"timer":"0:00000000","reporter":1},{"address":"2D0301E0","users":1,"timer":"0:00000000","reporter":1},{"address":"2C0301E0","users":1,"timer":"0:00000000","reporter":1},{"address":"370301E0","users":1,"timer":"0:00000000","reporter":1},{"address":"050101E0","users":1,"timer":"0:00000000","reporter":1},{"address":"620201E0","users":1,"timer":"0:00000000","reporter":1},{"address":"040201E0","users":1,"timer":"0:00000000","reporter":1},{"address":"5F0101E0","users":1,"timer":"0:00000000","reporter":1},{"address":"520101E0","users":1,"timer":"0:00000000","reporter":1},{"address":"2BEF01E0","users":1,"timer":"0:00000000","reporter":1},{"address":"4D0101E0","users":1,"timer":"0:00000000","reporter":1},{"address":"E00C01E0","users":1,"timer":"0:00000000","reporter":1},{"address":"400301E0","users":1,"timer":"0:00000000","reporter":1},{"address":"FB0000E0","users":1,"timer":"0:00000000","reporter":1},{"address":"010000E0","users":1,"timer":"0:00000000","reporter":0}]},{"index":3,"device":"eth1","count":5,"querier":"V2","groups":[{"address":"FB0000E0","users":1,"timer":"0:00000000","reporter":1},{"address":"010000E0","users":1,"timer":"0:00000000","reporter":0}]}] diff --git a/tests/test_proc_net_igmp.py b/tests/test_proc_net_igmp.py new file mode 100644 index 00000000..bcff2514 --- /dev/null +++ b/tests/test_proc_net_igmp.py @@ -0,0 +1,54 @@ +import os +import unittest +import json +from typing import Dict +import jc.parsers.proc_net_igmp + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class MyTests(unittest.TestCase): + f_in: Dict = {} + f_json: Dict = {} + + @classmethod + def setUpClass(cls): + fixtures = { + 'proc_net_igmp': ( + 'fixtures/linux-proc/net_igmp', + 'fixtures/linux-proc/net_igmp.json'), + 'proc_net_igmp_more': ( + 'fixtures/linux-proc/net_igmp_more', + 'fixtures/linux-proc/net_igmp_more.json') + } + + for file, filepaths in fixtures.items(): + with open(os.path.join(THIS_DIR, filepaths[0]), 'r', encoding='utf-8') as a, \ + open(os.path.join(THIS_DIR, filepaths[1]), 'r', encoding='utf-8') as b: + cls.f_in[file] = a.read() + cls.f_json[file] = json.loads(b.read()) + + + def test_proc_net_igmp_nodata(self): + """ + Test 'proc_net_igmp' with no data + """ + self.assertEqual(jc.parsers.proc_net_igmp.parse('', quiet=True), []) + + def test_proc_net_igmp(self): + """ + Test '/proc/net/igmp' + """ + self.assertEqual(jc.parsers.proc_net_igmp.parse(self.f_in['proc_net_igmp'], quiet=True), + self.f_json['proc_net_igmp']) + + def test_proc_net_igmp_more(self): + """ + Test '/proc/net/igmp' additional file + """ + self.assertEqual(jc.parsers.proc_net_igmp.parse(self.f_in['proc_net_igmp_more'], quiet=True), + self.f_json['proc_net_igmp_more']) + + +if __name__ == '__main__': + unittest.main()