diff --git a/docs/shared_parsers_catalog/ansible.rst b/docs/shared_parsers_catalog/ansible.rst new file mode 100644 index 000000000..d5ec8b2d1 --- /dev/null +++ b/docs/shared_parsers_catalog/ansible.rst @@ -0,0 +1,3 @@ +.. automodule:: insights.parsers.ansible + :members: + :show-inheritance: diff --git a/insights/parsers/ansible.py b/insights/parsers/ansible.py new file mode 100644 index 000000000..d0e9892d5 --- /dev/null +++ b/insights/parsers/ansible.py @@ -0,0 +1,55 @@ +""" +Ansible - Parsers relates to Ansible +==================================== + +Below Parser is included in this module + +AnsibleTelemetry - command "/usr/share/ansible/telemetry/telemetry.py" +---------------------------------------------------------------------- +""" + +import json + +from insights import CommandParser, parser +from insights.core.exceptions import SkipComponent, ParseException +from insights.specs import Specs + + +@parser(Specs.ansible_telemetry) +class AnsibleTelemetry(CommandParser, list): + """ + Parse the output of command "/usr/share/ansible/telemetry/telemetry.py". + + Sample output of the command is in NDJSON format:: + + {"collections":{"ansible.builtin":{"resources":{"action":{"ansible.builtin.command":13}},"version":"*"}},"ansible_core":{"version":"2.18.9rc1"}} + {"collections":{"ansible.builtin":{"resources":{"action":{"ansible.builtin.command":14}},"version":"*"}},"ansible_core":{"version":"2.19.9"}} + + Raises: + SkipComponent: when nothing is parsed. + ParseException: when any line is not parsable for JSON. + + Examples: + >>> type(ansible_telemetry) + + >>> ansible_telemetry[0]['collections']['ansible.builtin']['version'] == '*' + True + >>> ansible_telemetry[1]['ansible_core']['version'] == '2.19.9' + True + """ + + def parse_content(self, content): + if not content: + raise SkipComponent("Empty output.") + + for line in content: + line = line.strip() + if line: + try: + line_json = json.loads(line) + except Exception: + raise ParseException("Invalid line: {0}".format(line)) + self.append(line_json) if line_json else None + + if len(self) == 0: # pragma: no cover + raise SkipComponent("Nothing parsable.") diff --git a/insights/specs/__init__.py b/insights/specs/__init__.py index 0255c9eaf..0b9e2d68a 100644 --- a/insights/specs/__init__.py +++ b/insights/specs/__init__.py @@ -20,6 +20,7 @@ class Specs(SpecSet): malware_detection = RegistryPoint() # Regular collection specs + ansible_telemetry = RegistryPoint() abrt_ccpp_conf = RegistryPoint( filterable=True, no_obfuscate=['hostname', 'ipv4', 'ipv6', 'mac'] ) @@ -439,12 +440,8 @@ class Specs(SpecSet): lsblk_pairs = RegistryPoint() lscpu = RegistryPoint(no_obfuscate=['hostname', 'ipv4', 'ipv6', 'mac']) lsinitrd = RegistryPoint(filterable=True, no_obfuscate=['ipv4', 'ipv6', 'mac']) - lsinitrd_kdump_image = RegistryPoint( - filterable=True, no_obfuscate=['ipv4', 'ipv6', 'mac'] - ) - lsinitrd_lvm_conf = RegistryPoint( - filterable=True, no_obfuscate=['ipv4', 'ipv6', 'mac'] - ) + lsinitrd_kdump_image = RegistryPoint(filterable=True, no_obfuscate=['ipv4', 'ipv6', 'mac']) + lsinitrd_lvm_conf = RegistryPoint(filterable=True, no_obfuscate=['ipv4', 'ipv6', 'mac']) lsmod = RegistryPoint(no_obfuscate=['hostname', 'ipv4', 'ipv6', 'mac']) lsof = RegistryPoint(filterable=True) lspci = RegistryPoint(no_obfuscate=['hostname', 'ipv4', 'ipv6', 'mac']) @@ -815,9 +812,7 @@ class Specs(SpecSet): no_obfuscate=['hostname', 'ipv4', 'ipv6', 'mac'] ) subscription_manager_status = RegistryPoint(no_obfuscate=['hostname', 'ipv4', 'ipv6', 'mac']) - subscription_manager_syspurpose = RegistryPoint( - no_obfuscate=['ipv4', 'ipv6', 'mac'] - ) + subscription_manager_syspurpose = RegistryPoint(no_obfuscate=['ipv4', 'ipv6', 'mac']) sudoers = RegistryPoint(multi_output=True, filterable=True) swift_conf = RegistryPoint() swift_log = RegistryPoint(filterable=True) diff --git a/insights/specs/default.py b/insights/specs/default.py index 18b1a3704..80d494097 100644 --- a/insights/specs/default.py +++ b/insights/specs/default.py @@ -118,6 +118,7 @@ class DefaultSpecs(Specs): malware_detection = malware_detection_ds.malware_detection # Regular collection specs + ansible_telemetry = simple_command("/usr/share/ansible/telemetry/telemetry.py") abrt_ccpp_conf = simple_file("/etc/abrt/plugins/CCpp.conf") abrt_status_bare = simple_command("/usr/bin/abrt status --bare=True") alternatives_display_python = simple_command("/usr/sbin/alternatives --display python") @@ -316,7 +317,9 @@ class DefaultSpecs(Specs): fapolicyd_rules = glob_file(r"/etc/fapolicyd/rules.d/*.rules") fcoeadm_i = simple_command("/usr/sbin/fcoeadm -i") files_dirs_number = ls.files_dirs_number - filefrag = simple_command("/sbin/filefrag /boot/grub2/grubenv /boot/initramfs*.img /boot/vmlinuz*", keep_rc=True) + filefrag = simple_command( + "/sbin/filefrag /boot/grub2/grubenv /boot/initramfs*.img /boot/vmlinuz*", keep_rc=True + ) findmnt_lo_propagation = simple_command("/bin/findmnt -lo+PROPAGATION") firewall_cmd_list_all_zones = simple_command("/usr/bin/firewall-cmd --list-all-zones") firewalld_conf = simple_file("/etc/firewalld/firewalld.conf") diff --git a/insights/tests/parsers/test_ansible.py b/insights/tests/parsers/test_ansible.py new file mode 100644 index 000000000..9ed87e40c --- /dev/null +++ b/insights/tests/parsers/test_ansible.py @@ -0,0 +1,47 @@ +import doctest +import pytest + +from insights.core.exceptions import ContentException, SkipComponent, ParseException +from insights.parsers import ansible +from insights.tests import context_wrap + +AT_INPUT_NG_CMD = """ +no such file or directory: /usr/share/ansible/telemetry/telemetry.py +""" + +AT_INPUT_NG_CNT = """ +{"collections":{"ansible.builtin":{"resources":{"action":{"ansible.builtin.command":13}},"version":"*"}},"ansible_core":{"version":"2.18.9rc1"}} + +{"collections":{"ansible.builtin":{"resources":{"action":{"ansible.builtin.command":14}},"version":"*"}},"ansible_core":{"version":"2.19.9"}}] +""" + +AT_INPUT = """ +{"collections":{"ansible.builtin":{"resources":{"action":{"ansible.builtin.command":13}},"version":"*"}},"ansible_core":{"version":"2.18.9rc1"}} +{"collections":{"ansible.builtin":{"resources":{"action":{"ansible.builtin.command":14}},"version":"*"}},"ansible_core":{"version":"2.19.9"}} +""".strip() + + +def test_ansible_telemetry(): + ret = ansible.AnsibleTelemetry(context_wrap(AT_INPUT)) + assert len(ret) == 2 + assert ret[0]['ansible_core']['version'] == '2.18.9rc1' + assert ret[1]['collections']['ansible.builtin']['resources']['action'] == { + 'ansible.builtin.command': 14 + } + + with pytest.raises(SkipComponent): + ansible.AnsibleTelemetry(context_wrap("")) + + with pytest.raises(ContentException): + ansible.AnsibleTelemetry(context_wrap(AT_INPUT_NG_CMD)) + + with pytest.raises(ParseException): + ansible.AnsibleTelemetry(context_wrap(AT_INPUT_NG_CNT)) + + +def test_doc_examples(): + env = { + 'ansible_telemetry': ansible.AnsibleTelemetry(context_wrap(AT_INPUT)), + } + failed, total = doctest.testmod(ansible, globs=env) + assert failed == 0