diff --git a/SOURCES/sos-collector-pexpect.patch b/SOURCES/sos-collector-pexpect.patch new file mode 100644 index 0000000..ca55865 --- /dev/null +++ b/SOURCES/sos-collector-pexpect.patch @@ -0,0 +1,30 @@ +From 76ca28dc5ce222d24086b387a448ceeb53055e4b Mon Sep 17 00:00:00 2001 +From: Jake Hunsaker +Date: Wed, 15 May 2019 16:21:01 -0400 +Subject: [PATCH] [sosnode] Properly quote commands that need a pty + +When we run locally and need a pty, we need to quote the command and +launch it in a bash shell to function correctly with pexpect.spawn(). + +Signed-off-by: Jake Hunsaker +--- + soscollector/sosnode.py | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/soscollector/sosnode.py b/soscollector/sosnode.py +index 6c01471..fc42b94 100644 +--- a/soscollector/sosnode.py ++++ b/soscollector/sosnode.py +@@ -366,6 +366,9 @@ class SosNode(): + get_pty = True + if not self.local and not force_local: + cmd = "%s %s" % (self.ssh_cmd, quote(cmd)) ++ else: ++ if get_pty: ++ cmd = "/bin/bash -c %s" % quote(cmd) + res = pexpect.spawn(cmd, encoding='utf-8') + if need_root: + if self.config['need_sudo']: +-- +2.17.2 + diff --git a/SOURCES/sos-collector-rhhi-list.patch b/SOURCES/sos-collector-rhhi-list.patch new file mode 100644 index 0000000..588fb5f --- /dev/null +++ b/SOURCES/sos-collector-rhhi-list.patch @@ -0,0 +1,172 @@ +From 9108f2f83613bce42e8be91bbe7fe91fd1b1763a Mon Sep 17 00:00:00 2001 +From: Jake Hunsaker +Date: Thu, 18 Apr 2019 14:58:23 -0400 +Subject: [PATCH] [sos-collector] Fix --list-options output + +Fixes the output of --list-options to include layered profiles when +reporting base profile options. + +Additionally adds output of the cluster types supported by the local +installation of sos-collector. Added cluster_name values to clusters +that were missing them. + +Fixes: #22 + +Signed-off-by: Jake Hunsaker +--- + soscollector/clusters/jbon.py | 1 + + soscollector/clusters/kubernetes.py | 1 + + soscollector/clusters/pacemaker.py | 1 + + soscollector/clusters/satellite.py | 1 + + soscollector/sos_collector.py | 40 +++++++++++++++++------------ + 5 files changed, 27 insertions(+), 17 deletions(-) + +diff --git a/soscollector/clusters/jbon.py b/soscollector/clusters/jbon.py +index ea85ddf..0c53093 100644 +--- a/soscollector/clusters/jbon.py ++++ b/soscollector/clusters/jbon.py +@@ -23,6 +23,7 @@ class jbon(Cluster): + use the provided --nodes list + ''' + ++ cluster_name = 'Just a Bunch Of Nodes (no cluster)' + packages = None + + def get_nodes(self): +diff --git a/soscollector/clusters/kubernetes.py b/soscollector/clusters/kubernetes.py +index e18ee71..32bc68b 100644 +--- a/soscollector/clusters/kubernetes.py ++++ b/soscollector/clusters/kubernetes.py +@@ -19,6 +19,7 @@ from soscollector.clusters import Cluster + + class kubernetes(Cluster): + ++ cluster_name = 'Community Kubernetes' + packages = ('kubernetes-master',) + sos_plugins = ['kubernetes'] + sos_plugin_options = {'kubernetes.all': 'on'} +diff --git a/soscollector/clusters/pacemaker.py b/soscollector/clusters/pacemaker.py +index 778d8ae..be6c09e 100644 +--- a/soscollector/clusters/pacemaker.py ++++ b/soscollector/clusters/pacemaker.py +@@ -18,6 +18,7 @@ from soscollector.clusters import Cluster + + class pacemaker(Cluster): + ++ cluster_name = 'Pacemaker High Availability Cluster Manager' + sos_plugins = ['pacemaker'] + packages = ('pacemaker',) + option_list = [ +diff --git a/soscollector/clusters/satellite.py b/soscollector/clusters/satellite.py +index ccdfefb..2c5c73d 100644 +--- a/soscollector/clusters/satellite.py ++++ b/soscollector/clusters/satellite.py +@@ -20,6 +20,7 @@ from soscollector.clusters import Cluster + class satellite(Cluster): + """Red Hat Satellite 6""" + ++ cluster_name = 'Red Hat Satellite 6' + packages = ('satellite', 'satellite-installer') + + def _psql_cmd(self, query): +diff --git a/soscollector/sos_collector.py b/soscollector/sos_collector.py +index 54410a2..5b31602 100644 +--- a/soscollector/sos_collector.py ++++ b/soscollector/sos_collector.py +@@ -218,19 +218,28 @@ class SosCollector(): + + def list_options(self): + '''Display options for available clusters''' ++ ++ sys.stdout.write('\nThe following clusters are supported by this ' ++ 'installation\n') ++ sys.stdout.write('Use the short name with --cluster-type or cluster ' ++ 'options (-c)\n\n') ++ for cluster in sorted(self.clusters, key=lambda x: self.clusters[x]): ++ sys.stdout.write(" {:<15} {:30}\n".format( ++ cluster, ++ self.clusters[cluster].cluster_name)) ++ + _opts = {} + for _cluster in self.clusters: + for opt in self.clusters[_cluster].options: + if opt.name not in _opts.keys(): + _opts[opt.name] = opt + else: +- clust = _opts[opt.name].cluster +- if any(c in opt.cluster for c in clust): +- _opts[opt.name].cluster = ','.join(clust) +- else: +- _opts[opt.name] = opt +- print('\nThe following cluster options are available:\n') +- print('{:25} {:15} {:<10} {:10} {:<}'.format( ++ for clust in opt.cluster: ++ if clust not in _opts[opt.name].cluster: ++ _opts[opt.name].cluster.append(clust) ++ ++ sys.stdout.write('\nThe following cluster options are available:\n\n') ++ sys.stdout.write(' {:25} {:15} {:<10} {:10} {:<}\n'.format( + 'Cluster', + 'Option Name', + 'Type', +@@ -239,20 +248,17 @@ class SosCollector(): + )) + + for _opt in sorted(_opts, key=lambda x: _opts[x].cluster): +- _clus = None + opt = _opts[_opt] +- if isinstance(opt.cluster, list): +- _clus = opt.cluster[0] +- optln = '{:25} {:15} {:<10} {:<10} {:<10}'.format( +- _clus or opt.cluster, ++ optln = ' {:25} {:15} {:<10} {:<10} {:<10}\n'.format( ++ ', '.join(c for c in sorted(opt.cluster)), + opt.name, + opt.opt_type.__name__, + str(opt.value), +- opt.description +- ) +- print(optln) +- print('\nOptions take the form of cluster.name=value' +- '\nE.G. "ovirt.no-database=True" or "pacemaker.offline=False"') ++ opt.description) ++ sys.stdout.write(optln) ++ sys.stdout.write('\nOptions take the form of cluster.name=value' ++ '\nE.G. "ovirt.no-database=True" or ' ++ '"pacemaker.offline=False"\n') + + def delete_tmp_dir(self): + '''Removes the temp directory and all collected sosreports''' +-- +2.17.2 + +From 70faf097e9d36898cfc624e9e57f46eea25d321c Mon Sep 17 00:00:00 2001 +From: Jake Hunsaker +Date: Tue, 2 Jul 2019 12:35:14 -0400 +Subject: [PATCH] [sos-collector] Remove key sorting on cluster list + +On python3, using the cluster key on sorting the list was causing an +exception. After testing, it appears a normal keyless sort still gives +us the desired effect, so remove the key sorting. + +Signed-off-by: Jake Hunsaker +--- + soscollector/sos_collector.py | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/soscollector/sos_collector.py b/soscollector/sos_collector.py +index a691219..d0a598f 100644 +--- a/soscollector/sos_collector.py ++++ b/soscollector/sos_collector.py +@@ -223,7 +223,7 @@ class SosCollector(): + 'installation\n') + sys.stdout.write('Use the short name with --cluster-type or cluster ' + 'options (-c)\n\n') +- for cluster in sorted(self.clusters, key=lambda x: self.clusters[x]): ++ for cluster in sorted(self.clusters): + sys.stdout.write(" {:<15} {:30}\n".format( + cluster, + self.clusters[cluster].cluster_name)) +-- +2.17.2 + diff --git a/SOURCES/sos-collector-rhhiv-profile.patch b/SOURCES/sos-collector-rhhiv-profile.patch new file mode 100644 index 0000000..8a65aa9 --- /dev/null +++ b/SOURCES/sos-collector-rhhiv-profile.patch @@ -0,0 +1,234 @@ +From fb8c2af36672b5868f504bae0704392f9e9b44a5 Mon Sep 17 00:00:00 2001 +From: Jake Hunsaker +Date: Fri, 12 Apr 2019 14:45:01 -0400 +Subject: [PATCH 1/3] [clusters] Add a cluster_name class member for better + identification + +Adds a cluster_name class member to Cluster() so that clusters may +specify a well-defined name beyond the name/acronym used to enable the +cluster manually. + +Signed-off-by: Jake Hunsaker +--- + soscollector/clusters/__init__.py | 9 +++++++++ + soscollector/sos_collector.py | 3 +-- + soscollector/sosnode.py | 1 - + 3 files changed, 10 insertions(+), 3 deletions(-) + +diff --git a/soscollector/clusters/__init__.py b/soscollector/clusters/__init__.py +index b9d2418..c1bd360 100644 +--- a/soscollector/clusters/__init__.py ++++ b/soscollector/clusters/__init__.py +@@ -26,6 +26,7 @@ class Cluster(object): + sos_plugins = [] + sos_plugin_options = {} + sos_preset = '' ++ cluster_name = None + + def __init__(self, config): + '''This is the class that cluster profile should subclass in order to +@@ -50,6 +51,14 @@ class Cluster(object): + self.options = [] + self._get_options() + ++ @classmethod ++ def name(cls): ++ '''Returns the cluster's name as a string. ++ ''' ++ if cls.cluster_name: ++ return cls.cluster_name ++ return cls.__name__.lower() ++ + def _get_options(self): + '''Loads the options defined by a cluster and sets the default value''' + for opt in self.option_list: +diff --git a/soscollector/sos_collector.py b/soscollector/sos_collector.py +index fee48ab..54410a2 100644 +--- a/soscollector/sos_collector.py ++++ b/soscollector/sos_collector.py +@@ -554,8 +554,7 @@ this utility or remote systems that it connects to. + break + + self.config['cluster'] = cluster +- name = str(cluster.__class__.__name__).lower() +- self.config['cluster_type'] = name ++ self.config['cluster_type'] = cluster.name() + self.log_info( + 'Cluster type set to %s' % self.config['cluster_type']) + break +diff --git a/soscollector/sosnode.py b/soscollector/sosnode.py +index 405d05d..3aba0bf 100644 +--- a/soscollector/sosnode.py ++++ b/soscollector/sosnode.py +@@ -100,7 +100,6 @@ class SosNode(): + return True + return False + +- + def create_sos_container(self): + '''If the host is containerized, create the container we'll be using + ''' +-- +2.17.2 + + +From 8d94e9ee9162c1b7676822958a94dfcd727d6dc8 Mon Sep 17 00:00:00 2001 +From: Jake Hunsaker +Date: Fri, 12 Apr 2019 14:47:31 -0400 +Subject: [PATCH 2/3] [clusters] Add cluster_name where appropriate + +Adds a cluster_name to a few of the existing cluster profiles. + +Signed-off-by: Jake Hunsaker +--- + soscollector/clusters/kubernetes.py | 1 + + soscollector/clusters/ovirt.py | 2 ++ + 2 files changed, 3 insertions(+) + +diff --git a/soscollector/clusters/kubernetes.py b/soscollector/clusters/kubernetes.py +index c5c2094..e18ee71 100644 +--- a/soscollector/clusters/kubernetes.py ++++ b/soscollector/clusters/kubernetes.py +@@ -52,6 +52,7 @@ class kubernetes(Cluster): + + class openshift(kubernetes): + ++ cluster_name = 'OpenShift Container Platform' + packages = ('atomic-openshift',) + sos_preset = 'ocp' + cmd = 'oc' +diff --git a/soscollector/clusters/ovirt.py b/soscollector/clusters/ovirt.py +index 0a074ca..18cbf2e 100644 +--- a/soscollector/clusters/ovirt.py ++++ b/soscollector/clusters/ovirt.py +@@ -23,6 +23,7 @@ from getpass import getpass + + class ovirt(Cluster): + ++ cluster_name = 'oVirt' + packages = ('ovirt-engine',) + + option_list = [ +@@ -122,6 +123,7 @@ class ovirt(Cluster): + + class rhv(ovirt): + ++ cluster_name = 'Red Hat Virtualization' + packages = ('rhevm', 'rhvm') + sos_preset = 'rhv' + +-- +2.17.2 + + +From 4606699e9a460ebd6345444ae915ff8384619ed3 Mon Sep 17 00:00:00 2001 +From: Jake Hunsaker +Date: Tue, 16 Apr 2019 15:41:33 -0400 +Subject: [PATCH 3/3] [ovirt] Add RHHI-V support + +Adds support for RHHI-V environments which are RHV environments that +also use the hypervisors as gluster nodes. The new 'rhhi_virt' cluster +profile will be enabled when sos-collector is run against an environment +that is _both_ RHV and has nodes listed in the 'gluster_server' table in +the RHV database. Note that this means if community oVirt is in use, the +gluster bits enabled by 'rhhi_virt' will not be enabled for the oVirt +cluster type. + +Included with this change is making DB queries more programmatic, and +making minor stylistic changes to the main query used by get_nodes() to +allow easier reading of the SQL query being built. + +Finally, remove an unused import of getpass. + +Resolves: #21 + +Signed-off-by: Jake Hunsaker +--- + soscollector/clusters/ovirt.py | 44 ++++++++++++++++++++++++++-------- + 1 file changed, 34 insertions(+), 10 deletions(-) + +diff --git a/soscollector/clusters/ovirt.py b/soscollector/clusters/ovirt.py +index 18cbf2e..8697d2e 100644 +--- a/soscollector/clusters/ovirt.py ++++ b/soscollector/clusters/ovirt.py +@@ -18,13 +18,13 @@ import fnmatch + + from pipes import quote + from soscollector.clusters import Cluster +-from getpass import getpass + + + class ovirt(Cluster): + +- cluster_name = 'oVirt' ++ cluster_name = 'Community oVirt' + packages = ('ovirt-engine',) ++ db_exec = '/usr/share/ovirt-engine/dbscripts/engine-psql.sh -c' + + option_list = [ + ('no-database', False, 'Do not collect a database dump'), +@@ -33,6 +33,14 @@ class ovirt(Cluster): + ('no-hypervisors', False, 'Do not collect from hypervisors') + ] + ++ def _run_db_query(self, query): ++ ''' ++ Wrapper for running DB queries on the master. Any scrubbing of the ++ query should be done _before_ passing the query to this method. ++ ''' ++ cmd = "%s %s" % (self.db_exec, quote(query)) ++ return self.exec_master_cmd(cmd, need_root=True) ++ + def _sql_scrub(self, val): + ''' + Manually sanitize SQL queries since we can't leave this up to the +@@ -58,18 +66,16 @@ class ovirt(Cluster): + def format_db_cmd(self): + cluster = self._sql_scrub(self.get_option('cluster')) + datacenter = self._sql_scrub(self.get_option('datacenter')) +- query = ("select host_name from vds_static where cluster_id in " +- "(select cluster_id from cluster where name like '%s'" +- " and storage_pool_id in (select id from storage_pool " +- "where name like '%s'))" % (cluster, datacenter)) +- self.dbcmd = ('/usr/share/ovirt-engine/dbscripts/engine-psql.sh ' +- '-c {}'.format(quote(query))) +- self.log_debug('Query command for ovirt DB set to: %s' % self.dbcmd) ++ self.dbquery = ("SELECT host_name from vds_static where cluster_id in " ++ "(select cluster_id FROM cluster WHERE name like '%s'" ++ " and storage_pool_id in (SELECT id FROM storage_pool " ++ "WHERE name like '%s'))" % (cluster, datacenter)) ++ self.log_debug('Query command for ovirt DB set to: %s' % self.dbquery) + + def get_nodes(self): + if self.get_option('no-hypervisors'): + return [] +- res = self.exec_master_cmd(self.dbcmd, need_root=True) ++ res = self._run_db_query(self.dbquery) + if res['status'] == 0: + nodes = res['stdout'].splitlines()[2:-1] + return [n.split('(')[0].strip() for n in nodes] +@@ -134,3 +140,21 @@ class rhv(ovirt): + return 'rhvh' + else: + return 'rhelh' ++ ++ ++class rhhi_virt(rhv): ++ ++ cluster_name = 'Red Hat Hyperconverged Infrastructure - Virtualization' ++ sos_plugins = ('gluster',) ++ sos_plugin_options = {'gluster.dump': 'on'} ++ sos_preset = 'rhv' ++ ++ def check_enabled(self): ++ return (self.master.is_installed('rhvm') and self._check_for_rhhiv()) ++ ++ def _check_for_rhhiv(self): ++ ret = self._run_db_query('SELECT count(server_id) FROM gluster_server') ++ if ret['status'] == 0: ++ # if there are any entries in this table, RHHI-V is in use ++ return ret['stdout'].splitlines()[2].strip() != '0' ++ return False +-- +2.17.2 + diff --git a/SPECS/sos-collector.spec b/SPECS/sos-collector.spec index 4901407..7d05b01 100644 --- a/SPECS/sos-collector.spec +++ b/SPECS/sos-collector.spec @@ -1,7 +1,7 @@ Summary: Capture sosreports from multiple nodes simultaneously Name: sos-collector Version: 1.7 -Release: 2%{?dist} +Release: 5%{?dist} Source0: http://people.redhat.com/jhunsake/sos-collector/%{name}-%{version}.tar.gz License: GPLv2 BuildArch: noarch @@ -13,7 +13,9 @@ Provides: clustersos = %{version}-%{release} Patch0: sos-collector-none-cluster-fix.patch Patch1: sos-collector-nested-container-fix.patch Patch2: sos-collector-rhcos-image.patch - +Patch3: sos-collector-pexpect.patch +Patch4: sos-collector-rhhiv-profile.patch +Patch5: sos-collector-rhhi-list.patch %if 0%{?rhel} == 7 BuildRequires: python-devel @@ -40,6 +42,9 @@ is run on the nodes. %patch0 -p1 %patch1 -p1 %patch2 -p1 +%patch3 -p1 +%patch4 -p1 +%patch5 -p1 %build %if 0%{?rhel} == 7 @@ -78,6 +83,13 @@ install -p -m644 man/en/sos-collector.1 ${RPM_BUILD_ROOT}%{_mandir}/man1/ %license LICENSE %changelog +* Fri May 24 2019 Jake Hunsaker - 1.7-5 +- Fix local execution with pexpect +- Fix list output exception + +* Thu May 02 2019 Jake Hunsaker - 1.7-3 +- Backport RHHI-V cluster profile + * Thu Apr 11 2019 Jake Hunsaker - 1.7-2 - Fix 'none' cluster type enablement - Update RHCOS image to RHEL 8 version