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 parse_content(self, content): if content and "Connection refused" in content[0]: raise SkipComponent("NTP service is down and connection refused") leap = None for line in content: if 'leap=' in line: leap = line.split('leap=')[1].rstrip() if leap is None: raise SkipComponent() self.update(leap=leap)
def pmlog_summary_args(broker): """ Determines the pmlogger file and the metrics to collect via `pmlog_summary` spec. Returns: str: Full arguments string that will be passed to the `pmlogsummary`, which contains the `pmlogger` archive file and the required `metrics`. Raises: SkipComponent: Raises when meeting one of the following scenario: - No pmlogger process is running - No pmlogger file - No "mandatory on" metrics in `config.ros` """ pm_file = None try: ps = broker[Ps] if not ps.search(COMMAND_NAME__contains='pmlogger'): raise SkipComponent("No 'pmlogger' is running") pcp_log_date = (datetime.date.today() - datetime.timedelta(days=1)).strftime("%Y%m%d") pm_file = "/var/log/pcp/pmlogger/ros/{0}.index".format(pcp_log_date) if not (os.path.exists(pm_file) and os.path.isfile(pm_file)): raise SkipComponent( "No pmlogger archive file: {0}".format(pm_file)) except Exception as e: raise SkipComponent( "Failed to check pmlogger file existence: {0}".format(str(e))) metrics = set() try: ros = broker[RosConfig] for spec in ros.specs: if spec.get('state') == 'mandatory on': metrics.update(spec.get('metrics').keys()) if not metrics: raise SkipComponent("No 'mandatory on' metrics in config.ros") except Exception as e: raise SkipComponent("Failed to get pmlogger metrics: {0}".format( str(e))) return "{0} {1}".format(pm_file, ' '.join(sorted(metrics)))
def pmlog_summary_file(broker): """ Determines the name for the pmlogger file and checks for its existance Returns the name of the latest pmlogger summary file if a running ``pmlogger`` process is detected on the system. Returns: str: Full path to the latest pmlogger file Raises: SkipComponent: raises this exception when the command is not present or the file is not present """ ps = broker[Ps] if ps.search(COMMAND__contains='pmlogger'): pcp_log_date = (datetime.date.today() - datetime.timedelta(days=1)).strftime("%Y%m%d") file = "/var/log/pcp/pmlogger/ros/%s.index" % (pcp_log_date) try: if os.path.exists(file) and os.path.isfile(file): return file except Exception as e: SkipComponent( "Failed to check for pmlogger file existance: {0}".format( 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 is_ceph_monitor(broker): """ bool: Returns True if ceph monitor process ceph-mon is running on this node """ ps = broker[DefaultSpecs.ps_auxww].content findall = re.compile(r"ceph\-mon").findall if any(findall(p) for p in ps): return True raise SkipComponent()
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 md_device_list(broker): md = broker[Mdstat] if md.components: return [ dev["device_name"] for dev in md.components if dev["active"] ] 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 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 sap_hana_sid(broker): """ list: List of the SID of SAP HANA Instances. """ hana = broker[LocalSpecs.sap_hana_instance] sids = sorted(set(h.sid.lower() for h in hana)) if sids: return sids raise SkipComponent()
def parse_content(self, content): if "Connection refused" in content[0]: raise SkipComponent("NTP service is down and connection refused") self.data = {} for line in content: m = re.search(r'leap=(\d*)', line) if m: self.data["leap"] = m.group(1)
def sap_hana_instance(broker): """ list: List of the SAP HANA Instances. """ sap = broker[LocalSpecs.sap_instance] insts = sorted(v for v in sap if v.type == 'HDB') if insts: return insts raise SkipComponent()
def sap_hana_sid_SID_nr(broker): """ list: List of tuples (sid, SID, Nr) of SAP HANA Instances. """ hana = broker[LocalSpecs.sap_hana_instance] sids = sorted((h.sid.lower(), h.sid, h.number) for h in hana) if sids: return sids raise SkipComponent()
def parse_content(self, content): if "Connection refused" in content[0]: raise SkipComponent("NTP service is down and connection refused") self.data = [] for row in content[2:]: if row.strip(): values = row.split(" ", 2) if row.startswith(" "): self.data.append({"source": values[1], "flag": " "}) else: self.data.append({"source": values[0][1:], "flag": values[0][0]})
def pcp_enabled(broker): """ Returns: bool: True if pmproxy service is on in services Raises: SkipComponent: When pmproxy service is not enabled """ if not broker[Services].is_on("pmproxy"): raise SkipComponent("pmproxy not enabled") return True
def parse_content(self, content): if content and "Connection refused" in content[0]: raise SkipComponent("NTP service is down and connection refused") data = [] if len(content) > 2: for row in content[2:]: if row.strip(): values = row.split(" ", 2) if row.startswith(" "): data.append({"source": values[1], "flag": " "}) else: data.append({ "source": values[0][1:], "flag": values[0][0] }) if not data: raise SkipComponent() self.extend(data)
def corosync_cmapctl_cmd_list(broker): """ corosync-cmapctl add different arguments on RHEL7 and RHEL8. Returns: list: A list of related corosync-cmapctl commands based the RHEL version. """ if broker.get(IsRhel7): return ["/usr/sbin/corosync-cmapctl", 'corosync-cmapctl -d runtime.schedmiss.timestamp', 'corosync-cmapctl -d runtime.schedmiss.delay'] if broker.get(IsRhel8): return ["/usr/sbin/corosync-cmapctl", '/usr/sbin/corosync-cmapctl -m stats', '/usr/sbin/corosync-cmapctl -C schedmiss'] raise SkipComponent()
def corosync_cmapctl_cmd_list(broker): if broker.get(IsRhel7): return [ "/usr/sbin/corosync-cmapctl", 'corosync-cmapctl -d runtime.schedmiss.timestamp', 'corosync-cmapctl -d runtime.schedmiss.delay' ] if broker.get(IsRhel8): return [ "/usr/sbin/corosync-cmapctl", '/usr/sbin/corosync-cmapctl -m stats', '/usr/sbin/corosync-cmapctl -C schedmiss' ] raise SkipComponent()
def parse_content(self, content): """ Get source, mode and state for chrony """ data = [] if len(content) > 3: for row in content[3:]: if row.strip(): values = row.split(" ", 2) data.append({ "source": values[1], "mode": values[0][0], "state": values[0][1] }) if not data: raise SkipComponent() self.extend(data)
def corosync_cmapctl_cmd_list(broker): """ corosync-cmapctl add different arguments on RHEL7 and RHEL8. Returns: list: A list of related corosync-cmapctl commands based the RHEL version. """ corosync_cmd = '/usr/sbin/corosync-cmapctl' if os.path.exists(corosync_cmd): if broker.get(IsRhel7): return [ corosync_cmd, ' '.join([corosync_cmd, '-d runtime.schedmiss.timestamp']), ' '.join([corosync_cmd, '-d runtime.schedmiss.delay'])] if broker.get(IsRhel8) or broker.get(IsRhel9): return [ corosync_cmd, ' '.join([corosync_cmd, '-m stats']), ' '.join([corosync_cmd, '-C schedmiss'])] raise SkipComponent()
def pcp_enabled(broker): """ bool: Returns True if pmproxy service is on in services """ if not broker[Services].is_on("pmproxy"): raise SkipComponent("pmproxy not enabled")
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 is_ceph_monitor(broker): """ bool: Returns True if ceph monitor process ceph-mon is running on this node """ ps = broker[Ps] if ps.search(COMMAND__contains='ceph-mon'): return True raise SkipComponent()
def is_azure(broker): """ bool: Returns True if this node is identified as running in Azure """ cp = broker[CloudProvider] if cp and cp.cloud_provider == CloudProvider.AZURE: return True raise SkipComponent()
def __init__(self, rhel, version=None): if rhel.major != version: raise SkipComponent("Not RHEL{vers}".format(vers=version)) self.minor = rhel.minor
def __init__(self, virt): if virt.is_virtual: raise SkipComponent("Not a bare metal system.")
def __init__(self, cp): if not cp or cp.cloud_provider != CloudProvider.GOOGLE: raise SkipComponent("Not Google Cloud Platform instance")
def __init__(self, cp): if not cp or cp.cloud_provider != CloudProvider.AZURE: raise SkipComponent("Not Azure instance")
def __init__(self, rhel): if rhel.major != 8: raise SkipComponent('Not RHEL8')