def test_datasource_provider(): data = "blahblah\nblahblah2" class MyParser(Parser): def parse_content(self, content): self.content = content ds = DatasourceProvider(data, relative_path="things") p = MyParser(ds) assert p.content == data.splitlines() assert list(ds.stream()) == data.splitlines()
def lpstat_protocol_printers_info(broker): """ This datasource provides the not-sensitive information collected from ``/usr/bin/lpstat -v``. Typical content of ``/usr/bin/lpstat -v`` file is:: "device for test_printer1: ipp://cups.test.com/printers/test_printer1" Returns: DatasourceProvider: Returns the collected content containing non-sensitive information Raises: SkipComponent: When the filter/path does not exist or any exception occurs. """ try: content = broker[LocalSpecs.lpstat_v].content result = [] for line in content: if "device for " in line: "Remove printer address information" result.append( line.split("://", 1)[0] if '://' in line else line) if result: return DatasourceProvider( content="\n".join(result), relative_path='insights_commands/lpstat_-v') except Exception as e: raise SkipComponent("Unexpected exception:{e}".format(e=str(e))) raise SkipComponent
def cloud_cfg(broker): """This datasource provides the network configuration collected from ``/etc/cloud/cloud.cfg``. Typical content of ``/etc/cloud/cloud.cfg`` file is:: #cloud-config users: - name: demo ssh-authorized-keys: - key_one - key_two passwd: $6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/ network: version: 1 config: - type: physical name: eth0 subnets: - type: dhcp - type: dhcp6 system_info: default_user: name: user2 plain_text_passwd: 'someP@assword' home: /home/user2 debug: output: /var/log/cloud-init-debug.log verbose: true Note: This datasource may be executed using the following command: ``insights-cat --no-header cloud_cfg`` Example: ``{"version": 1, "config": [{"type": "physical", "name": "eth0", "subnets": [{"type": "dhcp"}, {"type": "dhcp6"}]}]}`` Returns: str: JSON string when the ``network`` parameter is configure, else nothing is returned. Raises: SkipComponent: When the path does not exist. """ relative_path = '/etc/cloud/cloud.cfg' network_config = '' try: with open(relative_path, 'r') as f: content = yaml.load(f, Loader=yaml.SafeLoader) network_config = content.get('network', None) if network_config: return DatasourceProvider(content=json.dumps(network_config), relative_path=relative_path) except OSError: raise SkipComponent()
def cmd_and_pkg(broker): """ Collect a list of running commands and the associated RPM package providing those commands. The commands are based on filters so rules must add the desired commands as filters to enable collection. If a command is not provided by an RPM then it will not be included in the output. In order for the full command line to be present in the Ps combiner a filter must be added to the spec ``ps_auxww``. A filter must also be added to ``package_provides_command`` so this datasource will look for the command in Ps. Arguments: broker: the broker object for the current session Returns: DatasourceProvider: Returns the collected information as a file with 1 line per command Raises: SkipComponent: Raised if no data is collected """ commands = get_filters(Specs.package_provides_command) """ list: List of commands to search for, added as filters for the spec """ if commands: pkg_cmd = list() for cmd in get_running_commands(broker[Ps], broker[HostContext], list(commands)): pkg = get_package(broker[HostContext], cmd) if pkg is not None: pkg_cmd.append("{0} {1}".format(cmd, pkg)) if pkg_cmd: return DatasourceProvider( '\n'.join(pkg_cmd), relative_path='insights_commands/package_provides_command') raise SkipComponent
def awx_manage_check_license_data_datasource(broker): """ This datasource provides the not-sensitive information collected from ``/usr/bin/awx-manage check_license --data``. Typical content of ``/usr/bin/awx-manage check_license --data`` file is:: {"contact_email": "*****@*****.**", "company_name": "test Inc", "instance_count": 100, "license_date": 1655092799, "license_type": "enterprise", "subscription_name": "Red Hat Ansible Automation, Standard (100 Managed Nodes)", "sku": "MCT3691", "support_level": "Standard", "product_name": "Red Hat Ansible Automation Platform", "valid_key": true, "satellite": null, "pool_id": "2c92808179803e530179ea5989a157a4", "current_instances": 1, "available_instances": 100, "free_instances": 99, "time_remaining": 29885220, "trial": false, "grace_period_remaining": 32477220, "compliant": true, "date_warning": false, "date_expired": false} Returns: str: JSON string containing non-sensitive information. Raises: SkipComponent: When the filter/path does not exist or any exception occurs. """ try: filters = get_filters(Specs.awx_manage_check_license_data) content = broker[LocalSpecs.awx_manage_check_license_data_raw].content if content and filters: json_data = json.loads(content[0]) filter_result = {} for item in filters: filter_result[item] = json_data.get(item) if filter_result: return DatasourceProvider( content=json.dumps( collections.OrderedDict(sorted( filter_result.items()))), relative_path= 'insights_commands/awx-manage_check_license_--data') except Exception as e: raise SkipComponent("Unexpected exception:{e}".format(e=str(e))) raise SkipComponent
def ld_library_path_of_user(broker): """ list: The list of "Username LD_LIBRARY_PATH", e.g.:: [ 'sr1adm /usr/sap/RH1/SYS/exe/run:/usr/lib/', 'sr2adm /usr/sap/RH2/SYS/exe/run', ] .. note:: Currently, only Sap users are supported. """ ctx = broker[HostContext] llds = [] for sid in broker[sap_sid]: usr = '******'.format(sid) ret, vvs = ctx.shell_out("/bin/su -l {0} -c /bin/env".format(usr), keep_rc=True, timeout=DEFAULT_SHELL_TIMEOUT) if ret != 0: continue for v in vvs: if "LD_LIBRARY_PATH=" in v: llds.append('{0} {1}'.format(usr, v.split('=', 1)[-1])) if llds: return DatasourceProvider( '\n'.join(llds), relative_path='insights_commands/echo_user_LD_LIBRARY_PATH') raise SkipComponent('')
def candlepin_broker(broker): """ This datasource provides the candlepn broker configuration information collected from ``/etc/candlepin/broker.xml``. Typical content of ``/etc/candlepin/broker.xml`` file is:: <configuration xmlns="urn:activemq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq /schema/artemis-configuration.xsd"> <core xmlns="urn:activemq:core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:activemq:core "> <acceptors> <acceptor name="in-vm">vm://0</acceptor> <acceptor name="stomp">tcp://localhost:61613?protocols=STOMP;useEpoll=false;sslEnabled=true;trustStorePath=/etc/candlepin/certs/truststore;trustStorePassword=CDX9i3K5uPPBzcNtzz5tcycVf5PuXA5w;keyStorePath=/etc/candlepin/certs/keystore;keyStorePassword=4iBpTS45VZjFmVdNzRhRKNXtxbsH5Dij;needClientAuth=true</acceptor> </acceptors> <security-enabled>true</security-enabled> </core> </configuration> Note: This datasource may be executed using the following command: ``insights cat --no-header candlepin_broker`` Returns: str: XML string after removeing sensitive information. Raises: SkipComponent: When the path does not exist or any exception occurs. """ relative_path = '/etc/candlepin/broker.xml' try: content = broker[LocalSpecs.candlepin_broker_input].content if content: root = ET.fromstring('\n'.join(content)) # remove namespace before save to avoid urgly search for node in root.getiterator(): prefix, has_namespace, postfix = node.tag.rpartition('}') if has_namespace: node.tag = postfix # remove sensitive data core_ele = root.find('core') passwd_ele = core_ele.find('cluster-password') if passwd_ele is not None: core_ele.remove(passwd_ele) acc_ele = core_ele.find('acceptors') if acc_ele: core_ele.remove(acc_ele) return DatasourceProvider( content=[line for line in ET.tostring(root).decode('utf-8').splitlines() if line.strip()], relative_path=relative_path ) except Exception as e: raise SkipComponent("Unexpected exception:{e}".format(e=str(e))) raise SkipComponent()
def test_ansible_tower_license_datasource(): awx_manage_data = Mock() awx_manage_data.content = AWX_MANAGE_LICENSE.splitlines() broker = {LocalSpecs.awx_manage_check_license_data_raw: awx_manage_data} result = awx_manage_check_license_data_datasource(broker) assert result is not None assert isinstance(result, DatasourceProvider) expected = DatasourceProvider(content=json.dumps(collections.OrderedDict(sorted(AWX_MANAGE_FILTER_JSON.items()))), relative_path=RELATIVE_PATH) assert result.content == expected.content assert result.relative_path == expected.relative_path
def test_ps_eo_cmd(): ps_eo_args = Mock() ps_eo_args.content = PS_DATA.splitlines() broker = {LocalSpecs.ps_eo_args: ps_eo_args} result = ps_eo_cmd(broker) assert result is not None assert isinstance(result, DatasourceProvider) expected = DatasourceProvider(content=PS_EXPECTED.strip(), relative_path=RELATIVE_PATH) assert result.content == expected.content assert result.relative_path == expected.relative_path
def test_cloud_cfg(): cloud_init_file = Mock() cloud_init_file.content = CLOUD_CFG.splitlines() broker = {LocalSpecs.cloud_cfg_input: cloud_init_file} result = cloud_cfg(broker) assert result is not None assert isinstance(result, DatasourceProvider) expected = DatasourceProvider(content=json.dumps(CLOUD_CFG_JSON), relative_path=RELATIVE_PATH) assert result.content == expected.content assert result.relative_path == expected.relative_path
def test_lpstat_datasource(): lpstat_data = Mock() lpstat_data.content = LPSTAT_V.splitlines() broker = {LocalSpecs.lpstat_v: lpstat_data} result = lpstat_protocol_printers_info(broker) assert result is not None assert isinstance(result, DatasourceProvider) expected = DatasourceProvider(content=LPSTAT_V_RESULT, relative_path=RELATIVE_PATH) assert result.content == expected.content assert result.relative_path == expected.relative_path
def test_candlpin_broker_no_sensitive_info(): candlepin_broker_file = Mock() candlepin_broker_file.content = CANDLEPIN_BROKER_NO_SENSITIVE_INFO.splitlines() broker = {candlepin_broker.LocalSpecs.candlepin_broker_input: candlepin_broker_file} result = candlepin_broker.candlepin_broker(broker) assert result is not None assert isinstance(result, DatasourceProvider) expected = DatasourceProvider(content=CANDLE_BROKER_NO_SENTISVE_INFO.splitlines(), relative_path=RELATIVE_PATH) xml_check_removed(result.content) xml_compare(result.content, expected.content) assert result.relative_path == expected.relative_path
def test_cloud_cfg(ssh_deletekeys): cloud_cfg_string = CLOUD_CFG.format(value=ssh_deletekeys) cloud_cfg_dict = CLOUD_CFG_JSON.copy() cloud_cfg_dict["ssh_deletekeys"] = ssh_deletekeys cloud_init_file = Mock() cloud_init_file.content = cloud_cfg_string.splitlines() broker = {LocalSpecs.cloud_cfg_input: cloud_init_file} result = cloud_cfg(broker) assert result is not None assert isinstance(result, DatasourceProvider) expected = DatasourceProvider(content=json.dumps(cloud_cfg_dict), relative_path=RELATIVE_PATH) assert result.content == expected.content assert result.relative_path == expected.relative_path
def malware_detection_app(broker): """ Custom datasource to collects content for malware scanner if a scanner is present on the system """ try: # Only run malware-detection if it was passed as an option to insights-client insights_config = InsightsConfig().load_all() if not (insights_config and hasattr(insights_config, 'app') and insights_config.app == 'malware-detection'): raise SkipComponent mdc = MalwareDetectionClient(insights_config) scan_results = mdc.run() if scan_results: return DatasourceProvider(content=scan_results, relative_path="malware-detection-results.json") else: raise SkipComponent except Exception: raise SkipComponent
def test_satellite_missed_queues_with_more_data(): host_uuids = Mock() host_uuids.content = HOST_UUIDS_2.splitlines() qpid_queues = Mock() qpid_queues.content = QPID_QUEUES.splitlines() messages = Mock() messages.stream = mock_stream broker = { Specs.messages: messages, LocalSpecs.content_host_uuids: host_uuids, LocalSpecs.qpid_queues: qpid_queues, } result = satellite_missed_pulp_agent_queues(broker) assert result is not None assert isinstance(result, DatasourceProvider) expected = DatasourceProvider(content=MISSED_QUEUES_OUTPUT_2.splitlines(), relative_path=RELATIVE_PATH) assert sorted(result.content) == sorted(expected.content) assert result.relative_path == expected.relative_path
def yum_updates(_broker): """ This datasource provides a list of available updates on the system. It uses the yum python library installed locally, and collects list of available package updates, along with advisory info where applicable. Sample data returned:: { "releasever": "8", "basearch": "x86_64", "update_list": { "NetworkManager-1:1.22.8-4.el8.x86_64": { "available_updates": [ { "package": "NetworkManager-1:1.22.8-5.el8_2.x86_64", "repository": "rhel-8-for-x86_64-baseos-rpms", "basearch": "x86_64", "releasever": "8", "erratum": "RHSA-2020:3011" } ] } }, "metadata_time": "2021-01-01T09:39:45Z" } Returns: list: List of available updates Raises: SkipComponent: Raised on systems different than RHEL 7 """ if not _broker.get(IsRhel7): raise SkipComponent("Yum updates currently only works on RHEL 7") with UpdatesManager() as umgr: umgr.load() response = { "releasever": umgr.releasever, "basearch": umgr.basearch, "update_list": {}, } data = {'package_list': umgr.installed_packages()} updates = {} for pkg in data["package_list"]: (nevra, updates_list) = umgr.updates(pkg) updates[nevra] = updates_list for (nevra, update_list) in updates.items(): if update_list: out_list = [] for pkg in umgr.sorted_pkgs(update_list): pkg_dict = { "package": umgr.pkg_nevra(pkg), "repository": umgr.pkg_repo(pkg), "basearch": response["basearch"], "releasever": response["releasever"], } erratum = umgr.advisory(pkg) if erratum: pkg_dict["erratum"] = erratum out_list.append(pkg_dict) response["update_list"][nevra] = {"available_updates": out_list} ts = umgr.last_update() if ts: response["metadata_time"] = time.strftime("%FT%TZ", time.gmtime(ts)) return DatasourceProvider(content=json.dumps(response), relative_path='insights_commands/yum_updates_list')
def the_data(broker): data = """ foo bar baz baz bar """.strip() return DatasourceProvider(data, "the_data")
def machine_id(broker): doc = json.loads(broker[OpenshiftSpecsImpl.namespaces].content) v = next(o["metadata"]["uid"] for o in doc["items"] if o["metadata"]["name"] == "kube-system") return DatasourceProvider(content=v, relative_path="/etc/insights-client/machine-id")
988 /usr/sbin/httpd -DFOREGROUND 1036 /usr/sbin/httpd -DFOREGROUND 1037 /usr/sbin/httpd -DFOREGROUND 1038 /usr/sbin/httpd -DFOREGROUND 1039 /usr/sbin/httpd -DFOREGROUND 1040 /usr/local/sbin/httpd -DFOREGROUND 28218 /usr/bin/java TestSleepMethod1 28219 java TestSleepMethod1 28240 /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.292.b10-1.el7_9.x86_64/jre/bin/java TestSleepMethod2 333083 /home/user3/apps/pycharm-2021.1.1/jbr/bin/java -classpath /home/user3/apps/pycharm-2021.1.1/lib/bootstrap.jar:/home/user3/apps/pycharm-2021.1.1/lib/util.jar:/home/user3/apps/pycharm-2021.1.1/lib/jdom.jar:/home/user3/apps/pycharm-2021.1.1/lib/log4j.jar:/home/user3/apps/pycharm-2021.1.1/lib/jna.jar -Xms128m -Xmx2048m -XX:ReservedCodeCacheSize=512m -XX:+UseG1GC -XX:SoftRefLRUPolicyMSPerMB=50 -XX:CICompilerCount=2 -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow -ea -Dsun.io.useCanonCaches=false -Djdk.http.auth.tunneling.disabledSchemes="" -Djdk.attach.allowAttachSelf=true -Djdk.module.illegalAccess.silent=true -Dkotlinx.coroutines.debug=off -Dsun.tools.attach.tmp.only=true -XX:ErrorFile=/home/user3/java_error_in_pycharm_%p.log -XX:HeapDumpPath=/home/user3/java_error_in_pycharm_.hprof -Didea.vendor.name=JetBrains -Didea.paths.selector=PyCharm2021.1 -Djb.vmOptionsFile=/home/user3/.config/JetBrains/PyCharm2021.1/pycharm64.vmoptions -Didea.platform.prefix=Python com.intellij.idea.Main """ EXPECTED = DatasourceProvider( "\n".join([ "{0} {1}".format(HTTPD_PATH, HTTPD_PKG), "{0} {1}".format(JAVA_PATH_1, JAVA_PKG_2), "{0} {1}".format(JAVA_PATH_2, JAVA_PKG_2) ]), relative_path='insights_commands/package_provides_command') def test_cmd_and_pkg(): pseo = PsEoCmd(context_wrap(PS_EO_CMD)) ps = Ps(None, None, None, None, None, None, pseo) broker = dr.Broker() broker[HostContext] = FakeContext() broker[Ps] = ps result = cmd_and_pkg(broker) assert result is not None assert sorted(result.content) == sorted(EXPECTED.content)
def cloud_cfg(broker): """ This datasource provides configuration collected from ``/etc/cloud/cloud.cfg``. Typical content of ``/etc/cloud/cloud.cfg`` file is:: #cloud-config users: - name: demo ssh-authorized-keys: - key_one - key_two passwd: $6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/ ssh_deletekeys: 1 network: version: 1 config: - type: physical name: eth0 subnets: - type: dhcp - type: dhcp6 system_info: default_user: name: user2 plain_text_passwd: 'someP@assword' home: /home/user2 debug: output: /var/log/cloud-init-debug.log verbose: true Note: This datasource may be executed using the following command: ``insights cat --no-header cloud_cfg`` Sample output in JSON format:: { "ssh_deletekeys": 1, "network": { "version": 1, "config": [ { "type": "physical", "name": "eth0", "subnets": [ { "type": "dhcp" }, { "type": "dhcp6" } ] } ] }, "debug": { "output": "/var/log/cloud-init-debug.log", "verbose": true } } Returns: str: JSON string after removing the sensitive information. Raises: SkipComponent: When the path does not exist or any exception occurs. """ relative_path = '/etc/cloud/cloud.cfg' try: filters = get_filters(Specs.cloud_cfg) content = broker[LocalSpecs.cloud_cfg_input].content if content and filters: result = dict() content = yaml.load('\n'.join(content), Loader=yaml.SafeLoader) if isinstance(content, dict): # remove sensitive data content.pop('users', None) content.pop('system_info', None) # apply filters for item in filters: if item in content: result[item] = content[item] if result: result = dict(sorted(result.items(), key=lambda x: x[0])) return DatasourceProvider(content=json.dumps(result), relative_path=relative_path) raise SkipComponent("Invalid YAML format") except Exception as e: raise SkipComponent("Unexpected exception:{e}".format(e=str(e))) raise SkipComponent
def satellite_missed_pulp_agent_queues(broker): """ This datasource provides the missed pulp agent queues information on satellite server. Note: This datasource may be executed using the following command: ``insights cat --no-header satellite_missed_pulp_agent_queues`` Sample output:: pulp.agent.09008eec-aba6-4174-aa9f-e930004ce5c9:2018-01-16 00:06:13 pulp.agent.fac7ebbc-ee4f-44b4-9fe0-3f4e42c7f024:2018-01-16 00:06:16 0 Returns: str: All the missed pulp agent queues and the boolean mark if the data is truncated in the last line. If the value of last line is 0, it means all the missed queues are returned. If the value of the last line is 1, it means there are a lot of missed queues, to avoid render error, only the first 10 missed queues are returned. Raises: SkipComponent: When the error doen't happen or the missed queues have been recreated. """ def _parse_non_existing_queues_in_msg(): agentq_date_re = re.compile( r'^(?P<date>\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[Protocol\] error Error on attach: Node not found: (?P<agentq>pulp.agent.[0-9a-f-]+)$' ) agent_queue_last_date = {} ms_obj = broker[Specs.messages] for line in ms_obj.stream(): if NODE_NOT_FOUND_ERROR in line: info, msg = [i.strip() for i in line.split(': ', 1)] info_splits = info.rsplit(None, 2) if len(info_splits) >= 3 and info_splits[2].startswith('qpidd'): # The timestamp from syslog doesn't contain the year, but the # message itself does - so use that. match = agentq_date_re.search(msg) if match: agent_queue_last_date[match.group('agentq')] = match.group('date') return agent_queue_last_date def _get_content_host_uuid(): output = broker[LocalSpecs.content_host_uuids].content host_uuids = [] if len(output) > 3: for line in output[2:-1]: host_uuids.append(line.strip()) return host_uuids def _get_qpid_queues(): output = broker[LocalSpecs.qpid_queues].content current_queues = [] if len(output) > 3: current_queues = [line.split()[0].strip() for line in output[3:] if line.split()[0].startswith('pulp.agent')] return current_queues missed_queues_in_log = _parse_non_existing_queues_in_msg() if missed_queues_in_log: host_uuids = _get_content_host_uuid() if host_uuids: qpid_queues = _get_qpid_queues() missed_queues = [] too_more_data = 0 for queue in missed_queues_in_log: if queue.split('.')[-1] in host_uuids and queue not in qpid_queues: missed_queues.append('%s:%s' % (queue, missed_queues_in_log[queue])) # only return 10 missed queues in case too long data can't be rendered if len(missed_queues) >= 10: too_more_data = 1 break if missed_queues: missed_queues.append(str(too_more_data)) return DatasourceProvider(missed_queues, relative_path='insights_commands/satellite_missed_qpid_queues') raise SkipComponent
def cloud_cfg(broker): """ This datasource provides the network configuration information collected from ``/etc/cloud/cloud.cfg``. Typical content of ``/etc/cloud/cloud.cfg`` file is:: #cloud-config users: - name: demo ssh-authorized-keys: - key_one - key_two passwd: $6$j212wezy$7H/1LT4f9/N3wpgNunhsIqtMj62OKiS3nyNwuizouQc3u7MbYCarYeAHWYPYb2FT.lbioDm2RrkJPb9BZMN1O/ network: version: 1 config: - type: physical name: eth0 subnets: - type: dhcp - type: dhcp6 system_info: default_user: name: user2 plain_text_passwd: 'someP@assword' home: /home/user2 debug: output: /var/log/cloud-init-debug.log verbose: true Note: This datasource may be executed using the following command: ``insights cat --no-header cloud_cfg`` Sample data returned includes only the ``network`` portion of the input file in JSON format:: { "version": 1, "config": [ { "type": "physical", "name": "eth0", "subnets": [ {"type": "dhcp"}, {"type": "dhcp6"} ] } ] } Returns: str: JSON string when the ``network`` parameter includes content, else `None` is returned. Raises: SkipComponent: When the path does not exist or any exception occurs. """ relative_path = '/etc/cloud/cloud.cfg' try: content = broker[LocalSpecs.cloud_cfg_input].content if content: content = yaml.load('\n'.join(content), Loader=yaml.SafeLoader) network_config = content.get('network', None) if network_config: return DatasourceProvider(content=json.dumps(network_config), relative_path=relative_path) except Exception as e: raise SkipComponent("Unexpected exception:{e}".format(e=str(e))) raise SkipComponent('No network section in yaml')
def ps_eo_cmd(broker): """ Custom datasource to collect the full paths to all running commands on the system provided by the ``ps -eo pid,args`` command. After collecting the data, all of the args are trimmed to leave only the command including full path. Sample output from the ``ps -eo pid, args`` command:: PID COMMAND 1 /usr/lib/systemd/systemd --switched-root --system --deserialize 31 2 [kthreadd] 3 [rcu_gp] 4 [rcu_par_gp] 6 [kworker/0:0H-events_highpri] 9 [mm_percpu_wq] 10 [rcu_tasks_kthre] 11 /usr/bin/python3 /home/user1/python_app.py 12 [kworker/u16:0-kcryptd/253:0] This datasource trims off the args to minimize possible PII and sensitive information. After trimming the data looks like this:: PID COMMAND 1 /usr/lib/systemd/systemd 2 [kthreadd] 3 [rcu_gp] 4 [rcu_par_gp] 6 [kworker/0:0H-events_highpri] 9 [mm_percpu_wq] 10 [rcu_tasks_kthre] 11 /usr/bin/python3 12 [kworker/u16:0-kcryptd/253:0] Returns: str: Returns a multiline string in the same format as ``ps`` output Raises: SkipComponent: Raised if no data is available """ content = broker[LocalSpecs.ps_eo_args].content data = [] data.append('PID COMMAND') start = False for l in content: if 'PID' in l and 'COMMAND' in l: start = True continue if not start: continue pid, args = l.strip().split(None, 1) if ' ' in args: cmd, _ = args.split(None, 1) else: cmd = args data.append('{0} {1}'.format(pid, cmd)) if len(data) > 1: return DatasourceProvider('\n'.join(data), relative_path='insights_commands/ps_eo_cmd') raise SkipComponent()