diff options
-rw-r--r-- | sos.spec | 334 | ||||
-rw-r--r-- | sos/archive.py | 301 | ||||
-rw-r--r-- | sos/plugins/__init__.py | 41 | ||||
-rw-r--r-- | sos/plugins/cloudforms.py | 4 | ||||
-rw-r--r-- | sos/plugins/logs.py | 4 | ||||
-rw-r--r-- | sos/plugins/lvm2.py | 4 | ||||
-rw-r--r-- | sos/plugins/rhui.py | 2 | ||||
-rw-r--r-- | sos/plugins/sanlock.py | 2 | ||||
-rw-r--r-- | sos/plugins/satellite.py | 3 | ||||
-rw-r--r-- | sos/plugins/system.py | 2 | ||||
-rw-r--r-- | sos/policies/__init__.py | 4 | ||||
-rw-r--r-- | sos/policies/redhat.py | 5 | ||||
-rw-r--r-- | sos/sosreport.py | 39 |
13 files changed, 544 insertions, 201 deletions
@@ -2,10 +2,10 @@ Summary: A set of tools to gather troubleshooting information from a system Name: sos -Version: 2.3 +Version: 3.0 Release: 1%{?dist} Group: Applications/System -Source0: https://fedorahosted.org/releases/s/o/sos/%{name}-%{version}.tar.gz +Source0: https://github.com/sosreport/sosreport License: GPLv2+ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot BuildArch: noarch @@ -13,6 +13,7 @@ Url: http://fedorahosted.org/sos BuildRequires: python-devel BuildRequires: gettext Requires: libxml2-python +Requires: rpm-python Requires: tar Requires: bzip2 Requires: xz @@ -44,32 +45,313 @@ rm -rf ${RPM_BUILD_ROOT} %{python_sitelib}/* %{_mandir}/man1/* %{_mandir}/man5/* -%doc README.md LICENSE +%doc AUTHORS README.md LICENSE %config(noreplace) %{_sysconfdir}/sos.conf %changelog -* Wed Jul 21 2010 Adam Stokes <ajs at redhat dot com> = 2.3-1 -- Auto update version during build -- Capture plugin tracebacks properly into a separate file - -* Thu May 20 2010 Adam Stokes <ajs at redhat dot com> = 2.2-0 -- Corosync plugin added -- Cluster plugin updated - -* Thu Apr 22 2010 Adam Stokes <ajs at redhat dot com> = 2.1-0 -- Include --help in manpage -- If tmp-dir is defined then put compressed archive there - -* Sun Apr 11 2010 Adam Stokes <ajs at redhat dot com> = 2.0-0 -- Bump release to 2 -- Fix problem where sos generates error on newline in hostname -- Remove references to sysreport* - -* Tue Mar 30 2010 Adam Stokes <ajs at redhat dot com> = 1.9-5 -- Remove references to rh-upload - -* Fri Mar 26 2010 Adam Stokes <ajs at redhat dot com> = 1.9-4 +* Mon Jun 10 2013 Bryn M. Reeves <bmr@redhat.com> = 3.0-1 +- New upstream release + +* Thu May 23 2013 Bryn M. Reeves <bmr@redhat.com> = 2.2-39 +- Always invoke tar with '-f-' option + Resolves: bz966602 + +* Mon Jan 21 2013 Bryn M. Reeves <bmr@redhat.com> = 2.2-38 +- Fix interactive mode regression when --ticket unspecified + Resolves: bz822113 + +* Fri Jan 18 2013 Bryn M. Reeves <bmr@redhat.com> = 2.2-37 +- Fix propagation of --ticket parameter in interactive mode + Resolves: bz822113 + +* Thu Jan 17 2013 Bryn M. Reeves <bmr@redhat.com> = 2.2-36 +- Revert OpenStack patch + Resolves: bz840057 + +* Wed Jan 9 2013 Bryn M. Reeves <bmr@redhat.com> = 2.2-35 +- Report --name and --ticket values as defaults + Resolves: bz822113 +- Fix device-mapper command execution logging + Resolves: bz824378 +- Fix data collection and rename PostreSQL module to pgsql + Resolves: bz852049 + +* Fri Oct 19 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-34 +- Add support for content delivery hosts to RHUI module + Resolves: bz821323 + +* Thu Oct 18 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-33 +- Add Red Hat Update Infrastructure module + Resolves: bz821323 +- Collect /proc/iomem in hardware module + Resolves: bz840975 +- Collect subscription-manager output in general module + Resolves: bz825968 +- Collect rhsm log files in general module + Resolves: bz826312 +- Fix exception in gluster module on non-gluster systems + Resolves: bz849546 +- Fix exception in psql module when dbname is not given + Resolves: bz852049 + +* Wed Oct 17 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-32 +- Collect /proc/pagetypeinfo in memory module + Resolves: bz809727 +- Strip trailing newline from command output + Resolves: bz850433 +- Add sanlock module + Resolves: bz850779 +- Do not collect archived accounting files in psacct module + Resolves: bz850542 +- Call spacewalk-debug from rhn module to collect satellite data + Resolves: bz859142 + +* Mon Oct 15 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-31 +- Avoid calling volume status when collecting gluster statedumps + Resolves: bz849546 +- Use a default report name if --name is empty + Resolves: bz822113 +- Quote tilde characters passed to shell in RPM module + Resolves: bz821005 +- Collect KDC and named configuration in ipa module + Resolves: bz825149 +- Sanitize hostname characters before using as report path + Resolves: bz822174 +- Collect /etc/multipath in device-mapper module + Resolves: bz817093 +- New plug-in for PostgreSQL + Resolves: bz852049 +- Add OpenStack module + Resolves: bz840057 +- Avoid deprecated sysctls in /proc/sys/net + Resolves: bz834594 +- Fix error logging when calling external programs + Resolves: bz824378 +- Use ip instead of ifconfig to generate network interface lists + Resolves: bz833170 + +* Wed May 23 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-29 +- Collect the swift configuration directory in gluster module + Resolves: bz822442 +- Update IPA module and related plug-ins + Resolves: bz812395 + +* Fri May 18 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-28 +- Collect mcelog files in the hardware module + Resolves: bz810702 + +* Wed May 02 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-27 +- Add nfs statedump collection to gluster module + Resolves: bz752549 + +* Tue May 01 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-26 +- Use wildcard to match possible libvirt log paths + Resolves: bz814474 + +* Mon Apr 23 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-25 +- Add forbidden paths for new location of gluster private keys + Resolves: bz752549 + +* Fri Mar 9 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-24 +- Fix katello and aeolus command string syntax + Resolves: bz752666 +- Remove stray hunk from gluster module patch + Resolves: bz784061 + +* Thu Mar 8 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-22 +- Correct aeolus debug invocation in CloudForms module + Resolves: bz752666 +- Update gluster module for gluster-3.3 + Resolves: bz784061 +- Add additional command output to gluster module + Resolves: bz768641 +- Add support for collecting gluster configuration and logs + Resolves: bz752549 + +* Wed Mar 7 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-19 +- Collect additional diagnostic information for realtime systems + Resolves: bz789096 +- Improve sanitization of RHN user and case number in report name + Resolves: bz771393 +- Fix verbose output and debug logging + Resolves: bz782339 +- Add basic support for CloudForms data collection + Resolves: bz752666 +- Add support for Subscription Asset Manager diagnostics + Resolves: bz752670 + +* Tue Mar 6 2012 Bryn M. Reeves <bmr@redhat.com> = 2.2-18 +- Collect fence_virt.conf in cluster module + Resolves: bz760995 +- Fix collection of /proc/net directory tree + Resolves: bz730641 +- Gather output of cpufreq-info when present + Resolves: bz760424 +- Fix brctl showstp output when bridges contain multiple interfaces + Resolves: bz751273 +- Add /etc/modprobe.d to kernel module + Resolves: bz749919 +- Ensure relative symlink targets are correctly handled when copying + Resolves: bz782589 +- Fix satellite and proxy package detection in rhn plugin + Resolves: bz749262 +- Collect stderr output from external commands + Resolves: bz739080 +- Collect /proc/cgroups in the cgroups module + Resolve: bz784874 +- Collect /proc/irq in the kernel module + Resolves: bz784862 +- Fix installed-rpms formatting for long package names + Resolves: bz767827 +- Add symbolic links for truncated log files + Resolves: bz766583 +- Collect non-standard syslog and rsyslog log files + Resolves: bz771501 +- Use correct paths for tomcat6 in RHN module + Resolves: bz749279 +- Obscure root password if present in anacond-ks.cfg + Resolves: bz790402 +- Do not accept embedded forward slashes in RHN usernames + Resolves: bz771393 +- Add new sunrpc module to collect rpcinfo for gluster systems + Resolves: bz784061 + +* Tue Nov 1 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-17 +- Do not collect subscription manager keys in general plugin + Resolves: bz750607 + +* Fri Sep 23 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-16 +- Fix execution of RHN hardware.py from hardware plugin + Resolves: bz736718 +- Fix hardware plugin to support new lsusb path + Resolves: bz691477 + +* Fri Sep 09 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-15 +- Fix brctl collection when a bridge contains no interfaces + Resolves: bz697899 +- Fix up2dateclient path in hardware plugin + Resolves: bz736718 + +* Mon Aug 15 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-14 +- Collect brctl show and showstp output + Resolves: bz697899 +- Collect nslcd.conf in ldap plugin + Resolves: bz682124 + +* Sun Aug 14 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-11 +- Truncate files that exceed specified size limit + Resolves: bz683219 +- Add support for collecting Red Hat Subscrition Manager configuration + Resolves: bz714293 +- Collect /etc/init on systems using upstart + Resolves: bz694813 +- Don't strip whitespace from output of external programs + Resolves: bz713449 +- Collect ipv6 neighbour table in network module + Resolves: bz721163 +- Collect basic cgroups configuration data + Resolves: bz729455 + +* Sat Aug 13 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-10 +- Fix collection of data from LVM2 reporting tools in devicemapper plugin + Resolves: bz704383 +- Add /proc/vmmemctl collection to vmware plugin + Resolves: bz709491 + +* Fri Aug 12 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-9 +- Collect yum repository list by default + Resolves: bz600813 +- Add basic Infiniband plugin + Resolves: bz673244 +- Add plugin for scsi-target-utils iSCSI target + Resolves: bz677124 +- Fix autofs plugin LC_ALL usage + Resolves: bz683404 +- Fix collection of lsusb and add collection of -t and -v outputs + Resolves: bz691477 +- Extend data collection by qpidd plugin + Resolves: bz726360 +- Add ethtool pause, coalesce and ring (-a, -c, -g) options to network plugin + Resolves: bz726427 + +* Thu Apr 07 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-8 +- Use sha256 for report digest when operating in FIPS mode + Resolves: bz689387 + +* Tue Apr 05 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-7 +- Fix parted and dumpe2fs output on s390 + Resolves: bz622784 + +* Fri Feb 25 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-6 +- Fix collection of chkconfig output in startup.py + Resolves: bz659467 +- Collect /etc/dhcp in dhcp.py plugin + Resolves: bz676522 +- Collect dmsetup ls --tree output in devicemapper.py + Resolves: bz675559 +- Collect lsblk output in filesys.py + Resolves: bz679433 + +* Thu Feb 24 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-4 +- Fix collection of logs and config files in sssd.py + Resolves: bz624162 +- Add support for collecting entitlement certificates in rhn.py + Resolves: bz678665 + +* Thu Feb 03 2011 Bryn M. Reeves <bmr@redhat.com> = 2.2-3 +- Fix cluster plugin dlm lockdump for el6 + Resolves: bz622407 +- Add sssd plugin to collect configuration and logs + Resolves: bz624162 +- Collect /etc/anacrontab in system plugin + Resolves: bz622527 +- Correct handling of redhat-release for el6 + Resolves: bz622528 + +* Thu Jul 29 2010 Adam Stokes <ajs at redhat dot com> = 2.2-2 +- Resolves: bz582259 +- Resolves: bz585942 +- Resolves: bz584253 +- Resolves: bz581817 + +* Thu Jun 10 2010 Adam Stokes <ajs at redhat dot com> = 2.2-0 +- Resolves: bz581921 +- Resolves: bz584253 +- Resolves: bz562651 +- Resolves: bz566170 +- Resolves: bz586450 +- Resolves: bz588223 +- Resolves: bz559737 +- Resolves: bz586405 +- Resolves: bz598978 +- Resolves: bz584763 + +* Wed Apr 28 2010 Adam Stokes <ajs at redhat dot com> = 2.1-0 +- Resolves: bz585923 +- Resolves: bz585942 +- Resolves: bz586409 +- Resolves: bz586389 +- Resolves: bz548096 +- Resolves: bz557828 +- Resolves: bz563637 +- Resolves: bz584253 +- Resolves: bz462823 +- Resolves: bz528881 +- Resolves: bz566170 +- Resolves: bz578787 +- Resolves: bz581817 +- Resolves: bz581826 +- Resolves: bz584695 +- Resolves: bz568637 +- Resolves: bz584767 +- Resolves: bz586370 + +* Mon Apr 12 2010 Adam Stokes <ajs at redhat dot com> = 2.0-0 +- Resolves: bz580015 + +* Tue Mar 30 2010 Adam Stokes <ajs at redhat dot com> = 1.9-3 - fix setup.py to autocompile translations and man pages +- rebase 1.9 * Fri Mar 19 2010 Adam Stokes <ajs at redhat dot com> = 1.9-2 - updated translations @@ -126,10 +408,6 @@ rm -rf ${RPM_BUILD_ROOT} * Wed Feb 25 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.8-10 - Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild -* Wed Jan 21 2009 Adam Stokes <ajs at redhat dot com> - 1.8-9 -- Resolves: bz436053 /usr/share/sos is not owned by any package -- Resolves: bz434626 Wrong directory structure for translations - * Mon Dec 29 2008 Adam Stokes <ajs at redhat dot com> - 1.8-5 - removed source defines as python manifest handles this diff --git a/sos/archive.py b/sos/archive.py index f1b0b057..42916606 100644 --- a/sos/archive.py +++ b/sos/archive.py @@ -22,6 +22,7 @@ import tarfile import zipfile import shutil import logging +import shutil import shlex import re # required for compression callout (FIXME: move to policy?) @@ -39,51 +40,143 @@ except ImportError: class Archive(object): + log = logging.getLogger("sos") + _name = "unset" - def prepend(self, src): - if src: - name = os.path.split(self._name)[-1] - renamed = os.path.join(name, src.lstrip(os.sep)) - return renamed + # this is our contract to clients of the Archive class hierarchy. + # All sub-classes need to implement these methods (or inherit concrete + # implementations from a parent class. + def add_file(self, src, dest=None): + raise NotImplementedError + + def add_string(self, content, dest): + raise NotImplementedError + + def add_link(self, source, link_name): + raise NotImplementedError + + def add_dir(self, path): + raise NotImplementedError + + def get_tmp_dir(self): + """Return a temporary directory that clients of the archive may + use to write content to. The content of the path is guaranteed + to be included in the generated archive.""" + raise NotImplementedError - def add_link(self, dest, link_name): + def cleanup(self): + """Clean up any temporary resources used by an Archive class.""" pass - def compress(self, method): - """Compress an archive object via method. ZIP archives are ignored. If - method is automatic then the following technologies are tried in order: xz, - bz2 and gzip""" + def finalize(self, method): + """Finalize an archive object via method. This may involve creating + An archive that is subsequently compressed or simply closing an + archive that supports in-line handling. If method is automatic then + the following technologies are tried in order: xz, bz2 and gzip""" self.close() -class TarFileArchive(Archive): +class FileCacheArchive(Archive): - def __init__(self, name): + _tmp_dir = "" + _archive_root = "" + _archive_path = "" + + def __init__(self, name, tmpdir): self._name = name - self._suffix = "tar" - self.tarfile = tarfile.open(self.name(), - mode="w", format=tarfile.PAX_FORMAT) - - # this can be used to set permissions if using the - # tarfile.add() interface to add directory trees. - def copy_permissions_filter(self, tar_info): - orig_path = tar_info.name[len(os.path.split(self._name)[-1]):] - fstat = os.stat(orig_path) - context = self.get_selinux_context(orig_path) - if(context): - tar_info.pax_headers['RHT.security.selinux'] = context - self.set_tar_info_from_stat(tar_info,fstat) - return tar_info + self._tmp_dir = tmpdir + self._archive_root = os.path.join(tmpdir, name) + os.makedirs(self._archive_root, 0700) + self.log.debug("initialised empty FileCacheArchive at %s" + % self._archive_root) + + def dest_path(self, name): + if os.path.isabs(name): + name = name.lstrip(os.sep) + return (os.path.join(self._archive_root, name)) + + def _check_path(self, dest): + dest_dir = os.path.split(dest)[0] + if not dest_dir: + return + if not os.path.isdir(dest_dir): + self._makedirs(dest_dir) - def get_selinux_context(self, path): + def add_file(self, src, dest=None): + if not dest: + dest = src + dest = self.dest_path(dest) + self._check_path(dest) try: - (rc, c) = selinux.getfilecon(path) - return c - except: - return None + shutil.copy(src, dest) + shutil.copystat(src, dest) + stat = os.stat(src) + os.chown(dest, stat.st_uid, stat.st_gid) + except IOError as e: + self.log.info("caught IO error copying %s" % src) + self.log.debug("added %s to FileCacheArchive %s" + % (src, self._archive_root)) + + def add_string(self, content, dest): + src = dest + dest = self.dest_path(dest) + self._check_path(dest) + f = open(dest, 'w') + f.write(content) + if os.path.exists(src): + shutil.copystat(src, dest) + self.log.debug("added string at %s to FileCacheArchive %s" + % (src, self._archive_root)) + + def add_link(self, source, link_name): + dest = self.dest_path(link_name) + self._check_path(dest) + if not os.path.exists(dest): + os.symlink(source, dest) + self.log.debug("added symlink at %s to %s in FileCacheArchive %s" + % (dest, source, self._archive_root)) + + def add_dir(self, path): + self.makedirs(path) + + def _makedirs(self, path, mode=0700): + os.makedirs(path, mode) + + def get_tmp_dir(self): + return self._archive_root + + def makedirs(self, path, mode=0700): + self._makedirs(self.dest_path(path)) + self.log.debug("created directory at %s in FileCacheArchive %s" + % (path, self._archive_root)) + + def open_file(self, path): + path = self.dest_path(path) + return open(path, "r") + + def cleanup(self): + shutil.rmtree(self._archive_root) + + def finalize(self, method): + self.log.debug("finalizing archive %s" % self._archive_root) + self._build_archive() + self.cleanup() + self.log.debug("built archive at %s (size=%d)" % (self._archive_path, + os.stat(self._archive_path).st_size)) + return self._compress() + +class TarFileArchive(FileCacheArchive): + + method = None + _with_selinux_context = False + + def __init__(self, name, tmpdir): + super(TarFileArchive, self).__init__(name, tmpdir) + self._suffix = "tar" + self._archive_path = os.path.join(tmpdir, self.name()) - def set_tar_info_from_stat(self, tar_info, fstat, mode=None): + def set_tarinfo_from_stat(self, tar_info, fstat, mode=None): tar_info.mtime = fstat.st_mtime tar_info.pax_headers['atime'] = "%.9f" % fstat.st_atime tar_info.pax_headers['ctime'] = "%.9f" % fstat.st_ctime @@ -94,115 +187,56 @@ class TarFileArchive(Archive): tar_info.uid = fstat.st_uid tar_info.gid = fstat.st_gid - def name(self): - return "%s.%s" % (self._name, self._suffix) - - def add_parent(self, path): - path = os.path.split(path)[0] - if path == '': - return - self.add_file(path) - - def add_file(self, src, dest=None): - if dest: - dest = self.prepend(dest) - else: - dest = self.prepend(src) - - if dest in self.tarfile.getnames(): - return - if src != '/': - self.add_parent(src) - - tar_info = tarfile.TarInfo(name=dest) - - if os.path.isdir(src): - tar_info.type = tarfile.DIRTYPE - fileobj = None - else: - try: - fp = open(src, 'rb') - content = fp.read() - fp.close() - except: - # files with read permissions that cannot be read may exist - # in /proc, /sys and other virtual file systems. - content = "" - tar_info.size = len(content) - fileobj = StringIO(content) - - # FIXME: handle this at a higher level? - if src.startswith("/sys/") or src.startswith ("/proc/"): - context = None - else: - context = self.get_selinux_context(src) - if context: - tar_info.pax_headers['RHT.security.selinux'] = context + # this can be used to set permissions if using the + # tarfile.add() interface to add directory trees. + def copy_permissions_filter(self, tarinfo): + orig_path = tarinfo.name[len(os.path.split(self._name)[-1]):] + if not orig_path: + orig_path = self._archive_root try: - fstat = os.stat(src) - if os.path.isdir(src) and not (fstat.st_mode & 000200): - # directories not writable by their owner are a world of pain - # in tar archives. Do not allow them (see Issue #85). - mode = fstat.st_mode | 000200 - else: - mode = None - self.set_tar_info_from_stat(tar_info,fstat, mode) - self.tarfile.addfile(tar_info, fileobj) - except Exception as e: - raise e - finally: - mode = None - - def add_string(self, content, dest): - fstat = None - if os.path.exists(dest): - fstat = os.stat(dest) - dest = self.prepend(dest) - tar_info = tarfile.TarInfo(name=dest) - tar_info.size = len(content) - if fstat: - context = self.get_selinux_context(dest) - if context: - tar_info.pax_headers['RHT.security.selinux'] = context - self.set_tar_info_from_stat(tar_info, fstat) - else: - tar_info.mtime = time.time() - self.tarfile.addfile(tar_info, StringIO(content)) - - def add_link(self, dest, link_name): - tar_info = tarfile.TarInfo(name=self.prepend(link_name)) - tar_info.type = tarfile.SYMTYPE - tar_info.linkname = dest - tar_info.mtime = time.time() - self.tarfile.addfile(tar_info, None) + fstat = os.stat(orig_path) + except OSError: + return tarinfo + if self._with_selinux_context: + context = self.get_selinux_context(orig_path) + if(context): + tarinfo.pax_headers['RHT.security.selinux'] = context + self.set_tarinfo_from_stat(tarinfo,fstat) + return tarinfo - def open_file(self, name): + def get_selinux_context(self, path): try: - self.tarfile.close() - self.tarfile = tarfile.open(self.name(), mode="r") - name = self.prepend(name) - file_obj = self.tarfile.extractfile(name) - file_obj = StringIO(file_obj.read()) - return file_obj - finally: - self.tarfile.close() - self.tarfile = tarfile.open(self.name(), mode="a") - - def close(self): - self.tarfile.close() + (rc, c) = selinux.getfilecon(path) + return c + except: + return None - def compress(self, method): - super(TarFileArchive, self).compress(method) + def name(self): + return "%s.%s" % (self._name, self._suffix) + def _build_archive(self): + old_pwd = os.getcwd() + old_umask = os.umask(0077) + os.chdir(self._tmp_dir) + tar = tarfile.open(self._archive_path, mode="w") + tar.add(os.path.split(self._name)[1], filter=self.copy_permissions_filter) + tar.close() + os.umask(old_umask) + os.chdir(old_pwd) + + def _compress(self): methods = ['xz', 'bzip2', 'gzip'] - - if method in methods: - methods = [method] + if self.method in methods: + methods = [self.method] last_error = Exception("compression failed for an unknown reason") log = logging.getLogger('sos') for cmd in methods: + suffix = "." + cmd.replace('ip', '') + # use fast compression if using xz or bz2 + if cmd != "gzip": + cmd = "%s -1" % cmd try: command = shlex.split("%s %s" % (cmd,self.name())) p = Popen(command, stdout=PIPE, stderr=PIPE, bufsize=-1) @@ -211,14 +245,13 @@ class TarFileArchive(Archive): log.info(stdout) if stderr: log.error(stderr) - self._suffix += "." + cmd.replace('ip', '') + self._suffix += suffix return self.name() except Exception, e: last_error = e else: raise last_error - class ZipFileArchive(Archive): def __init__(self, name): @@ -234,8 +267,8 @@ class ZipFileArchive(Archive): def name(self): return "%s.zip" % self._name - def compress(self, method): - super(ZipFileArchive, self).compress(method) + def finalize(self, method): + super(ZipFileArchive, self).finalize(method) return self.name() def add_file(self, src, dest=None): diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py index 1a28a1e3..70711a3e 100644 --- a/sos/plugins/__init__.py +++ b/sos/plugins/__init__.py @@ -422,42 +422,43 @@ class Plugin(object): except Exception: return default - def add_copy_spec_limit(self, fname, sizelimit=None, sub=None): + def add_copy_spec_limit(self, copyspec, sizelimit=None, sub=None): """Add a file or glob but limit it to sizelimit megabytes. If fname is a single file the file will be tailed to meet sizelimit. If the first file in a glob is too large it will be tailed to meet the sizelimit. """ - if not (fname and len(fname)): + if not (copyspec and len(copyspec)): return False - files = glob.glob(fname) + files = glob.glob(copyspec) files.sort() if len(files) == 0: return - cursize = 0 + current_size = 0 limit_reached = False sizelimit *= 1024 * 1024 # in MB - flog = None + _file = None - for flog in files: - cursize += os.stat(flog)[ST_SIZE] - if sizelimit and cursize > sizelimit: + for _file in files: + current_size += os.stat(_file)[ST_SIZE] + if sizelimit and current_size > sizelimit: limit_reached = True break - self.add_copy_spec(flog, sub) + self.add_copy_spec(_file, sub) - if flog == files[0] and limit_reached: - flog_name = flog + if limit_reached: + file_name = _file if sub: old, new = sub - flog_name = flog.replace(old, new) - strfile = flog_name.replace(os.path.sep, ".") + ".tailed" - self.add_string_as_file(tail(flog, sizelimit), strfile) + file_name = _file.replace(old, new) + if file_name[0] == os.sep: + file_name = file_name.lstrip(os.sep) + strfile = file_name.replace(os.path.sep, ".") + ".tailed" + self.add_string_as_file(tail(_file, sizelimit), strfile) self.archive.add_link(os.path.join( - os.path.relpath('/', os.path.dirname(flog)), 'sos_strings', - self.name(), strfile), flog) - + os.path.relpath('/', os.path.dirname(_file)), 'sos_strings', + self.name(), strfile), _file) def add_copy_specs(self, copyspecs, sub=None): for copyspec in copyspecs: @@ -495,6 +496,12 @@ class Plugin(object): """Run a program and collect the output""" self.collect_cmds.append( (exe, suggest_filename, root_symlink, timeout) ) + def get_cmd_dir(self): + """Return a directory into which this module should store + collected command output""" + return os.path.join(self.archive.get_tmp_dir(), + 'sos_commands', self.name()) + def file_grep(self, regexp, *fnames): """Returns lines matched in fnames, where fnames can either be pathnames to files to grep through or open file objects to grep through diff --git a/sos/plugins/cloudforms.py b/sos/plugins/cloudforms.py index 1dc0eae3..d72dc42b 100644 --- a/sos/plugins/cloudforms.py +++ b/sos/plugins/cloudforms.py @@ -30,9 +30,9 @@ class Cloudforms(Plugin, RedHatPlugin): katello_debug = "/usr/share/katello/script/katello-debug" aeolus_debug = "aeolus-debug" if os.path.isfile(katello_debug): - katello_debug_path = os.path.join(self.commons['dstroot'],"katello-debug") + katello_debug_path = os.path.join(self.get_cmd_dir(), "katello-debug") self.add_cmd_output("%s --notar -d %s" % (katello_debug, katello_debug_path)) if os.path.isfile(aeolus_debug): - aeolus_debug_path = os.path.join(self.commons['dstroot'],"aeolus-debug") + aeolus_debug_path = os.path.join(self.get_cmd_dir(), "aeolus-debug") self.add_cmd_output("%s --notar -d %s" % (aeolus_debug, aeolus_debug_path)) diff --git a/sos/plugins/logs.py b/sos/plugins/logs.py index 28db0e47..8359e063 100644 --- a/sos/plugins/logs.py +++ b/sos/plugins/logs.py @@ -32,9 +32,9 @@ class Logs(Plugin): self.add_copy_specs([ "/etc/syslog.conf", "/etc/rsyslog.conf" - )] + ]) - self.limit = self.get_option("syslogsize") + self.limit = self.get_option("logsize") self.add_copy_spec_limit("/var/log/boot*", sizelimit = self.limit) if self.get_option('all_logs'): diff --git a/sos/plugins/lvm2.py b/sos/plugins/lvm2.py index 8fb1843b..4e55f27f 100644 --- a/sos/plugins/lvm2.py +++ b/sos/plugins/lvm2.py @@ -29,8 +29,8 @@ class Lvm2(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): """Collects an lvmdump in standard format with optional metadata archives for each physical volume present. """ - cmd = "lvmdump -d '%s'" % os.path.join(self.commons['dstroot'], - "lvmdump") + lvmdump_cmd = "lvmdump -d '%s'" + cmd = lvmdump_cmd % os.path.join(self.get_cmd_dir(), "lvmdump") if self.get_option('lvmdump-a'): cmd += " -a" self.add_cmd_output(cmd) diff --git a/sos/plugins/rhui.py b/sos/plugins/rhui.py index 69f3f22a..9bf29590 100644 --- a/sos/plugins/rhui.py +++ b/sos/plugins/rhui.py @@ -32,7 +32,7 @@ class Rhui(Plugin, RedHatPlugin): else: cds = "" - rhui_debug_dst_path = os.path.join(self.commons['dstroot'], + rhui_debug_dst_path = os.path.join(self.get_cmd_dir(), self.commons['cmddir'], self.name()) try: os.mkdir(rhui_debug_dst_path) diff --git a/sos/plugins/sanlock.py b/sos/plugins/sanlock.py index 67474af8..232dd007 100644 --- a/sos/plugins/sanlock.py +++ b/sos/plugins/sanlock.py @@ -32,5 +32,5 @@ class RedHatSANLock(SANLock, RedHatPlugin): files = [ "/etc/sysconfig/sanlock" ] def setup(self): - super(RedHatSanlock, self).setup() + super(RedHatSANLock, self).setup() self.add_copy_spec("/etc/sysconfig/sanlock") diff --git a/sos/plugins/satellite.py b/sos/plugins/satellite.py index b2d3cde3..75c047fc 100644 --- a/sos/plugins/satellite.py +++ b/sos/plugins/satellite.py @@ -78,8 +78,7 @@ class Satellite(Plugin, RedHatPlugin): "/etc/tomcat6/", "/var/log/tomcat6/"]) if os.path.exists("spacewalk-debug"): self.add_cmd_output("spacewalk-debug --dir %s" - % os.path.join(self.commons['dstroot'], - "sos_commands/rhn")) + % os.path.join(self.get_cmd_dir())) if self.proxy: self.add_copy_specs(["/etc/squid", "/var/log/squid"]) diff --git a/sos/plugins/system.py b/sos/plugins/system.py index 73c51c32..a7e3eda1 100644 --- a/sos/plugins/system.py +++ b/sos/plugins/system.py @@ -21,7 +21,7 @@ class System(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin): plugin_name = "system" def setup(self): - self.add_copy_specs("/proc/sys") + self.add_copy_spec("/proc/sys") self.add_forbidden_path( "/proc/sys/net/ipv6/neigh/*/retrans_time") self.add_forbidden_path( diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py index 522aaff5..8eff9935 100644 --- a/sos/policies/__init__.py +++ b/sos/policies/__init__.py @@ -187,6 +187,10 @@ No changes will be made to system configuration. self.report_name += "." + self.ticket_number return "sosreport-%s-%s" % (self.report_name, time.strftime("%Y%m%d%H%M%S")) + def get_tmp_dir(self, opt_tmp_dir): + if not opt_tmp_dir: + return tempfile.gettempdir() + def validatePlugin(self, plugin_class): """ Verifies that the plugin_class should execute under this policy diff --git a/sos/policies/redhat.py b/sos/policies/redhat.py index d9dc6b3e..c3740dce 100644 --- a/sos/policies/redhat.py +++ b/sos/policies/redhat.py @@ -35,6 +35,7 @@ class RedHatPolicy(LinuxPolicy): distro = "Red Hat" vendor = "Red Hat" vendor_url = "http://www.redhat.com/" + _tmp_dir = "/var/tmp" def __init__(self): super(RedHatPolicy, self).__init__() @@ -81,6 +82,10 @@ class RedHatPolicy(LinuxPolicy): ret.append(int(runlevel)) return ret + def get_tmp_dir(self, opt_tmp_dir): + if not opt_tmp_dir: + return self._tmp_dir + def get_local_name(self): return self.host_name() diff --git a/sos/sosreport.py b/sos/sosreport.py index df9be1ee..908cbede 100644 --- a/sos/sosreport.py +++ b/sos/sosreport.py @@ -495,7 +495,7 @@ class SoSOptions(object): help="specify alternate configuration file") parser.add_option("--tmp-dir", action="store", dest="tmp_dir", - help="specify alternate temporary directory", default=tempfile.gettempdir()) + help="specify alternate temporary directory", default=None) parser.add_option("--report", action="store_true", dest="report", help="Enable HTML/XML reporting", default=False) @@ -517,6 +517,8 @@ class SoSReport(object): self.all_options = deque() self.xml_report = XmlReport() self.global_plugin_options = {} + self.archive = None + self.tempfile_util = None try: import signal @@ -527,11 +529,12 @@ class SoSReport(object): #self.opts = self.parse_options(args)[0] self.opts = SoSOptions(args) - self.tempfile_util = TempFileUtil(tmp_dir=self.opts.tmp_dir) self._set_debug() self._read_config() self.policy = sos.policies.load() self._is_root = self.policy.is_root() + self.tmpdir = self.policy.get_tmp_dir(self.opts.tmp_dir) + self.tempfile_util = TempFileUtil(self.tmpdir) self._set_directories() def print_header(self): @@ -542,7 +545,7 @@ class SoSReport(object): 'cmddir': self.cmddir, 'logdir': self.logdir, 'rptdir': self.rptdir, - 'tmpdir': self.opts.tmp_dir, + 'tmpdir': self.tmpdir, 'soslog': self.soslog, 'proflog' : self.proflog, 'policy': self.policy, @@ -558,15 +561,21 @@ class SoSReport(object): def _set_archive(self): if self.opts.compression_type not in ('auto', 'zip', 'bzip2', 'gzip', 'xz'): - raise Exception("Invalid compression type specified. Options are: auto, zip, bzip2, gzip and xz") - archive_name = os.path.join(self.opts.tmp_dir,self.policy.get_archive_name()) + raise Exception("Invalid compression type specified. Options are:" + + "auto, zip, bzip2, gzip and xz") + archive_name = os.path.join(self.tmpdir,self.policy.get_archive_name()) if self.opts.compression_type == 'auto': auto_archive = self.policy.preferred_archive_name() - self.archive = auto_archive(archive_name) + self.archive = auto_archive(archive_name, self.tmpdir) elif self.opts.compression_type == 'zip': - self.archive = ZipFileArchive(archive_name) + self.archive = ZipFileArchive(archive_name, self.tmpdir) else: - self.archive = TarFileArchive(archive_name) + self.archive = TarFileArchive(archive_name, self.tmpdir) + + def _make_archive_paths(self): + self.archive.makedirs(self.cmddir, 0755) + self.archive.makedirs(self.logdir, 0755) + self.archive.makedirs(self.rptdir, 0755) def _set_directories(self): self.cmddir = 'sos_commands' @@ -914,6 +923,7 @@ class SoSReport(object): try: self.policy.pre_work() self._set_archive() + self._make_archive_paths() except Exception, e: import traceback traceback.print_exc(e) @@ -1089,9 +1099,12 @@ class SoSReport(object): # compression could fail for a number of reasons try: - final_filename = self.archive.compress(self.opts.compression_type) + final_filename = self.archive.finalize(self.opts.compression_type) except: - return False + if self.opts.debug: + raise + else: + return False # automated submission will go here if not self.opts.upload: @@ -1145,7 +1158,11 @@ class SoSReport(object): self.version() return self.final_work() - except SystemExit: + except (SystemExit, KeyboardInterrupt): + if self.archive: + self.archive.cleanup() + if self.tempfile_util: + self.tempfile_util.clean() return False def main(args): |