aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sos/archive.py293
-rw-r--r--sos/sosreport.py7
2 files changed, 152 insertions, 148 deletions
diff --git a/sos/archive.py b/sos/archive.py
index 3bd3fdbc..376d7069 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,66 +40,126 @@ except ImportError:
class Archive(object):
+ log = logging.getLogger("sos")
+
_name = "unset"
+ # 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 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 FileCacheArchive(Archive):
+
+ _tmp_dir = ""
+ _archive_root = ""
+ _archive_path = ""
+
def __init__(self, name, tmpdir):
self._name = name
self._tmp_dir = tmpdir
- self._archive_path = os.path.join(tmpdir, name)
- os.makedirs(self._archive_path, 0700)
+ 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 make_path(self, name):
+ def dest_path(self, name):
if os.path.isabs(name):
name = name.lstrip(os.sep)
- return (os.path.join(self._archive_path, name))
-
- def prepend(self, src):
- if src:
- name = os.path.split(self._name)[-1]
- renamed = os.path.join(name, src.lstrip(os.sep))
- return renamed
+ return (os.path.join(self._archive_root, name))
- def add_link(self, dest, link_name):
- pass
-
- def makedirs(self, name, mode=0700):
- print "Archive.mkdir('%s')" % name
- os.makedirs(self.make_path(name), mode)
-
- 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 _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)
- self.close()
+ def add_file(self, src, dest=None):
+ if not dest:
+ dest = src
+ dest = self.dest_path(dest)
+ self._check_path(dest)
+ try:
+ 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))
-class TarFileArchive(Archive):
+ 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)
+ 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 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 finalize(self, method):
+ self.log.debug("finalizing archive %s" % self._archive_root)
+ #print "finalizing archive %s" % self._archive_root
+ self._build_archive()
+ 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.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
-
- def get_selinux_context(self, path):
- try:
- (rc, c) = selinux.getfilecon(path)
- return c
- except:
- return None
+ 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
@@ -109,115 +170,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()
+ os.chdir(self._tmp_dir)
+ tar = tarfile.open(self._archive_path,
+ mode="w", format=tarfile.PAX_FORMAT)
+ tar.add(os.path.split(self._name)[1], filter=self.copy_permissions_filter)
+ tar.close()
+ os.chdir(old_pwd)
+ shutil.rmtree(self._archive_root)
+
+ 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)
@@ -226,14 +228,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):
@@ -249,8 +250,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/sosreport.py b/sos/sosreport.py
index 7fa76501..b6b4cbf7 100644
--- a/sos/sosreport.py
+++ b/sos/sosreport.py
@@ -1096,9 +1096,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: