diff --git a/docs/shared_parsers_catalog/proc_keys.rst b/docs/shared_parsers_catalog/proc_keys.rst new file mode 100644 index 0000000000..570dc0733d --- /dev/null +++ b/docs/shared_parsers_catalog/proc_keys.rst @@ -0,0 +1,3 @@ +.. automodule:: insights.parsers.proc_keys + :members: + :show-inheritance: diff --git a/insights/parsers/proc_keys.py b/insights/parsers/proc_keys.py new file mode 100644 index 0000000000..1302089e91 --- /dev/null +++ b/insights/parsers/proc_keys.py @@ -0,0 +1,111 @@ +""" +ProcKeys - File ``/proc/keys`` +============================== + +This parser reads the content of ``/proc/keys``. + +""" + +from insights import Parser, parser +from insights.parsers import SkipException, keyword_search +from insights.specs import Specs + + +@parser(Specs.proc_keys) +class ProcKeys(Parser, list): + """ + Class ``ProcKeys`` parses the content of the ``/proc/keys`` file. + + This file exposes a list of the keys for which the reading thread has view + permission, providing various information about each key. The fields shown + in each line of this file contains below attributes:: + + ID (string): The ID of the key, expressed in hexadecimal. + Flags (string): A set of flags describing the state of the key + Usage (string): The count of the number of kernel credential structures + that are pinning the key. + Timeout (string): The amount of time until the key will expire (weeks, days, + hours, minutes, and seconds). The string perm here means + that the key is permanent (no timeout). The string expd + means that the key has already expired, but has not yet + been garbage collected. + Permissions (string): The key permissions, expressed as four hexadecimal bytes + containing, from left to right, the possessor, user, group, + and other permissions. + UID (string): The user ID of the key owner. + GID (string): The group ID of the key. + Type (string) : The key type. + Description (string) The key description (name). For most key types, it has the + form name[: extra-info]. (The name subfield is the key's + description (name). The optional extra-info field provides + some further information about the key.) + + + Sample output:: + + 009a2028 I--Q--- 1 perm 3f010000 1000 1000 user krb_ccache:primary: 12 + 1806c4ba I--Q--- 1 perm 3f010000 1000 1000 keyring _pid: 2 + 25d3a08f I--Q--- 1 perm 1f3f0000 1000 65534 keyring _uid_ses.1000: 1 + 28576bd8 I--Q--- 3 perm 3f010000 1000 1000 keyring _krb: 1 + 2c546d21 I--Q--- 190 perm 3f030000 1000 1000 keyring _ses: 2 + 30a4e0be I------ 4 2d 1f030000 1000 65534 keyring _persistent.1000: 1 + 32100fab I--Q--- 4 perm 1f3f0000 1000 65534 keyring _uid.1000: 2 + 32a387ea I--Q--- 1 perm 3f010000 1000 1000 keyring _pid: 2 + 3ce56aea I--Q--- 5 perm 3f030000 1000 1000 keyring _ses: 1 + + + Examples: + >>> type(proc_keys) + + >>> proc_keys[0]['id'] + '009a2028' + >>> proc_keys[0]['flags'] + 'I--Q---' + >>> proc_keys[0]['usage'] + '1' + >>> proc_keys[0]['timeout'] + 'perm' + >>> proc_keys[0]['permissions'] + '3f010000' + >>> proc_keys[0]['uid'] + '1000' + >>> proc_keys[0]['gid'] + '1000' + >>> proc_keys[0]['type'] + 'user' + >>> proc_keys[0]['description'] + 'krb_ccache:primary: 12' + """ + + def parse_content(self, content): + + if not content: + raise SkipException("No Contents") + + column = ['id', 'flags', 'usage', 'timeout', 'permissions', 'uid', 'gid', 'type', 'description'] + + for line in content: + row = line.split(None, 8) + if row and len(column) == len(row): + self.append(dict(zip(column, row))) + else: + raise SkipException("Invalid Contents: {0}".format(line)) + + def search(self, **kwargs): + """ + Get the sublist containing the keywords by searching the ``/proc/keys`` list. + + This uses the :py:func:`insights.parsers.keyword_search` function for searching, + see its documentation for usage details. If no search parameters are given or does + match the search, then nothing will be returned. + + Returns: + list: A list of dictionaries of the ``/proc/keys`` content that match the given search criteria. + + Examples: + >>> proc_keys.search(timeout='perm')[0] == proc_keys[0] + True + >>> proc_keys.search(description__contains='uid')[0] == proc_keys[2] + True + """ + return keyword_search(self, **kwargs) diff --git a/insights/specs/__init__.py b/insights/specs/__init__.py index b21deb6037..46880f6d28 100644 --- a/insights/specs/__init__.py +++ b/insights/specs/__init__.py @@ -523,6 +523,7 @@ class Specs(SpecSet): postgresql_conf = RegistryPoint() postgresql_log = RegistryPoint(multi_output=True, filterable=True) prev_uploader_log = RegistryPoint() + proc_keys = RegistryPoint() proc_netstat = RegistryPoint() proc_slabinfo = RegistryPoint() proc_snmp_ipv4 = RegistryPoint() diff --git a/insights/specs/default.py b/insights/specs/default.py index f9c88e585d..918c447706 100644 --- a/insights/specs/default.py +++ b/insights/specs/default.py @@ -106,6 +106,7 @@ class DefaultSpecs(Specs): candlepin_broker = candlepin_broker.candlepin_broker candlepin_log = simple_file("/var/log/candlepin/candlepin.log") cgroups = simple_file("/proc/cgroups") + proc_keys = simple_file("/proc/keys") ps_alxwww = simple_command("/bin/ps alxwww") ps_aux = simple_command("/bin/ps aux") ps_auxcww = simple_command("/bin/ps auxcww") diff --git a/insights/tests/parsers/test_proc_keys.py b/insights/tests/parsers/test_proc_keys.py new file mode 100644 index 0000000000..6ce2c7ce41 --- /dev/null +++ b/insights/tests/parsers/test_proc_keys.py @@ -0,0 +1,58 @@ +from insights.parsers.proc_keys import ProcKeys +from insights.parsers import proc_keys, SkipException +from insights.tests import context_wrap +import doctest +import pytest + +PROC_KEYS = """ +009a2028 I--Q--- 1 perm 3f010000 1000 1000 user krb_ccache:primary: 12 +1806c4ba I--Q--- 1 perm 3f010000 1000 1000 keyring _pid: 2 +25d3a08f I--Q--- 1 perm 1f3f0000 1000 65534 keyring _uid_ses.1000: 1 +28576bd8 I--Q--- 3 perm 3f010000 1000 1000 keyring _krb: 1 +2c546d21 I--Q--- 190 perm 3f030000 1000 1000 keyring _ses: 2 +30a4e0be I------ 4 2d 1f030000 1000 65534 keyring _persistent.1000: 1 +32100fab I--Q--- 4 perm 1f3f0000 1000 65534 keyring _uid.1000: 2 +32a387ea I--Q--- 1 perm 3f010000 1000 1000 keyring _pid: 2 +3ce56aea I--Q--- 5 perm 3f030000 1000 1000 keyring _ses: 1 +""".strip() + +PROC_KEYS_INVALID = """ +unknow_case +""".strip() + +PROC_KEYS_EMPTY = """ +""".strip() + + +def test_etc_systemd(): + proc_keys_content = ProcKeys(context_wrap(PROC_KEYS)) + assert proc_keys_content[1]['id'] == '1806c4ba' + assert proc_keys_content[1]['flags'] == 'I--Q---' + assert proc_keys_content[1]['usage'] == '1' + assert proc_keys_content[1]['timeout'] == 'perm' + assert proc_keys_content[2]['permissions'] == '1f3f0000' + assert proc_keys_content[2]['uid'] == '1000' + assert proc_keys_content[2]['gid'] == '65534' + assert proc_keys_content[2]['type'] == 'keyring' + assert proc_keys_content[2]['description'] == '_uid_ses.1000: 1' + + assert proc_keys_content.search(timeout='perm')[0] == proc_keys_content[0] + assert proc_keys_content.search(description__contains='pid')[0] == proc_keys_content[1] + + +def test_empty(): + with pytest.raises(SkipException) as e: + ProcKeys(context_wrap(PROC_KEYS_EMPTY)) + assert 'No Contents' in str(e) + + with pytest.raises(SkipException) as e: + ProcKeys(context_wrap(PROC_KEYS_INVALID)) + assert "Invalid Contents: unknow_case" in str(e) + + +def test_systemd_examples(): + env = { + 'proc_keys': ProcKeys(context_wrap(PROC_KEYS)) + } + failed, total = doctest.testmod(proc_keys, globs=env) + assert failed == 0