Blame SOURCES/sos-bz1185990-sos-inside-container.patch

c81b6a
From cb3d265849771f7e53b0587196930328005414e0 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 19 Jan 2015 18:54:09 +0000
c81b6a
Subject: [PATCH 01/38] [sosreport] add --sysroot option
c81b6a
c81b6a
Add a --sysroot=SYSROOT option to specify that the root file system to
c81b6a
be inspected is mounted at SYSROOT.
c81b6a
c81b6a
This allows basic support for container environments where sos is
c81b6a
running in a container and inspecting the containing host and its
c81b6a
environment ('superspection').
c81b6a
c81b6a
For this to work currently the following conditions must be met:
c81b6a
c81b6a
- sos is sufficiently privileged to read and search relevant file
c81b6a
  system paths within SYSROOT
c81b6a
c81b6a
- sos must share the PID and network namespace of the target host
c81b6a
c81b6a
- binaries called by sos must be present and executable in the
c81b6a
  SYSROOT inherited by sos. If PATH includes paths inside SYSROOT
c81b6a
  appropriate values must be set for LD_LIBRARY_PATH to allow
c81b6a
  shared executables to be linked.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/sosreport.py | 16 +++++++++++++++-
c81b6a
 1 file changed, 15 insertions(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/sosreport.py b/sos/sosreport.py
c81b6a
index 9a0cf6c..47eddc9 100644
c81b6a
--- a/sos/sosreport.py
c81b6a
+++ b/sos/sosreport.py
c81b6a
@@ -529,6 +529,17 @@ class SoSOptions(object):
c81b6a
         self._report = value
c81b6a
 
c81b6a
     @property
c81b6a
+    def sysroot(self):
c81b6a
+        if self._options is not None:
c81b6a
+            return self._options.sysroot
c81b6a
+        return self._sysroot
c81b6a
+
c81b6a
+    @sysroot.setter
c81b6a
+    def sysroot(self, value):
c81b6a
+        self._check_options_initialized()
c81b6a
+        self._sysroot = value
c81b6a
+
c81b6a
+    @property
c81b6a
     def compression_type(self):
c81b6a
         if self._options is not None:
c81b6a
             return self._options.compression_type
c81b6a
@@ -615,6 +626,9 @@ class SoSOptions(object):
c81b6a
         parser.add_option("--no-report", action="store_true",
c81b6a
                           dest="report",
c81b6a
                           help="Disable HTML/XML reporting", default=False)
c81b6a
+        parser.add_option("-s", "--sysroot", action="store", dest="sysroot",
c81b6a
+                          help="system root directory path (default='/')",
c81b6a
+                          default="/")
c81b6a
         parser.add_option("-z", "--compression-type", dest="compression_type",
c81b6a
                           help="compression technology to use [auto, "
c81b6a
                                "gzip, bzip2, xz] (default=auto)",
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 3eed62e132f67930bb1cf5c9eaa5927083011043 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Fri, 23 Jan 2015 15:24:00 +0000
c81b6a
Subject: [PATCH 02/38] [plugins] propagate sysroot to Plugin via commons
c81b6a
c81b6a
Although plugins should generally be unaware that they are being
c81b6a
run with an alternate sysroot the generic plugin IO code must
c81b6a
peform the appropriate path prefixing when sysroot is not '/'.
c81b6a
c81b6a
Propagate sysroot to plugin classes via the commons dictionary.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/sosreport.py | 6 ++++++
c81b6a
 1 file changed, 6 insertions(+)
c81b6a
c81b6a
diff --git a/sos/sosreport.py b/sos/sosreport.py
c81b6a
index 47eddc9..580b5bd 100644
c81b6a
--- a/sos/sosreport.py
c81b6a
+++ b/sos/sosreport.py
c81b6a
@@ -651,6 +651,7 @@ class SoSReport(object):
c81b6a
         self.archive = None
c81b6a
         self.tempfile_util = None
c81b6a
         self._args = args
c81b6a
+        self.sysroot = "/"
c81b6a
 
c81b6a
         try:
c81b6a
             import signal
c81b6a
@@ -681,6 +682,10 @@ class SoSReport(object):
c81b6a
         self.tempfile_util = TempFileUtil(self.tmpdir)
c81b6a
         self._set_directories()
c81b6a
 
c81b6a
+        # set alternate system root directory
c81b6a
+        if self.opts.sysroot:
c81b6a
+            self.sysroot = self.opts.sysroot
c81b6a
+
c81b6a
     def print_header(self):
c81b6a
         self.ui_log.info("\n%s\n" % _("sosreport (version %s)" %
c81b6a
                          (__version__,)))
c81b6a
@@ -693,6 +698,7 @@ class SoSReport(object):
c81b6a
             'tmpdir': self.tmpdir,
c81b6a
             'soslog': self.soslog,
c81b6a
             'policy': self.policy,
c81b6a
+            'sysroot': self.sysroot,
c81b6a
             'verbosity': self.opts.verbosity,
c81b6a
             'xmlreport': self.xml_report,
c81b6a
             'cmdlineopts': self.opts,
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From b1f3b3373e8ef3e94238760a3e7e78d95c564260 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Fri, 23 Jan 2015 23:17:34 +0000
c81b6a
Subject: [PATCH 03/38] [plugins] prefix target paths with self.sysroot
c81b6a
c81b6a
Prefix copyspecs with self.sysroot when using an alternate root
c81b6a
path. Prefixes are applied before expanding copyspecs and the
c81b6a
prefixed paths are stored as the 'srcpath' attribute in the
c81b6a
archive. Destination paths in the report archive do not include
c81b6a
the prefix.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 50 +++++++++++++++++++++++++++++++++++--------
c81b6a
 tests/option_tests.py   |  3 ++-
c81b6a
 tests/plugin_tests.py   | 57 ++++++++++++++++++++++++++++++++-----------------
c81b6a
 3 files changed, 81 insertions(+), 29 deletions(-)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index 413ee73..790338b 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -101,6 +101,7 @@ class Plugin(object):
c81b6a
     files = ()
c81b6a
     archive = None
c81b6a
     profiles = ()
c81b6a
+    sysroot = '/'
c81b6a
 
c81b6a
     def __init__(self, commons):
c81b6a
         if not getattr(self, "option_list", False):
c81b6a
@@ -117,6 +118,7 @@ class Plugin(object):
c81b6a
         self.copy_paths = set()
c81b6a
         self.copy_strings = []
c81b6a
         self.collect_cmds = []
c81b6a
+        self.sysroot = commons['sysroot']
c81b6a
 
c81b6a
         self.soslog = self.commons['soslog'] if 'soslog' in self.commons \
c81b6a
             else logging.getLogger('sos')
c81b6a
@@ -154,6 +156,19 @@ class Plugin(object):
c81b6a
     def policy(self):
c81b6a
         return self.commons["policy"]
c81b6a
 
c81b6a
+    def join_sysroot(self, path):
c81b6a
+        if path[0] == os.sep:
c81b6a
+            path = path[1:]
c81b6a
+        return os.path.join(self.sysroot, path)
c81b6a
+
c81b6a
+    def strip_sysroot(self, path):
c81b6a
+        if path.startswith(self.sysroot):
c81b6a
+            return path[len(self.sysroot):]
c81b6a
+        return path
c81b6a
+
c81b6a
+    def use_sysroot(self):
c81b6a
+        return self.sysroot != os.path.abspath(os.sep)
c81b6a
+
c81b6a
     def is_installed(self, package_name):
c81b6a
         '''Is the package $package_name installed?'''
c81b6a
         return self.policy().pkg_by_name(package_name) is not None
c81b6a
@@ -207,6 +222,7 @@ class Plugin(object):
c81b6a
         '''
c81b6a
         try:
c81b6a
             path = self._get_dest_for_srcpath(srcpath)
c81b6a
+            self._log_debug("substituting scrpath '%s'" % srcpath)
c81b6a
             self._log_debug("substituting '%s' for '%s' in '%s'"
c81b6a
                             % (subst, regexp, path))
c81b6a
             if not path:
c81b6a
@@ -257,8 +273,9 @@ class Plugin(object):
c81b6a
         self._log_debug("copying link '%s' pointing to '%s' with isdir=%s"
c81b6a
                         % (srcpath, linkdest, os.path.isdir(absdest)))
c81b6a
 
c81b6a
+        dstpath = self.strip_sysroot(srcpath)
c81b6a
         # use the relative target path in the tarball
c81b6a
-        self.archive.add_link(reldest, srcpath)
c81b6a
+        self.archive.add_link(reldest, dstpath)
c81b6a
 
c81b6a
         if os.path.isdir(absdest):
c81b6a
             self._log_debug("link '%s' is a directory, skipping..." % linkdest)
c81b6a
@@ -277,7 +294,7 @@ class Plugin(object):
c81b6a
         self._do_copy_path(absdest)
c81b6a
 
c81b6a
         self.copied_files.append({'srcpath': srcpath,
c81b6a
-                                  'dstpath': srcpath,
c81b6a
+                                  'dstpath': dstpath,
c81b6a
                                   'symlink': "yes",
c81b6a
                                   'pointsto': linkdest})
c81b6a
 
c81b6a
@@ -288,6 +305,8 @@ class Plugin(object):
c81b6a
             self._do_copy_path(os.path.join(srcpath, afile), dest=None)
c81b6a
 
c81b6a
     def _get_dest_for_srcpath(self, srcpath):
c81b6a
+        if self.use_sysroot():
c81b6a
+            srcpath = self.join_sysroot(srcpath)
c81b6a
         for copied in self.copied_files:
c81b6a
             if srcpath == copied["srcpath"]:
c81b6a
                 return copied["dstpath"]
c81b6a
@@ -315,6 +334,9 @@ class Plugin(object):
c81b6a
         if not dest:
c81b6a
             dest = srcpath
c81b6a
 
c81b6a
+        if self.use_sysroot():
c81b6a
+            dest = self.strip_sysroot(dest)
c81b6a
+
c81b6a
         try:
c81b6a
             st = os.lstat(srcpath)
c81b6a
         except (OSError, IOError):
c81b6a
@@ -333,7 +355,7 @@ class Plugin(object):
c81b6a
         if not (stat.S_ISREG(st.st_mode) or stat.S_ISDIR(st.st_mode)):
c81b6a
             ntype = _node_type(st)
c81b6a
             self._log_debug("creating %s node at archive:'%s'"
c81b6a
-                            % (ntype, srcpath))
c81b6a
+                            % (ntype, dest))
c81b6a
             self._copy_node(srcpath, st)
c81b6a
             return
c81b6a
 
c81b6a
@@ -347,9 +369,11 @@ class Plugin(object):
c81b6a
         else:
c81b6a
             self.archive.add_file(srcpath, dest)
c81b6a
 
c81b6a
-        self.copied_files.append({'srcpath': srcpath,
c81b6a
-                                  'dstpath': dest,
c81b6a
-                                  'symlink': "no"})
c81b6a
+        self.copied_files.append({
c81b6a
+            'srcpath': srcpath,
c81b6a
+            'dstpath': dest,
c81b6a
+            'symlink': "no"
c81b6a
+        })
c81b6a
 
c81b6a
     def add_forbidden_path(self, forbiddenPath):
c81b6a
         """Specify a path to not copy, even if it's part of a copy_specs[]
c81b6a
@@ -416,6 +440,9 @@ class Plugin(object):
c81b6a
         except Exception:
c81b6a
             return default
c81b6a
 
c81b6a
+    def _add_copy_paths(self, copy_paths):
c81b6a
+        self.copy_paths.update(copy_paths)
c81b6a
+
c81b6a
     def add_copy_spec_limit(self, copyspec, sizelimit=None, tailit=True):
c81b6a
         """Add a file or glob but limit it to sizelimit megabytes. If fname is
c81b6a
         a single file the file will be tailed to meet sizelimit. If the first
c81b6a
@@ -424,10 +451,13 @@ class Plugin(object):
c81b6a
         if not (copyspec and len(copyspec)):
c81b6a
             return False
c81b6a
 
c81b6a
+        if self.use_sysroot():
c81b6a
+            copyspec = self.join_sysroot(copyspec)
c81b6a
         files = glob.glob(copyspec)
c81b6a
         files.sort()
c81b6a
         if len(files) == 0:
c81b6a
             return
c81b6a
+
c81b6a
         current_size = 0
c81b6a
         limit_reached = False
c81b6a
         sizelimit *= 1024 * 1024  # in MB
c81b6a
@@ -438,7 +468,7 @@ class Plugin(object):
c81b6a
             if sizelimit and current_size > sizelimit:
c81b6a
                 limit_reached = True
c81b6a
                 break
c81b6a
-            self.add_copy_spec(_file)
c81b6a
+            self._add_copy_paths([_file])
c81b6a
 
c81b6a
         if limit_reached and tailit:
c81b6a
             file_name = _file
c81b6a
@@ -459,12 +489,14 @@ class Plugin(object):
c81b6a
         if isinstance(copyspecs, six.string_types):
c81b6a
             copyspecs = [copyspecs]
c81b6a
         for copyspec in copyspecs:
c81b6a
+            if self.use_sysroot():
c81b6a
+                copyspec = self.join_sysroot(copyspec)
c81b6a
             if not (copyspec and len(copyspec)):
c81b6a
                 self._log_warn("added null or empty copy spec")
c81b6a
                 return False
c81b6a
             copy_paths = self._expand_copy_spec(copyspec)
c81b6a
-            self.copy_paths.update(copy_paths)
c81b6a
-            self._log_info("added copyspec '%s'" % copyspec)
c81b6a
+            self._add_copy_paths(copy_paths)
c81b6a
+            self._log_info("added copyspec '%s'" % copy_paths)
c81b6a
 
c81b6a
     def get_command_output(self, prog, timeout=300, runat=None, stderr=True):
c81b6a
         result = sos_get_command_output(prog, timeout=timeout, runat=runat,
c81b6a
diff --git a/tests/option_tests.py b/tests/option_tests.py
c81b6a
index fe37ccf..e8a26e2 100644
c81b6a
--- a/tests/option_tests.py
c81b6a
+++ b/tests/option_tests.py
c81b6a
@@ -8,10 +8,11 @@ class GlobalOptionTest(unittest.TestCase):
c81b6a
 
c81b6a
     def setUp(self):
c81b6a
         self.commons = {
c81b6a
+            'sysroot': '/',
c81b6a
             'global_plugin_options': {
c81b6a
                 'test_option': 'foobar',
c81b6a
                 'baz': None,
c81b6a
-                'empty_global': True,
c81b6a
+                'empty_global': True
c81b6a
             },
c81b6a
         }
c81b6a
         self.plugin = Plugin(self.commons)
c81b6a
diff --git a/tests/plugin_tests.py b/tests/plugin_tests.py
c81b6a
index e30ded5..14d3b49 100644
c81b6a
--- a/tests/plugin_tests.py
c81b6a
+++ b/tests/plugin_tests.py
c81b6a
@@ -127,50 +127,53 @@ class PluginToolTests(unittest.TestCase):
c81b6a
 
c81b6a
 class PluginTests(unittest.TestCase):
c81b6a
 
c81b6a
+    sysroot = os.getcwd()
c81b6a
+
c81b6a
     def setUp(self):
c81b6a
         self.mp = MockPlugin({
c81b6a
-            'cmdlineopts': MockOptions()
c81b6a
+            'cmdlineopts': MockOptions(),
c81b6a
+            'sysroot': self.sysroot
c81b6a
         })
c81b6a
         self.mp.archive = MockArchive()
c81b6a
 
c81b6a
     def test_plugin_default_name(self):
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         self.assertEquals(p.name(), "mockplugin")
c81b6a
 
c81b6a
     def test_plugin_set_name(self):
c81b6a
-        p = NamedMockPlugin({})
c81b6a
+        p = NamedMockPlugin({'sysroot': self.sysroot})
c81b6a
         self.assertEquals(p.name(), "testing")
c81b6a
 
c81b6a
     def test_plugin_no_descrip(self):
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         self.assertEquals(p.get_description(), "<no description available>")
c81b6a
 
c81b6a
     def test_plugin_no_descrip(self):
c81b6a
-        p = NamedMockPlugin({})
c81b6a
+        p = NamedMockPlugin({'sysroot': self.sysroot})
c81b6a
         self.assertEquals(p.get_description(), "This plugin has a description.")
c81b6a
 
c81b6a
     def test_set_plugin_option(self):
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         p.set_option("opt", "testing")
c81b6a
         self.assertEquals(p.get_option("opt"), "testing")
c81b6a
 
c81b6a
     def test_set_nonexistant_plugin_option(self):
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         self.assertFalse(p.set_option("badopt", "testing"))
c81b6a
 
c81b6a
     def test_get_nonexistant_plugin_option(self):
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         self.assertEquals(p.get_option("badopt"), 0)
c81b6a
 
c81b6a
     def test_get_unset_plugin_option(self):
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         self.assertEquals(p.get_option("opt"), 0)
c81b6a
 
c81b6a
     def test_get_unset_plugin_option_with_default(self):
c81b6a
         # this shows that even when we pass in a default to get,
c81b6a
         # we'll get the option's default as set in the plugin
c81b6a
         # this might not be what we really want
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         self.assertEquals(p.get_option("opt", True), True)
c81b6a
 
c81b6a
     def test_get_unset_plugin_option_with_default_not_none(self):
c81b6a
@@ -178,20 +181,20 @@ class PluginTests(unittest.TestCase):
c81b6a
         # if the plugin default is not None
c81b6a
         # we'll get the option's default as set in the plugin
c81b6a
         # this might not be what we really want
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         self.assertEquals(p.get_option("opt2", True), False)
c81b6a
 
c81b6a
     def test_get_option_as_list_plugin_option(self):
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         p.set_option("opt", "one,two,three")
c81b6a
         self.assertEquals(p.get_option_as_list("opt"), ['one', 'two', 'three'])
c81b6a
 
c81b6a
     def test_get_option_as_list_plugin_option_default(self):
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         self.assertEquals(p.get_option_as_list("opt", default=[]), [])
c81b6a
 
c81b6a
     def test_get_option_as_list_plugin_option_not_list(self):
c81b6a
-        p = MockPlugin({})
c81b6a
+        p = MockPlugin({'sysroot': self.sysroot})
c81b6a
         p.set_option("opt", "testing")
c81b6a
         self.assertEquals(p.get_option_as_list("opt"), ['testing'])
c81b6a
 
c81b6a
@@ -205,7 +208,8 @@ class PluginTests(unittest.TestCase):
c81b6a
 
c81b6a
     def test_copy_dir_forbidden_path(self):
c81b6a
         p = ForbiddenMockPlugin({
c81b6a
-            'cmdlineopts': MockOptions()
c81b6a
+            'cmdlineopts': MockOptions(),
c81b6a
+            'sysroot': self.sysroot
c81b6a
         })
c81b6a
         p.archive = MockArchive()
c81b6a
         p.setup()
c81b6a
@@ -219,12 +223,18 @@ class AddCopySpecTests(unittest.TestCase):
c81b6a
 
c81b6a
     def setUp(self):
c81b6a
         self.mp = MockPlugin({
c81b6a
-            'cmdlineopts': MockOptions()
c81b6a
+            'cmdlineopts': MockOptions(),
c81b6a
+            'sysroot': os.getcwd()
c81b6a
         })
c81b6a
         self.mp.archive = MockArchive()
c81b6a
 
c81b6a
     def assert_expect_paths(self):
c81b6a
-        self.assertEquals(self.mp.copy_paths, self.expect_paths)
c81b6a
+        def pathmunge(path):
c81b6a
+            if path[0] == '/':
c81b6a
+                path = path[1:]
c81b6a
+            return os.path.join(self.mp.sysroot, path)
c81b6a
+        expected_paths = set(map(pathmunge, self.expect_paths))
c81b6a
+        self.assertEquals(self.mp.copy_paths, expected_paths)
c81b6a
         
c81b6a
     # add_copy_spec()
c81b6a
 
c81b6a
@@ -242,6 +252,7 @@ class AddCopySpecTests(unittest.TestCase):
c81b6a
     # add_copy_spec_limit()
c81b6a
 
c81b6a
     def test_single_file_over_limit(self):
c81b6a
+        self.mp.sysroot = '/'
c81b6a
         fn = create_file(2) # create 2MB file, consider a context manager
c81b6a
         self.mp.add_copy_spec_limit(fn, 1)
c81b6a
         content, fname = self.mp.copy_strings[0]
c81b6a
@@ -252,10 +263,12 @@ class AddCopySpecTests(unittest.TestCase):
c81b6a
         os.unlink(fn)
c81b6a
 
c81b6a
     def test_bad_filename(self):
c81b6a
+        self.mp.sysroot = '/'
c81b6a
         self.assertFalse(self.mp.add_copy_spec_limit('', 1))
c81b6a
         self.assertFalse(self.mp.add_copy_spec_limit(None, 1))
c81b6a
 
c81b6a
     def test_glob_file_over_limit(self):
c81b6a
+        self.mp.sysroot = '/'
c81b6a
         # assume these are in /tmp
c81b6a
         fn = create_file(2)
c81b6a
         fn2 = create_file(2)
c81b6a
@@ -271,7 +284,10 @@ class AddCopySpecTests(unittest.TestCase):
c81b6a
 class CheckEnabledTests(unittest.TestCase):
c81b6a
 
c81b6a
     def setUp(self):
c81b6a
-        self.mp = EnablerPlugin({'policy': sos.policies.load()})
c81b6a
+        self.mp = EnablerPlugin({
c81b6a
+            'policy': sos.policies.load(),
c81b6a
+            'sysroot': os.getcwd()
c81b6a
+        })
c81b6a
 
c81b6a
     def test_checks_for_file(self):
c81b6a
         f = j("tail_test.txt")
c81b6a
@@ -296,7 +312,8 @@ class RegexSubTests(unittest.TestCase):
c81b6a
 
c81b6a
     def setUp(self):
c81b6a
         self.mp = MockPlugin({
c81b6a
-            'cmdlineopts': MockOptions()
c81b6a
+            'cmdlineopts': MockOptions(),
c81b6a
+            'sysroot': os.getcwd()
c81b6a
         })
c81b6a
         self.mp.archive = MockArchive()
c81b6a
 
c81b6a
@@ -310,6 +327,8 @@ class RegexSubTests(unittest.TestCase):
c81b6a
         self.assertEquals(0, replacements)
c81b6a
 
c81b6a
     def test_replacements(self):
c81b6a
+        # test uses absolute paths
c81b6a
+        self.mp.sysroot = '/'
c81b6a
         self.mp.add_copy_spec(j("tail_test.txt"))
c81b6a
         self.mp.collect()
c81b6a
         replacements = self.mp.do_file_sub(j("tail_test.txt"), r"(tail)", "foobar")
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From c4957d8aa4ea35f879639726267043f6bb46cc7c Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Sat, 24 Jan 2015 00:35:09 +0000
c81b6a
Subject: [PATCH 04/38] [docs] add -s/--sysroot to sosreport.1
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 man/en/sosreport.1 | 5 +++++
c81b6a
 1 file changed, 5 insertions(+)
c81b6a
c81b6a
diff --git a/man/en/sosreport.1 b/man/en/sosreport.1
c81b6a
index c2b60d8..b0a86f2 100644
c81b6a
--- a/man/en/sosreport.1
c81b6a
+++ b/man/en/sosreport.1
c81b6a
@@ -12,6 +12,7 @@ sosreport \- Collect and package diagnostic and support data
c81b6a
           [--no-report] [--config-file conf]\fR
c81b6a
           [--batch] [--build] [--debug]\fR
c81b6a
           [--name name] [--case-id id] [--ticket-number nr]
c81b6a
+          [-s|--sysroot]\fR
c81b6a
           [--tmp-dir directory]\fR
c81b6a
           [-p|--profile profile-name]\fR
c81b6a
           [--list-profiles]\fR
c81b6a
@@ -72,6 +73,10 @@ Disable HTML/XML report writing.
c81b6a
 .B \--config-file CONFIG
c81b6a
 Specify alternate configuration file.
c81b6a
 .TP
c81b6a
+.B \-s, \--sysroot SYSROOT
c81b6a
+Specify an alternate root file system path. Useful for collecting
c81b6a
+reports from containers and images.
c81b6a
+.TP
c81b6a
 .B \--tmp-dir DIRECTORY
c81b6a
 Specify alternate temporary directory to copy data as well as the
c81b6a
 compressed report.
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 4a0a3f9607006d402713320fc31780fb54556e6a Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Sun, 25 Jan 2015 14:20:10 +0000
c81b6a
Subject: [PATCH 05/38] [utilities] add chroot support to
c81b6a
 sos_get_command_output()
c81b6a
c81b6a
Allow callers of sos_get_command_output() to specify a path to
c81b6a
chroot into before executing command.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py  |  2 +-
c81b6a
 sos/utilities.py         | 27 ++++++++++++++++-----------
c81b6a
 tests/utilities_tests.py |  5 +++++
c81b6a
 3 files changed, 22 insertions(+), 12 deletions(-)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index 790338b..137e1a1 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -499,7 +499,7 @@ class Plugin(object):
c81b6a
             self._log_info("added copyspec '%s'" % copy_paths)
c81b6a
 
c81b6a
     def get_command_output(self, prog, timeout=300, runat=None, stderr=True):
c81b6a
-        result = sos_get_command_output(prog, timeout=timeout, runat=runat,
c81b6a
+        result = sos_get_command_output(prog, timeout=timeout, chdir=runat,
c81b6a
                                         stderr=stderr)
c81b6a
         if result['status'] == 124:
c81b6a
             self._log_warn("command '%s' timed out after %ds"
c81b6a
diff --git a/sos/utilities.py b/sos/utilities.py
c81b6a
index dfe6128..a82ac7c 100644
c81b6a
--- a/sos/utilities.py
c81b6a
+++ b/sos/utilities.py
c81b6a
@@ -120,15 +120,20 @@ def is_executable(command):
c81b6a
     return any(os.access(path, os.X_OK) for path in candidates)
c81b6a
 
c81b6a
 
c81b6a
-def sos_get_command_output(command, timeout=300, runat=None, stderr=True):
c81b6a
-    """Execute a command through the system shell. First checks to see if the
c81b6a
-    requested command is executable. Returns (returncode, stdout, 0)"""
c81b6a
-    def _child_chdir():
c81b6a
-        if(runat):
c81b6a
-            try:
c81b6a
-                os.chdir(runat)
c81b6a
-            except:
c81b6a
-                self.log_error("failed to chdir to '%s'" % runat)
c81b6a
+def sos_get_command_output(command, timeout=300, stderr=False,
c81b6a
+                           chroot=None, chdir=None):
c81b6a
+    """Execute a command and return a dictionary of status and output,
c81b6a
+    optionally changing root or current working directory before
c81b6a
+    executing command.
c81b6a
+    """
c81b6a
+    # Change root or cwd for child only. Exceptions in the prexec_fn
c81b6a
+    # closure are caught in the parent (chroot and chdir are bound from
c81b6a
+    # the enclosing scope).
c81b6a
+    def _child_prep_fn():
c81b6a
+        if (chroot):
c81b6a
+                os.chroot(chroot)
c81b6a
+        if (chdir):
c81b6a
+                os.chdir(chdir)
c81b6a
 
c81b6a
     cmd_env = os.environ
c81b6a
     # ensure consistent locale for collected command output
c81b6a
@@ -145,7 +150,7 @@ def sos_get_command_output(command, timeout=300, runat=None, stderr=True):
c81b6a
         p = Popen(args, shell=False, stdout=PIPE,
c81b6a
                   stderr=STDOUT if stderr else PIPE,
c81b6a
                   bufsize=-1, env=cmd_env, close_fds=True,
c81b6a
-                  preexec_fn=_child_chdir)
c81b6a
+                  preexec_fn=_child_prep_fn)
c81b6a
     except OSError as e:
c81b6a
         if e.errno == errno.ENOENT:
c81b6a
             return {'status': 127, 'output': ""}
c81b6a
@@ -185,7 +190,7 @@ def shell_out(cmd, timeout=30, runat=None):
c81b6a
     """Shell out to an external command and return the output or the empty
c81b6a
     string in case of error.
c81b6a
     """
c81b6a
-    return sos_get_command_output(cmd, timeout=timeout, runat=runat)['output']
c81b6a
+    return sos_get_command_output(cmd, timeout=timeout, chdir=runat)['output']
c81b6a
 
c81b6a
 
c81b6a
 class ImporterHelper(object):
c81b6a
diff --git a/tests/utilities_tests.py b/tests/utilities_tests.py
c81b6a
index 607056e..9327b1f 100644
c81b6a
--- a/tests/utilities_tests.py
c81b6a
+++ b/tests/utilities_tests.py
c81b6a
@@ -68,6 +68,11 @@ class ExecutableTest(unittest.TestCase):
c81b6a
         self.assertEquals(result['status'], 127)
c81b6a
         self.assertEquals(result['output'], "")
c81b6a
 
c81b6a
+    def test_output_chdir(self):
c81b6a
+        result = sos_get_command_output("/usr/bin/pwd", chdir=TEST_DIR)
c81b6a
+        self.assertEquals(result['status'], 0)
c81b6a
+        self.assertEquals(result['output'].strip(), TEST_DIR)
c81b6a
+
c81b6a
     def test_shell_out(self):
c81b6a
         path = os.path.join(TEST_DIR, 'test_exe.py')
c81b6a
         self.assertEquals("executed\n", shell_out(path))
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 9a87cb3415a7a9587828ee40d689439949def1be Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Sun, 25 Jan 2015 14:30:13 +0000
c81b6a
Subject: [PATCH 06/38] [sosreport] add --chroot option
c81b6a
c81b6a
Add a --chroot option to sosreport to control command chrooting.
c81b6a
c81b6a
The option takes one of three values:
c81b6a
c81b6a
  * auto   - Allow callers of the API to control chroot behaviour
c81b6a
  * always - Always chroot external commands to --sysroot
c81b6a
  * never  - Never chroot external commands
c81b6a
c81b6a
This is a fairly low-level option and may not be exposed to the
c81b6a
user in a final release; for now it will allow tests in container
c81b6a
environments to control the chrooting behaviour used for a run.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/sosreport.py         | 19 +++++++++++++++++++
c81b6a
 tests/utilities_tests.py |  4 +++-
c81b6a
 2 files changed, 22 insertions(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/sosreport.py b/sos/sosreport.py
c81b6a
index 580b5bd..d9abcb8 100644
c81b6a
--- a/sos/sosreport.py
c81b6a
+++ b/sos/sosreport.py
c81b6a
@@ -540,6 +540,21 @@ class SoSOptions(object):
c81b6a
         self._sysroot = value
c81b6a
 
c81b6a
     @property
c81b6a
+    def chroot(self):
c81b6a
+        if self._options is not None:
c81b6a
+            return self._options.chroot
c81b6a
+        return self._chroot
c81b6a
+
c81b6a
+    @chroot.setter
c81b6a
+    def chroot(self, value):
c81b6a
+        self._check_options_initialized()
c81b6a
+        if value not in ["auto", "always", "never"]:
c81b6a
+            msg = "SoSOptions.chroot '%s' is not a valid chroot mode: "
c81b6a
+            msg += "('auto', 'always', 'never')"
c81b6a
+            raise ValueError(msg % value)
c81b6a
+        self._chroot = value
c81b6a
+
c81b6a
+    @property
c81b6a
     def compression_type(self):
c81b6a
         if self._options is not None:
c81b6a
             return self._options.compression_type
c81b6a
@@ -630,6 +645,10 @@ class SoSOptions(object):
c81b6a
         parser.add_option("-s", "--sysroot", action="store", dest="sysroot",
c81b6a
                           help="system root directory path (default='/')",
c81b6a
                           default="/")
c81b6a
+        parser.add_option("-c", "--chroot", action="store", dest="chroot",
c81b6a
+                          help="chroot executed commands to SYSROOT "
c81b6a
+                               "[auto, always, never] (default=auto)",
c81b6a
+                               default="auto")
c81b6a
         parser.add_option("-z", "--compression-type", dest="compression_type",
c81b6a
                           help="compression technology to use [auto, "
c81b6a
                                "gzip, bzip2, xz] (default=auto)",
c81b6a
diff --git a/tests/utilities_tests.py b/tests/utilities_tests.py
c81b6a
index 9327b1f..c464692 100644
c81b6a
--- a/tests/utilities_tests.py
c81b6a
+++ b/tests/utilities_tests.py
c81b6a
@@ -69,7 +69,9 @@ class ExecutableTest(unittest.TestCase):
c81b6a
         self.assertEquals(result['output'], "")
c81b6a
 
c81b6a
     def test_output_chdir(self):
c81b6a
-        result = sos_get_command_output("/usr/bin/pwd", chdir=TEST_DIR)
c81b6a
+        cmd = "/bin/bash -c 'echo $PWD'"
c81b6a
+        result = sos_get_command_output(cmd, chdir=TEST_DIR)
c81b6a
+        print(result)
c81b6a
         self.assertEquals(result['status'], 0)
c81b6a
         self.assertEquals(result['output'].strip(), TEST_DIR)
c81b6a
 
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 0d060dc3aa5e90373e7bb55f9310b4cf9db0dad4 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Sun, 25 Jan 2015 15:04:29 +0000
c81b6a
Subject: [PATCH 07/38] [plugins] implement --chroot for command callouts
c81b6a
c81b6a
When --chroot=always is given chroot all commands to SYSROOT.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 8 ++++++--
c81b6a
 1 file changed, 6 insertions(+), 2 deletions(-)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index 137e1a1..10fdae5 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -499,8 +499,12 @@ class Plugin(object):
c81b6a
             self._log_info("added copyspec '%s'" % copy_paths)
c81b6a
 
c81b6a
     def get_command_output(self, prog, timeout=300, runat=None, stderr=True):
c81b6a
-        result = sos_get_command_output(prog, timeout=timeout, chdir=runat,
c81b6a
-                                        stderr=stderr)
c81b6a
+        if self.commons['cmdlineopts'].chroot == 'always':
c81b6a
+            root = self.sysroot
c81b6a
+        else:
c81b6a
+            root = None
c81b6a
+        result = sos_get_command_output(prog, timeout=timeout, stderr=stderr
c81b6a
+                                        chroot=root, chdir=runat)
c81b6a
         if result['status'] == 124:
c81b6a
             self._log_warn("command '%s' timed out after %ds"
c81b6a
                            % (prog, timeout))
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From f6f7934c7d3e7f6cb41879fc0625b06d0468af4e Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Sun, 25 Jan 2015 19:32:04 +0000
c81b6a
Subject: [PATCH 08/38] [plugin] fix chrooted symlink handling
c81b6a
c81b6a
_copy_symlink() needs to strip_sysroot(), not join_sysroot(), on
c81b6a
a link target before handing it to _do_copy_path().
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 2 +-
c81b6a
 1 file changed, 1 insertion(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index 10fdae5..9d04939 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -294,7 +294,7 @@ class Plugin(object):
c81b6a
         # to absolute paths to pass to _do_copy_path.
c81b6a
         self._log_debug("normalized link target '%s' as '%s'"
c81b6a
                         % (linkdest, absdest))
c81b6a
-        self._do_copy_path(absdest)
c81b6a
+        self._do_copy_path(self.strip_sysroot(absdest))
c81b6a
 
c81b6a
         self.copied_files.append({'srcpath': srcpath,
c81b6a
                                   'dstpath': dstpath,
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From b16fbf3911c6256674e072cff6fa706050861993 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Sun, 25 Jan 2015 21:54:19 +0000
c81b6a
Subject: [PATCH 09/38] [sosreport] check for valid CHROOT values
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/sosreport.py | 16 +++++++++++++---
c81b6a
 1 file changed, 13 insertions(+), 3 deletions(-)
c81b6a
c81b6a
diff --git a/sos/sosreport.py b/sos/sosreport.py
c81b6a
index d9abcb8..a0b89e7 100644
c81b6a
--- a/sos/sosreport.py
c81b6a
+++ b/sos/sosreport.py
c81b6a
@@ -225,6 +225,10 @@ class XmlReport(object):
c81b6a
         outf.close()
c81b6a
 
c81b6a
 
c81b6a
+# valid modes for --chroot
c81b6a
+chroot_modes = ["auto", "always", "never"]
c81b6a
+
c81b6a
+
c81b6a
 class SoSOptions(object):
c81b6a
     _list_plugins = False
c81b6a
     _noplugins = []
c81b6a
@@ -548,7 +552,7 @@ class SoSOptions(object):
c81b6a
     @chroot.setter
c81b6a
     def chroot(self, value):
c81b6a
         self._check_options_initialized()
c81b6a
-        if value not in ["auto", "always", "never"]:
c81b6a
+        if value not in chroot_modes:
c81b6a
             msg = "SoSOptions.chroot '%s' is not a valid chroot mode: "
c81b6a
             msg += "('auto', 'always', 'never')"
c81b6a
             raise ValueError(msg % value)
c81b6a
@@ -705,6 +709,14 @@ class SoSReport(object):
c81b6a
         if self.opts.sysroot:
c81b6a
             self.sysroot = self.opts.sysroot
c81b6a
 
c81b6a
+        self._setup_logging()
c81b6a
+
c81b6a
+        if self.opts.chroot not in chroot_modes:
c81b6a
+            self.soslog.error("invalid chroot mode: %s" % self.opts.chroot)
c81b6a
+            logging.shutdown()
c81b6a
+            self.tempfile_util.clean()
c81b6a
+            self._exit(1)
c81b6a
+
c81b6a
     def print_header(self):
c81b6a
         self.ui_log.info("\n%s\n" % _("sosreport (version %s)" %
c81b6a
                          (__version__,)))
c81b6a
@@ -1205,7 +1217,6 @@ class SoSReport(object):
c81b6a
                     self.ui_log.error(" %s while setting up plugins"
c81b6a
                                       % e.strerror)
c81b6a
                     self.ui_log.error("")
c81b6a
-                    self._exit(1)
c81b6a
                 if self.raise_plugins:
c81b6a
                     raise
c81b6a
                 self._log_plugin_exception(plugname, "setup")
c81b6a
@@ -1455,7 +1466,6 @@ class SoSReport(object):
c81b6a
 
c81b6a
     def execute(self):
c81b6a
         try:
c81b6a
-            self._setup_logging()
c81b6a
             self.policy.set_commons(self.get_commons())
c81b6a
             self.print_header()
c81b6a
             self.load_plugins()
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From f06efd6fa7bbb0c81ce0461d4eaeed225d6f04a2 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Sun, 25 Jan 2015 23:03:08 +0000
c81b6a
Subject: [PATCH 10/38] [plugins] add chroot parameter to callout APIs
c81b6a
c81b6a
Expose sos_get_command_output()'s chroot support to plugins via
c81b6a
add_cmd_output(), get_command_output(), call_ext_prog() and
c81b6a
related Plugin methods.
c81b6a
c81b6a
'chroot' is a boolean indicating whether the command should run
c81b6a
in the chroot (True) or in the host namespace (False).
c81b6a
c81b6a
Has no effect when Plugin.use_sysroot() is False.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 50 ++++++++++++++++++++++++++++---------------------
c81b6a
 1 file changed, 29 insertions(+), 21 deletions(-)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index 9d04939..c1b659d 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -498,12 +498,13 @@ class Plugin(object):
c81b6a
             self._add_copy_paths(copy_paths)
c81b6a
             self._log_info("added copyspec '%s'" % copy_paths)
c81b6a
 
c81b6a
-    def get_command_output(self, prog, timeout=300, runat=None, stderr=True):
c81b6a
-        if self.commons['cmdlineopts'].chroot == 'always':
c81b6a
+    def get_command_output(self, prog, timeout=300, stderr=True,
c81b6a
+                           chroot=True, runat=None):
c81b6a
+        if chroot or self.commons['cmdlineopts'].chroot == 'always':
c81b6a
             root = self.sysroot
c81b6a
         else:
c81b6a
             root = None
c81b6a
-        result = sos_get_command_output(prog, timeout=timeout, stderr=stderr
c81b6a
+        result = sos_get_command_output(prog, timeout=timeout, stderr=stderr,
c81b6a
                                         chroot=root, chdir=runat)
c81b6a
         if result['status'] == 124:
c81b6a
             self._log_warn("command '%s' timed out after %ds"
c81b6a
@@ -513,12 +514,13 @@ class Plugin(object):
c81b6a
             self._log_debug("could not run '%s': command not found" % prog)
c81b6a
         return result
c81b6a
 
c81b6a
-    def call_ext_prog(self, prog, timeout=300, runat=None, stderr=True):
c81b6a
+    def call_ext_prog(self, prog, timeout=300, stderr=True,
c81b6a
+                      chroot=True, runat=None):
c81b6a
         """Execute a command independantly of the output gathering part of
c81b6a
         sosreport.
c81b6a
         """
c81b6a
-        return self.get_command_output(prog, timeout=timeout, runat=runat,
c81b6a
-                                       stderr=True)
c81b6a
+        return self.get_command_output(prog, timeout=timeout, stderr=stderr,
c81b6a
+                                       chroot=chroot, runat=runat)
c81b6a
 
c81b6a
     def check_ext_prog(self, prog):
c81b6a
         """Execute a command independently of the output gathering part of
c81b6a
@@ -528,8 +530,8 @@ class Plugin(object):
c81b6a
         return self.call_ext_prog(prog)['status'] == 0
c81b6a
 
c81b6a
     def add_cmd_output(self, cmds, suggest_filename=None,
c81b6a
-                       root_symlink=None, timeout=300, runat=None,
c81b6a
-                       stderr=True):
c81b6a
+                       root_symlink=None, timeout=300, stderr=True,
c81b6a
+                       chroot=True, runat=None):
c81b6a
         """Run a program or a list of programs and collect the output"""
c81b6a
         if isinstance(cmds, six.string_types):
c81b6a
             cmds = [cmds]
c81b6a
@@ -537,9 +539,10 @@ class Plugin(object):
c81b6a
             self._log_warn("ambiguous filename or symlink for command list")
c81b6a
         for cmd in cmds:
c81b6a
             cmdt = (
c81b6a
-                cmd, suggest_filename, root_symlink, timeout, runat, stderr
c81b6a
+                cmd, suggest_filename, root_symlink, timeout, stderr,
c81b6a
+                chroot, runat
c81b6a
             )
c81b6a
-            _tuplefmt = "('%s', '%s', '%s', %s, '%s', '%s')"
c81b6a
+            _tuplefmt = "('%s', '%s', '%s', %s, '%s', '%s', '%s')"
c81b6a
             _logstr = "packed command tuple: " + _tuplefmt
c81b6a
             self._log_debug(_logstr % cmdt)
c81b6a
             self.collect_cmds.append(cmdt)
c81b6a
@@ -594,14 +597,14 @@ class Plugin(object):
c81b6a
         self._log_debug("added string '%s' as '%s'" % (content, filename))
c81b6a
 
c81b6a
     def get_cmd_output_now(self, exe, suggest_filename=None,
c81b6a
-                           root_symlink=False, timeout=300,
c81b6a
-                           runat=None, stderr=True):
c81b6a
+                           root_symlink=False, timeout=300, stderr=True,
c81b6a
+                           chroot=True, runat=None):
c81b6a
         """Execute a command and save the output to a file for inclusion in the
c81b6a
         report.
c81b6a
         """
c81b6a
         start = time()
c81b6a
-        result = self.get_command_output(exe, timeout=timeout, runat=runat,
c81b6a
-                                         stderr=stderr)
c81b6a
+        result = self.get_command_output(exe, timeout=timeout, stderr=stderr,
c81b6a
+                                         chroot=chroot, runat=runat)
c81b6a
         # 126 means 'found but not executable'
c81b6a
         if result['status'] == 126 or result['status'] == 127:
c81b6a
             return None
c81b6a
@@ -650,15 +653,20 @@ class Plugin(object):
c81b6a
 
c81b6a
     def _collect_cmd_output(self):
c81b6a
         for progs in zip(self.collect_cmds):
c81b6a
-            (prog, suggest_filename, root_symlink, timeout, runat, stderr
c81b6a
-             ) = progs[0]
c81b6a
-            self._log_debug("unpacked command tuple: "
c81b6a
-                            + "('%s', '%s', '%s', %s, '%s', %s)" % progs[0])
c81b6a
+            (
c81b6a
+                prog,
c81b6a
+                suggest_filename, root_symlink,
c81b6a
+                timeout,
c81b6a
+                stderr,
c81b6a
+                chroot, runat
c81b6a
+            ) = progs[0]
c81b6a
+            self._log_debug("unpacked command tuple: " +
c81b6a
+                            "('%s', '%s', '%s', %s, '%s', '%s', '%s')" %
c81b6a
+                            progs[0])
c81b6a
             self._log_info("collecting output of '%s'" % prog)
c81b6a
             self.get_cmd_output_now(prog, suggest_filename=suggest_filename,
c81b6a
-                                    root_symlink=root_symlink,
c81b6a
-                                    timeout=timeout, runat=runat,
c81b6a
-                                    stderr=stderr)
c81b6a
+                                    root_symlink=root_symlink, timeout=timeout,
c81b6a
+                                    chroot=chroot, runat=runat)
c81b6a
 
c81b6a
     def _collect_strings(self):
c81b6a
         for string, file_name in self.copy_strings:
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 3390d070b2945715a15f74462c511df2b2941ef5 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Sun, 25 Jan 2015 23:08:37 +0000
c81b6a
Subject: [PATCH 11/38] [plugin] add tmp_in_sysroot() method
c81b6a
c81b6a
Add a method that plugins can test to determine whether the
c81b6a
archive's temporary directory is inside sysroot. This is always
c81b6a
true when sysroot is '/'. When sysroot is a subdirectory of root
c81b6a
the temporary directory may be inaccessible from the chroot
c81b6a
namespace. Plugins can test this method to determine where to
c81b6a
write output.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 4 ++++
c81b6a
 1 file changed, 4 insertions(+)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index c1b659d..49f62bf 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -169,6 +169,10 @@ class Plugin(object):
c81b6a
     def use_sysroot(self):
c81b6a
         return self.sysroot != os.path.abspath(os.sep)
c81b6a
 
c81b6a
+    def tmp_in_sysroot(self):
c81b6a
+        paths = [self.sysroot, self.archive.get_tmp_dir()]
c81b6a
+        return os.path.commonprefix(paths) == self.sysroot
c81b6a
+
c81b6a
     def is_installed(self, package_name):
c81b6a
         '''Is the package $package_name installed?'''
c81b6a
         return self.policy().pkg_by_name(package_name) is not None
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 5ae1b392d1f081bcb43e91a572342d6f02e4728d Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Sun, 25 Jan 2015 23:27:51 +0000
c81b6a
Subject: [PATCH 12/38] [plugin] enforce forbidden paths when --sysroot is set
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 10 +++++++---
c81b6a
 1 file changed, 7 insertions(+), 3 deletions(-)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index 49f62bf..fd1acb5 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -317,6 +317,8 @@ class Plugin(object):
c81b6a
         return None
c81b6a
 
c81b6a
     def _is_forbidden_path(self, path):
c81b6a
+        if self.use_sysroot():
c81b6a
+            path = self.join_sysroot(path)
c81b6a
         return _path_in_path_list(path, self.forbidden_paths)
c81b6a
 
c81b6a
     def _copy_node(self, path, st):
c81b6a
@@ -379,13 +381,15 @@ class Plugin(object):
c81b6a
             'symlink': "no"
c81b6a
         })
c81b6a
 
c81b6a
-    def add_forbidden_path(self, forbiddenPath):
c81b6a
+    def add_forbidden_path(self, forbidden):
c81b6a
         """Specify a path to not copy, even if it's part of a copy_specs[]
c81b6a
         entry.
c81b6a
         """
c81b6a
+        if self.use_sysroot():
c81b6a
+            forbidden = self.join_sysroot(forbidden)
c81b6a
         # Glob case handling is such that a valid non-glob is a reduced glob
c81b6a
-        for filespec in glob.glob(forbiddenPath):
c81b6a
-            self.forbidden_paths.append(filespec)
c81b6a
+        for path in glob.glob(forbidden):
c81b6a
+            self.forbidden_paths.append(path)
c81b6a
 
c81b6a
     def get_all_options(self):
c81b6a
         """return a list of all options selected"""
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From e18d25a0e0c10a2702893f7bae2530dc2a41a394 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 00:00:08 +0000
c81b6a
Subject: [PATCH 13/38] [cluster] handle crm_report with --sysroot
c81b6a
c81b6a
Don't attempt to run crm_report in the chroot if tmp is not a
c81b6a
subdirectory of sysroot.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/cluster.py | 5 +++--
c81b6a
 1 file changed, 3 insertions(+), 2 deletions(-)
c81b6a
c81b6a
diff --git a/sos/plugins/cluster.py b/sos/plugins/cluster.py
c81b6a
index ea5dbae..f52f154 100644
c81b6a
--- a/sos/plugins/cluster.py
c81b6a
+++ b/sos/plugins/cluster.py
c81b6a
@@ -120,8 +120,9 @@ class Cluster(Plugin, RedHatPlugin):
c81b6a
             self._log_warn("scrubbing of crm passwords has been disabled:")
c81b6a
             self._log_warn("data collected by crm_report may contain"
c81b6a
                            " sensitive values.")
c81b6a
-        self.add_cmd_output('crm_report %s -S -d --dest %s --from "%s"'
c81b6a
-                            % (crm_scrub, crm_dest, crm_from))
c81b6a
+        self.add_cmd_output('crm_report %s -S -d --dest %s --from "%s"' %
c81b6a
+                            (crm_scrub, crm_dest, crm_from),
c81b6a
+                            chroot=self.tmp_in_sysroot())
c81b6a
 
c81b6a
     def do_lockdump(self):
c81b6a
         if self._mount_debug():
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 2ca9c74454699ba6ecad21d6b0c0809333d729aa Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 00:02:35 +0000
c81b6a
Subject: [PATCH 14/38] [dmraid] don't chroot if tmp is not inside sysroot
c81b6a
c81b6a
To dump metadata dmraid needs to chdir to the temporary archive
c81b6a
directory. Don't attempt to chroot into sysroot if the temporary
c81b6a
directory is not a subdirectory of it.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/dmraid.py | 3 ++-
c81b6a
 1 file changed, 2 insertions(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/plugins/dmraid.py b/sos/plugins/dmraid.py
c81b6a
index b7c0b42..87381a0 100644
c81b6a
--- a/sos/plugins/dmraid.py
c81b6a
+++ b/sos/plugins/dmraid.py
c81b6a
@@ -39,6 +39,7 @@ class Dmraid(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin):
c81b6a
             self.add_cmd_output("dmraid -%s" % (opt,))
c81b6a
         if self.get_option("metadata"):
c81b6a
             metadata_path = self.get_cmd_output_path("metadata")
c81b6a
-            self.add_cmd_output("dmraid -rD", runat=metadata_path)
c81b6a
+            self.add_cmd_output("dmraid -rD", runat=metadata_path,
c81b6a
+                                chroot=self.tmp_in_sysroot())
c81b6a
 
c81b6a
 # vim: et ts=4 sw=4
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 4ae09ee0ed25d771cc6cc8a013837ed4c647b3ed Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 00:04:15 +0000
c81b6a
Subject: [PATCH 15/38] [foreman] don't chroot if tmp is not inside sysroot
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/foreman.py | 4 +++-
c81b6a
 1 file changed, 3 insertions(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/plugins/foreman.py b/sos/plugins/foreman.py
c81b6a
index 9d1cbad..363b9d6 100644
c81b6a
--- a/sos/plugins/foreman.py
c81b6a
+++ b/sos/plugins/foreman.py
c81b6a
@@ -27,7 +27,9 @@ class Foreman(Plugin, RedHatPlugin):
c81b6a
 
c81b6a
     def setup(self):
c81b6a
         cmd = "foreman-debug"
c81b6a
+
c81b6a
         path = self.get_cmd_output_path(name="foreman-debug")
c81b6a
-        self.add_cmd_output("%s -g -q -a -d %s" % (cmd, path))
c81b6a
+        self.add_cmd_output("%s -g -q -a -d %s" % (cmd, path),
c81b6a
+                            chroot=self.tmp_in_sysroot())
c81b6a
 
c81b6a
 # vim: et ts=4 sw=4
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 78761114ea9cc1b33233db2186a5ff762e1ac2f2 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 00:07:12 +0000
c81b6a
Subject: [PATCH 16/38] [libvirt] use join_sysroot() before calling
c81b6a
 os.path.exists
c81b6a
c81b6a
The libvirt plugin tests for the presence of files. Use
c81b6a
join_sysroot() to ensure the correct path is tested.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/libvirt.py | 2 +-
c81b6a
 1 file changed, 1 insertion(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/plugins/libvirt.py b/sos/plugins/libvirt.py
c81b6a
index aaf862b..295c8eb 100644
c81b6a
--- a/sos/plugins/libvirt.py
c81b6a
+++ b/sos/plugins/libvirt.py
c81b6a
@@ -56,7 +56,7 @@ class Libvirt(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin):
c81b6a
         else:
c81b6a
             self.add_copy_spec("/var/log/libvirt")
c81b6a
 
c81b6a
-        if os.path.exists(libvirt_keytab):
c81b6a
+        if os.path.exists(self.join_sysroot(libvirt_keytab)):
c81b6a
             self.add_cmd_output("klist -ket %s" % libvirt_keytab)
c81b6a
 
c81b6a
         self.add_cmd_output("ls -lR /var/lib/libvirt/qemu")
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From b34ea0c6ea5449bbd2f4f9624e1644dc01b07e9d Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 00:09:11 +0000
c81b6a
Subject: [PATCH 17/38] [logs] fix do_regex_find_all() use for --sysroot
c81b6a
c81b6a
The logs plugin searches syslog configuration files. When using
c81b6a
--sysroot the plugin needs to use join_sysroot() to open the
c81b6a
correct path.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/logs.py | 8 ++++----
c81b6a
 1 file changed, 4 insertions(+), 4 deletions(-)
c81b6a
c81b6a
diff --git a/sos/plugins/logs.py b/sos/plugins/logs.py
c81b6a
index 2f86acc..7957898 100644
c81b6a
--- a/sos/plugins/logs.py
c81b6a
+++ b/sos/plugins/logs.py
c81b6a
@@ -38,12 +38,12 @@ class Logs(Plugin):
c81b6a
         ])
c81b6a
 
c81b6a
         if self.get_option('all_logs'):
c81b6a
-            logs = self.do_regex_find_all("^\S+\s+(-?\/.*$)\s+",
c81b6a
-                                          "/etc/syslog.conf")
c81b6a
+            syslog_conf = self.join_sysroot("/etc/syslog.conf")
c81b6a
+            logs = self.do_regex_find_all("^\S+\s+(-?\/.*$)\s+", syslog_conf)
c81b6a
             if self.is_installed("rsyslog") \
c81b6a
                     or os.path.exists("/etc/rsyslog.conf"):
c81b6a
                 logs += self.do_regex_find_all("^\S+\s+(-?\/.*$)\s+",
c81b6a
-                                               "/etc/rsyslog.conf")
c81b6a
+                                               rsyslog_conf)
c81b6a
             for i in logs:
c81b6a
                 if i.startswith("-"):
c81b6a
                     i = i[1:]
c81b6a
@@ -74,7 +74,7 @@ class RedHatLogs(Logs, RedHatPlugin):
c81b6a
         messages = "/var/log/messages"
c81b6a
         self.add_copy_spec_limit("/var/log/secure*", sizelimit=self.limit)
c81b6a
         self.add_copy_spec_limit(messages + "*", sizelimit=self.limit)
c81b6a
-        # collect five days worth of logs by default if the system is
c81b6a
+        # collect three days worth of logs by default if the system is
c81b6a
         # configured to use the journal and not /var/log/messages
c81b6a
         if not os.path.exists(messages) and self.is_installed("systemd"):
c81b6a
             try:
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 534eb7fe732ec366292ec582ec2891ef0648ffcb Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 00:11:15 +0000
c81b6a
Subject: [PATCH 18/38] [lvm2] don't chroot if tmp is not inside sysroot
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/lvm2.py | 2 +-
c81b6a
 1 file changed, 1 insertion(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/plugins/lvm2.py b/sos/plugins/lvm2.py
c81b6a
index dd10fa7..c626231 100644
c81b6a
--- a/sos/plugins/lvm2.py
c81b6a
+++ b/sos/plugins/lvm2.py
c81b6a
@@ -37,7 +37,7 @@ class Lvm2(Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin):
c81b6a
             lvmdump_opts = "-a -m"
c81b6a
         cmd = lvmdump_cmd % (lvmdump_opts,
c81b6a
                              self.get_cmd_output_path(name="lvmdump"))
c81b6a
-        self.add_cmd_output(cmd)
c81b6a
+        self.add_cmd_output(cmd, chroot=self.tmp_in_sysroot())
c81b6a
 
c81b6a
     def setup(self):
c81b6a
         # use locking_type 0 (no locks) when running LVM2 commands,
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From f4218ff5bab93908ca3d0804c9d837bdfa57f654 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 00:37:32 +0000
c81b6a
Subject: [PATCH 19/38] [docs] add --chroot to sosreport.1
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 man/en/sosreport.1 | 8 ++++++++
c81b6a
 1 file changed, 8 insertions(+)
c81b6a
c81b6a
diff --git a/man/en/sosreport.1 b/man/en/sosreport.1
c81b6a
index b0a86f2..f36f845 100644
c81b6a
--- a/man/en/sosreport.1
c81b6a
+++ b/man/en/sosreport.1
c81b6a
@@ -13,6 +13,7 @@ sosreport \- Collect and package diagnostic and support data
c81b6a
           [--batch] [--build] [--debug]\fR
c81b6a
           [--name name] [--case-id id] [--ticket-number nr]
c81b6a
           [-s|--sysroot]\fR
c81b6a
+          [-c|--chroot {auto|always|never}\fR
c81b6a
           [--tmp-dir directory]\fR
c81b6a
           [-p|--profile profile-name]\fR
c81b6a
           [--list-profiles]\fR
c81b6a
@@ -77,6 +78,13 @@ Specify alternate configuration file.
c81b6a
 Specify an alternate root file system path. Useful for collecting
c81b6a
 reports from containers and images.
c81b6a
 .TP
c81b6a
+.B \-c, \--chroot {auto|always|never}
c81b6a
+Set the chroot mode. When \--sysroot is used commands default to
c81b6a
+executing with SYSROOT as the root directory (unless disabled by
c81b6a
+a specific plugin). This can be overriden by setting \--chroot to
c81b6a
+"always" (alwyas chroot) or "never" (always run in the host
c81b6a
+namespace).
c81b6a
+.TP
c81b6a
 .B \--tmp-dir DIRECTORY
c81b6a
 Specify alternate temporary directory to copy data as well as the
c81b6a
 compressed report.
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From fb1b4a8b6793611ed91f43f6d3553351c704f50f Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 14:57:01 +0000
c81b6a
Subject: [PATCH 20/38] [plugin] handle ELOOP in _copy_dir()
c81b6a
c81b6a
A problem with systemd's management of the binfmt_misc automount
c81b6a
point in Atomic environments causes attempts to access this path to
c81b6a
fail with ELOOP:
c81b6a
c81b6a
  >>> import os
c81b6a
  >>> os.listdir("/host/proc/sys/fs/binfmt_misc/")
c81b6a
  Traceback (most recent call last):
c81b6a
    File "<stdin>", line 1, in <module>
c81b6a
  OSError: [Errno 2] No such file or directory: '/host/proc/sys/fs/binfmt_misc/'
c81b6a
c81b6a
For reasons that are not yet clear this causes the entire sos
c81b6a
process to immediately terminate.
c81b6a
c81b6a
For now avoid the problem by enclosing the problem os.listdir in
c81b6a
a try/except block that explicitly handles the one errno value
c81b6a
implicated here.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 16 ++++++++++++----
c81b6a
 1 file changed, 12 insertions(+), 4 deletions(-)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index fd1acb5..b206470 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -27,6 +27,7 @@ import stat
c81b6a
 from time import time
c81b6a
 import logging
c81b6a
 import fnmatch
c81b6a
+import errno
c81b6a
 
c81b6a
 # PYCOMPAT
c81b6a
 import six
c81b6a
@@ -303,10 +304,17 @@ class Plugin(object):
c81b6a
                                   'pointsto': linkdest})
c81b6a
 
c81b6a
     def _copy_dir(self, srcpath):
c81b6a
-        for afile in os.listdir(srcpath):
c81b6a
-            self._log_debug("recursively adding '%s' from '%s'"
c81b6a
-                            % (afile, srcpath))
c81b6a
-            self._do_copy_path(os.path.join(srcpath, afile), dest=None)
c81b6a
+        try:
c81b6a
+            for afile in os.listdir(srcpath):
c81b6a
+                self._log_debug("recursively adding '%s' from '%s'"
c81b6a
+                                % (afile, srcpath))
c81b6a
+                self._do_copy_path(os.path.join(srcpath, afile), dest=None)
c81b6a
+        except OSError as e:
c81b6a
+            if e.errno == errno.ELOOP:
c81b6a
+                msg = "Too many levels of symbolic links copying"
c81b6a
+                self._log_error("_copy_dir: %s '%s'" % (msg, srcpath))
c81b6a
+                return
c81b6a
+            raise e
c81b6a
 
c81b6a
     def _get_dest_for_srcpath(self, srcpath):
c81b6a
         if self.use_sysroot():
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 2c1af0457b668acb99129ed1f6dedcf7cdaa0eea Mon Sep 17 00:00:00 2001
c81b6a
From: Neependra Khare <nkhare@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 15:04:07 +0000
c81b6a
Subject: [PATCH 21/38] [kubernetes] new plugin
c81b6a
c81b6a
Add a plugin for Kubernetes support.
c81b6a
c81b6a
Signed-off-by: Neependra Khare <nkhare@redhat.com>
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/kubernetes.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++
c81b6a
 1 file changed, 46 insertions(+)
c81b6a
 create mode 100644 sos/plugins/kubernetes.py
c81b6a
c81b6a
diff --git a/sos/plugins/kubernetes.py b/sos/plugins/kubernetes.py
c81b6a
new file mode 100644
c81b6a
index 0000000..af3f3a6
c81b6a
--- /dev/null
c81b6a
+++ b/sos/plugins/kubernetes.py
c81b6a
@@ -0,0 +1,46 @@
c81b6a
+# Copyright (C) 2014 Red Hat, Inc. Neependra Khare <nkhare@redhat.com>
c81b6a
+# Copyright (C) 2014 Red Hat, Inc. Bryn M. Reeves <bmr@redhat.com>
c81b6a
+# This program is free software; you can redistribute it and/or modify
c81b6a
+# it under the terms of the GNU General Public License as published by
c81b6a
+# the Free Software Foundation; either version 2 of the License, or
c81b6a
+# (at your option) any later version.
c81b6a
+
c81b6a
+# This program is distributed in the hope that it will be useful,
c81b6a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
c81b6a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
c81b6a
+# GNU General Public License for more details.
c81b6a
+
c81b6a
+# You should have received a copy of the GNU General Public License
c81b6a
+# along with this program; if not, write to the Free Software
c81b6a
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
c81b6a
+
c81b6a
+from sos.plugins import Plugin, RedHatPlugin
c81b6a
+
c81b6a
+
c81b6a
+class kubernetes(Plugin, RedHatPlugin):
c81b6a
+
c81b6a
+    """Kubernetes plugin
c81b6a
+    """
c81b6a
+
c81b6a
+    def setup(self):
c81b6a
+        self.add_copy_spec("/etc/kubernetes")
c81b6a
+        self.add_copy_spec("/etc/etcd")
c81b6a
+        self.add_copy_spec("/var/run/flannel")
c81b6a
+
c81b6a
+        # Kubernetes master info
c81b6a
+        self.add_cmd_output("kubectl version")
c81b6a
+        self.add_cmd_output("kubectl get -o json pods")
c81b6a
+        self.add_cmd_output("kubectl get -o json minions")
c81b6a
+        self.add_cmd_output("kubectl get -o json replicationController")
c81b6a
+        self.add_cmd_output("kubectl get -o json events")
c81b6a
+        self.add_cmd_output("journalctl -r -u kubelet")
c81b6a
+
c81b6a
+        # etcd
c81b6a
+        self.add_cmd_output("curl http://127.0.0.1:4001/version")
c81b6a
+        self.add_cmd_output("curl http://127.0.0.1:4001/v2/members")
c81b6a
+        self.add_cmd_output("curl http://127.0.0.1:4001/v2/stats/leader")
c81b6a
+        self.add_cmd_output("curl http://127.0.0.1:4001/v2/stats/self")
c81b6a
+        self.add_cmd_output("curl http://127.0.0.1:4001/v2/stats/store")
c81b6a
+
c81b6a
+
c81b6a
+# vim: et ts=5 sw=4
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 550dda69c6c5d527498ad928a29c466fad4e250e Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 17:32:35 +0000
c81b6a
Subject: [PATCH 22/38] [docs] fix documentation of --sysroot parameter
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 man/en/sosreport.1 | 2 +-
c81b6a
 1 file changed, 1 insertion(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/man/en/sosreport.1 b/man/en/sosreport.1
c81b6a
index f36f845..eac9047 100644
c81b6a
--- a/man/en/sosreport.1
c81b6a
+++ b/man/en/sosreport.1
c81b6a
@@ -12,7 +12,7 @@ sosreport \- Collect and package diagnostic and support data
c81b6a
           [--no-report] [--config-file conf]\fR
c81b6a
           [--batch] [--build] [--debug]\fR
c81b6a
           [--name name] [--case-id id] [--ticket-number nr]
c81b6a
-          [-s|--sysroot]\fR
c81b6a
+          [-s|--sysroot SYSROOT]\fR
c81b6a
           [-c|--chroot {auto|always|never}\fR
c81b6a
           [--tmp-dir directory]\fR
c81b6a
           [-p|--profile profile-name]\fR
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From d7cf8535a3403fe6050e0905bef2b4429e595664 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 15:32:03 -0500
c81b6a
Subject: [PATCH 23/38] [utilities] add chroot support to shell_out()
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/utilities.py | 5 +++--
c81b6a
 1 file changed, 3 insertions(+), 2 deletions(-)
c81b6a
c81b6a
diff --git a/sos/utilities.py b/sos/utilities.py
c81b6a
index a82ac7c..6475619 100644
c81b6a
--- a/sos/utilities.py
c81b6a
+++ b/sos/utilities.py
c81b6a
@@ -186,11 +186,12 @@ def import_module(module_fqname, superclasses=None):
c81b6a
     return modules
c81b6a
 
c81b6a
 
c81b6a
-def shell_out(cmd, timeout=30, runat=None):
c81b6a
+def shell_out(cmd, timeout=30, chroot=None, runat=None):
c81b6a
     """Shell out to an external command and return the output or the empty
c81b6a
     string in case of error.
c81b6a
     """
c81b6a
-    return sos_get_command_output(cmd, timeout=timeout, chdir=runat)['output']
c81b6a
+    return sos_get_command_output(cmd, timeout=timeout,
c81b6a
+                                  chroot=chroot, chdir=runat)['output']
c81b6a
 
c81b6a
 
c81b6a
 class ImporterHelper(object):
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From a1a1fd6cfcdb62d7af7744bb5710a2c7d5b4262a Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 15:33:18 -0500
c81b6a
Subject: [PATCH 24/38] [policies] make PackageManager and Policy sysroot-aware
c81b6a
c81b6a
Add methods to Policy to get the host root file system path and
c81b6a
to test if sos is running in a container and allow Policy classes
c81b6a
to pass a chroot path into the PackageManager constructor in order
c81b6a
to obtain package data from the chroot.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/policies/__init__.py | 22 ++++++++++++++++++++--
c81b6a
 1 file changed, 20 insertions(+), 2 deletions(-)
c81b6a
c81b6a
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
c81b6a
index 4657614..cea4c09 100644
c81b6a
--- a/sos/policies/__init__.py
c81b6a
+++ b/sos/policies/__init__.py
c81b6a
@@ -57,11 +57,14 @@ class PackageManager(object):
c81b6a
 
c81b6a
     query_command = None
c81b6a
     timeout = 30
c81b6a
+    chroot = None
c81b6a
 
c81b6a
-    def __init__(self, query_command=None):
c81b6a
+    def __init__(self, query_command=None, chroot=None):
c81b6a
         self.packages = {}
c81b6a
         if query_command:
c81b6a
             self.query_command = query_command
c81b6a
+        if chroot:
c81b6a
+            self.chroot = chroot
c81b6a
 
c81b6a
     def all_pkgs_by_name(self, name):
c81b6a
         """
c81b6a
@@ -93,7 +96,11 @@ class PackageManager(object):
c81b6a
                           version': 'major.minor.version'}}
c81b6a
         """
c81b6a
         if self.query_command:
c81b6a
-            pkg_list = shell_out(self.query_command, self.timeout).splitlines()
c81b6a
+            cmd = self.query_command
c81b6a
+            pkg_list = shell_out(
c81b6a
+                cmd, timeout=self.timeout, chroot=self.chroot
c81b6a
+            ).splitlines()
c81b6a
+
c81b6a
             for pkg in pkg_list:
c81b6a
                 if '|' not in pkg:
c81b6a
                     continue
c81b6a
@@ -145,6 +152,9 @@ No changes will be made to system configuration.
c81b6a
     vendor_text = ""
c81b6a
     PATH = ""
c81b6a
 
c81b6a
+    _in_container = False
c81b6a
+    _host_sysroot = '/'
c81b6a
+
c81b6a
     def __init__(self):
c81b6a
         """Subclasses that choose to override this initializer should call
c81b6a
         super() to ensure that they get the required platform bits attached.
c81b6a
@@ -180,6 +190,14 @@ No changes will be made to system configuration.
c81b6a
         """
c81b6a
         return False
c81b6a
 
c81b6a
+    def in_container(self):
c81b6a
+        """ Returns True if sos is running inside a container environment.
c81b6a
+        """
c81b6a
+        return self._in_container
c81b6a
+
c81b6a
+    def host_sysroot(self):
c81b6a
+        return self._host_sysroot
c81b6a
+
c81b6a
     def dist_version(self):
c81b6a
         """
c81b6a
         Return the OS version
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 63805ed15d63ddfebb06cd03f96f310bbf60d3b2 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 15:36:40 -0500
c81b6a
Subject: [PATCH 25/38] [policies] add container support to Red Hat policy
c81b6a
c81b6a
Check for the presence of container-specific environment variables
c81b6a
and set _host_sysroot if present:
c81b6a
c81b6a
  container_uuid=UUID
c81b6a
  HOST=/path
c81b6a
c81b6a
If both a container environment variable and HOST are present run
c81b6a
the PackageManager query command in a chroot.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/policies/redhat.py | 23 +++++++++++++++++++++--
c81b6a
 1 file changed, 21 insertions(+), 2 deletions(-)
c81b6a
c81b6a
diff --git a/sos/policies/redhat.py b/sos/policies/redhat.py
c81b6a
index d2f8db0..f1db8ac 100644
c81b6a
--- a/sos/policies/redhat.py
c81b6a
+++ b/sos/policies/redhat.py
c81b6a
@@ -38,13 +38,17 @@ class RedHatPolicy(LinuxPolicy):
c81b6a
     vendor = "Red Hat"
c81b6a
     vendor_url = "http://www.redhat.com/"
c81b6a
     _tmp_dir = "/var/tmp"
c81b6a
+    _rpmq_cmd = 'rpm -qa --queryformat "%{NAME}|%{VERSION}\\n"'
c81b6a
+    _in_container = False
c81b6a
+    _host_sysroot = '/'
c81b6a
 
c81b6a
     def __init__(self):
c81b6a
         super(RedHatPolicy, self).__init__()
c81b6a
         self.report_name = ""
c81b6a
         self.ticket_number = ""
c81b6a
-        self.package_manager = PackageManager(
c81b6a
-            'rpm -qa --queryformat "%{NAME}|%{VERSION}\\n"')
c81b6a
+        # need to set _host_sysroot before PackageManager()
c81b6a
+        sysroot = self._container_init()
c81b6a
+        self.package_manager = PackageManager(self._rpmq_cmd, chroot=sysroot)
c81b6a
         self.valid_subclasses = [RedHatPlugin]
c81b6a
 
c81b6a
         pkgs = self.package_manager.all_pkgs()
c81b6a
@@ -70,6 +74,17 @@ class RedHatPolicy(LinuxPolicy):
c81b6a
         Fedora, RHEL or other Red Hat distribution or False otherwise."""
c81b6a
         return False
c81b6a
 
c81b6a
+    def _container_init(self):
c81b6a
+        """Check if sos is running in a container and if a host sysroot
c81b6a
+        has been passed in the environment.
c81b6a
+        """
c81b6a
+        if ENV_CONTAINER_UUID in os.environ:
c81b6a
+            self._in_container = True
c81b6a
+        if ENV_HOST_SYSROOT in os.environ:
c81b6a
+            self._host_sysroot = os.environ[ENV_HOST_SYSROOT]
c81b6a
+        use_sysroot = self._in_container and self._host_sysroot != '/'
c81b6a
+        return self._host_sysroot if use_sysroot else None
c81b6a
+
c81b6a
     def runlevel_by_service(self, name):
c81b6a
         from subprocess import Popen, PIPE
c81b6a
         ret = []
c81b6a
@@ -100,6 +115,10 @@ class RedHatPolicy(LinuxPolicy):
c81b6a
     def get_local_name(self):
c81b6a
         return self.host_name()
c81b6a
 
c81b6a
+# Container environment variables on Red Hat systems.
c81b6a
+ENV_CONTAINER_UUID = 'container_uuid'
c81b6a
+ENV_HOST_SYSROOT = 'HOST'
c81b6a
+
c81b6a
 
c81b6a
 class RHELPolicy(RedHatPolicy):
c81b6a
     distro = "Red Hat Enterprise Linux"
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From f8a1746a871e560e548d21f1fc68067d452140a0 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 15:39:16 -0500
c81b6a
Subject: [PATCH 26/38] [sosreport] set SYSROOT by policy
c81b6a
c81b6a
If --sysroot is not given on the command line and
c81b6a
Policy.in_container() is True set sysroot automatically if
c81b6a
Policy.get_host_sysroot() is not '/'.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/sosreport.py | 13 ++++++++++---
c81b6a
 1 file changed, 10 insertions(+), 3 deletions(-)
c81b6a
c81b6a
diff --git a/sos/sosreport.py b/sos/sosreport.py
c81b6a
index a0b89e7..835d828 100644
c81b6a
--- a/sos/sosreport.py
c81b6a
+++ b/sos/sosreport.py
c81b6a
@@ -648,7 +648,7 @@ class SoSOptions(object):
c81b6a
                           help="Disable HTML/XML reporting", default=False)
c81b6a
         parser.add_option("-s", "--sysroot", action="store", dest="sysroot",
c81b6a
                           help="system root directory path (default='/')",
c81b6a
-                          default="/")
c81b6a
+                          default=None)
c81b6a
         parser.add_option("-c", "--chroot", action="store", dest="chroot",
c81b6a
                           help="chroot executed commands to SYSROOT "
c81b6a
                                "[auto, always, never] (default=auto)",
c81b6a
@@ -705,11 +705,18 @@ class SoSReport(object):
c81b6a
         self.tempfile_util = TempFileUtil(self.tmpdir)
c81b6a
         self._set_directories()
c81b6a
 
c81b6a
+        self._setup_logging()
c81b6a
+
c81b6a
+        msg = "default"
c81b6a
+        host_sysroot = self.policy.host_sysroot()
c81b6a
         # set alternate system root directory
c81b6a
         if self.opts.sysroot:
c81b6a
+            msg = "cmdline"
c81b6a
             self.sysroot = self.opts.sysroot
c81b6a
-
c81b6a
-        self._setup_logging()
c81b6a
+        elif self.policy.in_container() and host_sysroot != os.sep:
c81b6a
+            msg = "policy"
c81b6a
+            self.sysroot = host_sysroot
c81b6a
+        self.soslog.debug("set sysroot to '%s' (%s)" % (self.sysroot, msg))
c81b6a
 
c81b6a
         if self.opts.chroot not in chroot_modes:
c81b6a
             self.soslog.error("invalid chroot mode: %s" % self.opts.chroot)
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From fac57721ae10dafec909c4fac408f42ebe23d4ac Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 16:14:01 -0500
c81b6a
Subject: [PATCH 27/38] [firewalld] work around command hangs in container
c81b6a
 environments
c81b6a
c81b6a
Add a 10s timeout to firewalld-cmd execution to avoid long dbus
c81b6a
timeouts in docker containers.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/firewalld.py | 4 +++-
c81b6a
 1 file changed, 3 insertions(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/plugins/firewalld.py b/sos/plugins/firewalld.py
c81b6a
index 98d011b..8eeb2b6 100644
c81b6a
--- a/sos/plugins/firewalld.py
c81b6a
+++ b/sos/plugins/firewalld.py
c81b6a
@@ -35,9 +35,11 @@ class FirewallD(Plugin, RedHatPlugin):
c81b6a
             "/etc/sysconfig/firewalld"
c81b6a
         ])
c81b6a
 
c81b6a
+        # use a 10s timeout to workaround dbus problems in
c81b6a
+        # docker containers.
c81b6a
         self.add_cmd_output([
c81b6a
             "firewall-cmd --list-all-zones",
c81b6a
             "firewall-cmd --permanent --list-all-zones"
c81b6a
-        ])
c81b6a
+        ], timeout=10)
c81b6a
 
c81b6a
 # vim: et ts=4 sw=4
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 6a239ddeb5de9a04e7e7081ea6425d89dddda3f5 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Mon, 26 Jan 2015 17:13:24 -0500
c81b6a
Subject: [PATCH 28/38] [policies] pass --sysroot down to policy classes
c81b6a
c81b6a
Policies that don't auto-detect a container environment with a
c81b6a
host file system need to pass the value of --sysroot down to the
c81b6a
PackageManager class in order to obtain package details from the
c81b6a
chroot environment.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/policies/__init__.py | 11 ++++++-----
c81b6a
 sos/policies/debian.py   |  4 ++--
c81b6a
 sos/policies/redhat.py   | 18 +++++++++++-------
c81b6a
 sos/policies/ubuntu.py   |  4 ++--
c81b6a
 sos/sosreport.py         |  2 +-
c81b6a
 5 files changed, 22 insertions(+), 17 deletions(-)
c81b6a
c81b6a
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
c81b6a
index cea4c09..a403bb9 100644
c81b6a
--- a/sos/policies/__init__.py
c81b6a
+++ b/sos/policies/__init__.py
c81b6a
@@ -28,7 +28,7 @@ def import_policy(name):
c81b6a
         return None
c81b6a
 
c81b6a
 
c81b6a
-def load(cache={}):
c81b6a
+def load(cache={}, sysroot=None):
c81b6a
     if 'policy' in cache:
c81b6a
         return cache.get('policy')
c81b6a
 
c81b6a
@@ -37,7 +37,7 @@ def load(cache={}):
c81b6a
     for module in helper.get_modules():
c81b6a
         for policy in import_policy(module):
c81b6a
             if policy.check():
c81b6a
-                cache['policy'] = policy()
c81b6a
+                cache['policy'] = policy(sysroot=sysroot)
c81b6a
 
c81b6a
     if 'policy' not in cache:
c81b6a
         cache['policy'] = GenericPolicy()
c81b6a
@@ -155,7 +155,7 @@ No changes will be made to system configuration.
c81b6a
     _in_container = False
c81b6a
     _host_sysroot = '/'
c81b6a
 
c81b6a
-    def __init__(self):
c81b6a
+    def __init__(self, sysroot=None):
c81b6a
         """Subclasses that choose to override this initializer should call
c81b6a
         super() to ensure that they get the required platform bits attached.
c81b6a
         super(SubClass, self).__init__(). Policies that require runtime
c81b6a
@@ -167,6 +167,7 @@ No changes will be made to system configuration.
c81b6a
         self.package_manager = PackageManager()
c81b6a
         self._valid_subclasses = []
c81b6a
         self.set_exec_path()
c81b6a
+        self._host_sysroot = sysroot
c81b6a
 
c81b6a
     def get_valid_subclasses(self):
c81b6a
         return [IndependentPlugin] + self._valid_subclasses
c81b6a
@@ -372,8 +373,8 @@ class LinuxPolicy(Policy):
c81b6a
     vendor = "None"
c81b6a
     PATH = "/bin:/sbin:/usr/bin:/usr/sbin"
c81b6a
 
c81b6a
-    def __init__(self):
c81b6a
-        super(LinuxPolicy, self).__init__()
c81b6a
+    def __init__(self, sysroot=None):
c81b6a
+        super(LinuxPolicy, self).__init__(sysroot=sysroot)
c81b6a
 
c81b6a
     def get_preferred_hash_algorithm(self):
c81b6a
         checksum = "md5"
c81b6a
diff --git a/sos/policies/debian.py b/sos/policies/debian.py
c81b6a
index acb5b85..e56e546 100644
c81b6a
--- a/sos/policies/debian.py
c81b6a
+++ b/sos/policies/debian.py
c81b6a
@@ -16,8 +16,8 @@ class DebianPolicy(LinuxPolicy):
c81b6a
     PATH = "/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games" \
c81b6a
            + ":/usr/local/sbin:/usr/local/bin"
c81b6a
 
c81b6a
-    def __init__(self):
c81b6a
-        super(DebianPolicy, self).__init__()
c81b6a
+    def __init__(self, sysroot=None):
c81b6a
+        super(DebianPolicy, self).__init__(sysroot=sysroot)
c81b6a
         self.report_name = ""
c81b6a
         self.ticket_number = ""
c81b6a
         self.package_manager = PackageManager(
c81b6a
diff --git a/sos/policies/redhat.py b/sos/policies/redhat.py
c81b6a
index f1db8ac..f20359d 100644
c81b6a
--- a/sos/policies/redhat.py
c81b6a
+++ b/sos/policies/redhat.py
c81b6a
@@ -42,12 +42,16 @@ class RedHatPolicy(LinuxPolicy):
c81b6a
     _in_container = False
c81b6a
     _host_sysroot = '/'
c81b6a
 
c81b6a
-    def __init__(self):
c81b6a
-        super(RedHatPolicy, self).__init__()
c81b6a
+    def __init__(self, sysroot=None):
c81b6a
+        super(RedHatPolicy, self).__init__(sysroot=sysroot)
c81b6a
         self.report_name = ""
c81b6a
         self.ticket_number = ""
c81b6a
         # need to set _host_sysroot before PackageManager()
c81b6a
-        sysroot = self._container_init()
c81b6a
+        if sysroot:
c81b6a
+            self._container_init()
c81b6a
+            self._host_sysroot = sysroot
c81b6a
+        else:
c81b6a
+            sysroot = self._container_init()
c81b6a
         self.package_manager = PackageManager(self._rpmq_cmd, chroot=sysroot)
c81b6a
         self.valid_subclasses = [RedHatPlugin]
c81b6a
 
c81b6a
@@ -145,8 +149,8 @@ No changes will be made to system configuration.
c81b6a
 %(vendor_text)s
c81b6a
 """)
c81b6a
 
c81b6a
-    def __init__(self):
c81b6a
-        super(RHELPolicy, self).__init__()
c81b6a
+    def __init__(self, sysroot=None):
c81b6a
+        super(RHELPolicy, self).__init__(sysroot=sysroot)
c81b6a
 
c81b6a
     @classmethod
c81b6a
     def check(self):
c81b6a
@@ -192,8 +196,8 @@ class FedoraPolicy(RedHatPolicy):
c81b6a
     vendor = "the Fedora Project"
c81b6a
     vendor_url = "https://fedoraproject.org/"
c81b6a
 
c81b6a
-    def __init__(self):
c81b6a
-        super(FedoraPolicy, self).__init__()
c81b6a
+    def __init__(self, sysroot=None):
c81b6a
+        super(FedoraPolicy, self).__init__(sysroot=sysroot)
c81b6a
 
c81b6a
     @classmethod
c81b6a
     def check(self):
c81b6a
diff --git a/sos/policies/ubuntu.py b/sos/policies/ubuntu.py
c81b6a
index 0dd2ea2..f236421 100644
c81b6a
--- a/sos/policies/ubuntu.py
c81b6a
+++ b/sos/policies/ubuntu.py
c81b6a
@@ -9,8 +9,8 @@ class UbuntuPolicy(DebianPolicy):
c81b6a
     vendor = "Ubuntu"
c81b6a
     vendor_url = "http://www.ubuntu.com/"
c81b6a
 
c81b6a
-    def __init__(self):
c81b6a
-        super(UbuntuPolicy, self).__init__()
c81b6a
+    def __init__(self, sysroot=None):
c81b6a
+        super(UbuntuPolicy, self).__init__(sysroot=sysroot)
c81b6a
         self.valid_subclasses = [UbuntuPlugin, DebianPlugin]
c81b6a
 
c81b6a
     @classmethod
c81b6a
diff --git a/sos/sosreport.py b/sos/sosreport.py
c81b6a
index 835d828..ad13a2d 100644
c81b6a
--- a/sos/sosreport.py
c81b6a
+++ b/sos/sosreport.py
c81b6a
@@ -687,7 +687,7 @@ class SoSReport(object):
c81b6a
         self._read_config()
c81b6a
 
c81b6a
         try:
c81b6a
-            self.policy = sos.policies.load()
c81b6a
+            self.policy = sos.policies.load(sysroot=self.opts.sysroot)
c81b6a
         except KeyboardInterrupt:
c81b6a
             self._exit(0)
c81b6a
 
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From d34bf4982f8a627901f6538be3b9e52cc30fe91b Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Tue, 27 Jan 2015 11:40:06 +0000
c81b6a
Subject: [PATCH 29/38] [sosoptions] ensure '_sysroot' and '_chroot' are
c81b6a
 initialised
c81b6a
c81b6a
Make sure the sysroot and chroot members of the SoSOptions object
c81b6a
are initialised to prevent exceptions when these are not set on
c81b6a
the command line:
c81b6a
c81b6a
  sosreport
c81b6a
  Traceback (most recent call last):
c81b6a
    File "/usr/sbin/sosreport", line 25, in <module>
c81b6a
      main(sys.argv[1:])
c81b6a
    File "/usr/lib/python2.7/site-packages/sos/sosreport.py", line 1490, in main
c81b6a
      sos = SoSReport(args)
c81b6a
    File "/usr/lib/python2.7/site-packages/sos/sosreport.py", line 673, in __init__
c81b6a
      self.policy = sos.policies.load(sysroot=self.opts.sysroot)
c81b6a
    File "/usr/lib/python2.7/site-packages/sos/policies/__init__.py", line 40, in load
c81b6a
      cache['policy'] = policy(sysroot=sysroot)
c81b6a
    File "/usr/lib/python2.7/site-packages/sos/policies/redhat.py", line 192, in __init__
c81b6a
      super(FedoraPolicy, self).__init__(sysroot=sysroot)
c81b6a
    File "/usr/lib/python2.7/site-packages/sos/policies/redhat.py", line 58, in __init__
c81b6a
      if self.package_manager.all_pkgs()['filesystem']['version'][0] == '3':
c81b6a
    File "/usr/lib/python2.7/site-packages/sos/policies/__init__.py", line 116, in all_pkgs
c81b6a
      self.packages = self.get_pkg_list()
c81b6a
    File "/usr/lib/python2.7/site-packages/sos/policies/__init__.py", line 99, in get_pkg_list
c81b6a
      pkg_list = shell_out(cmd, chroot=self.chroot).splitlines()
c81b6a
    File "/usr/lib/python2.7/site-packages/sos/utilities.py", line 191, in shell_out
c81b6a
      return sos_get_command_output(cmd, chroot=chroot, chdir=runat)['output']
c81b6a
    File "/usr/lib/python2.7/site-packages/sos/utilities.py", line 156, in sos_get_command_output
c81b6a
      raise e
c81b6a
  OSError: [Errno 1] Operation not permitted: '/'
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/sosreport.py | 2 ++
c81b6a
 1 file changed, 2 insertions(+)
c81b6a
c81b6a
diff --git a/sos/sosreport.py b/sos/sosreport.py
c81b6a
index ad13a2d..27c756f 100644
c81b6a
--- a/sos/sosreport.py
c81b6a
+++ b/sos/sosreport.py
c81b6a
@@ -251,6 +251,8 @@ class SoSOptions(object):
c81b6a
     _config_file = ""
c81b6a
     _tmp_dir = ""
c81b6a
     _report = True
c81b6a
+    _sysroot = None
c81b6a
+    _chroot = 'auto'
c81b6a
     _compression_type = 'auto'
c81b6a
 
c81b6a
     _options = None
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From c0858c2c87f246283f6c59b7bf7f64f7dea73a82 Mon Sep 17 00:00:00 2001
c81b6a
From: Neependra Khare <nkhare@redhat.com>
c81b6a
Date: Tue, 27 Jan 2015 15:54:23 +0000
c81b6a
Subject: [PATCH 30/38] [etcd] split etcd functionality from kubernetes into
c81b6a
 new plugin
c81b6a
c81b6a
Signed-off-by: Neependra Khare <nkhare@redhat.com>
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/etcd.py       | 36 ++++++++++++++++++++++++++++++++++++
c81b6a
 sos/plugins/kubernetes.py |  8 --------
c81b6a
 2 files changed, 36 insertions(+), 8 deletions(-)
c81b6a
 create mode 100644 sos/plugins/etcd.py
c81b6a
c81b6a
diff --git a/sos/plugins/etcd.py b/sos/plugins/etcd.py
c81b6a
new file mode 100644
c81b6a
index 0000000..69edca0
c81b6a
--- /dev/null
c81b6a
+++ b/sos/plugins/etcd.py
c81b6a
@@ -0,0 +1,36 @@
c81b6a
+# Copyright (C) 2015 Red Hat, Inc. Neependra Khare <nkhare@redhat.com>
c81b6a
+# Copyright (C) 2015 Red Hat, Inc. Bryn M. Reeves <bmr@redhat.com>
c81b6a
+# This program is free software; you can redistribute it and/or modify
c81b6a
+# it under the terms of the GNU General Public License as published by
c81b6a
+# the Free Software Foundation; either version 2 of the License, or
c81b6a
+# (at your option) any later version.
c81b6a
+
c81b6a
+# This program is distributed in the hope that it will be useful,
c81b6a
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
c81b6a
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
c81b6a
+# GNU General Public License for more details.
c81b6a
+
c81b6a
+# You should have received a copy of the GNU General Public License
c81b6a
+# along with this program; if not, write to the Free Software
c81b6a
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
c81b6a
+
c81b6a
+from sos.plugins import Plugin, RedHatPlugin
c81b6a
+
c81b6a
+
c81b6a
+class etcd(Plugin, RedHatPlugin):
c81b6a
+
c81b6a
+    """etcd plugin
c81b6a
+    """
c81b6a
+
c81b6a
+    def setup(self):
c81b6a
+        self.add_copy_spec("/etc/etcd")
c81b6a
+
c81b6a
+        self.add_cmd_output("curl http://localhost:4001/version")
c81b6a
+        self.add_cmd_output("curl http://localhost:4001/v2/members")
c81b6a
+        self.add_cmd_output("curl http://localhost:4001/v2/stats/leader")
c81b6a
+        self.add_cmd_output("curl http://localhost:4001/v2/stats/self")
c81b6a
+        self.add_cmd_output("curl http://localhost:4001/v2/stats/store")
c81b6a
+        self.add_cmd_output("ls -lR /var/lib/etcd/")
c81b6a
+
c81b6a
+
c81b6a
+# vim: et ts=5 sw=4
c81b6a
diff --git a/sos/plugins/kubernetes.py b/sos/plugins/kubernetes.py
c81b6a
index af3f3a6..289d784 100644
c81b6a
--- a/sos/plugins/kubernetes.py
c81b6a
+++ b/sos/plugins/kubernetes.py
c81b6a
@@ -24,7 +24,6 @@ class kubernetes(Plugin, RedHatPlugin):
c81b6a
 
c81b6a
     def setup(self):
c81b6a
         self.add_copy_spec("/etc/kubernetes")
c81b6a
-        self.add_copy_spec("/etc/etcd")
c81b6a
         self.add_copy_spec("/var/run/flannel")
c81b6a
 
c81b6a
         # Kubernetes master info
c81b6a
@@ -35,12 +34,5 @@ class kubernetes(Plugin, RedHatPlugin):
c81b6a
         self.add_cmd_output("kubectl get -o json events")
c81b6a
         self.add_cmd_output("journalctl -r -u kubelet")
c81b6a
 
c81b6a
-        # etcd
c81b6a
-        self.add_cmd_output("curl http://127.0.0.1:4001/version")
c81b6a
-        self.add_cmd_output("curl http://127.0.0.1:4001/v2/members")
c81b6a
-        self.add_cmd_output("curl http://127.0.0.1:4001/v2/stats/leader")
c81b6a
-        self.add_cmd_output("curl http://127.0.0.1:4001/v2/stats/self")
c81b6a
-        self.add_cmd_output("curl http://127.0.0.1:4001/v2/stats/store")
c81b6a
-
c81b6a
 
c81b6a
 # vim: et ts=5 sw=4
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 2ddc706c7219d0b891304fcb066dea865f8516b5 Mon Sep 17 00:00:00 2001
c81b6a
From: Neependra Khare <nkhare@redhat.com>
c81b6a
Date: Tue, 27 Jan 2015 15:58:32 +0000
c81b6a
Subject: [PATCH 31/38] [kubernetes] add services and pod logs collection
c81b6a
c81b6a
Signed-off-by: Neependra Khare <nkhare@redhat.com>
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/kubernetes.py | 12 ++++++++++++
c81b6a
 1 file changed, 12 insertions(+)
c81b6a
c81b6a
diff --git a/sos/plugins/kubernetes.py b/sos/plugins/kubernetes.py
c81b6a
index 289d784..9c2df5e 100644
c81b6a
--- a/sos/plugins/kubernetes.py
c81b6a
+++ b/sos/plugins/kubernetes.py
c81b6a
@@ -22,6 +22,8 @@ class kubernetes(Plugin, RedHatPlugin):
c81b6a
     """Kubernetes plugin
c81b6a
     """
c81b6a
 
c81b6a
+    option_list = [("podslog", "capture logs for pods", 'slow', False)]
c81b6a
+
c81b6a
     def setup(self):
c81b6a
         self.add_copy_spec("/etc/kubernetes")
c81b6a
         self.add_copy_spec("/var/run/flannel")
c81b6a
@@ -30,9 +32,19 @@ class kubernetes(Plugin, RedHatPlugin):
c81b6a
         self.add_cmd_output("kubectl version")
c81b6a
         self.add_cmd_output("kubectl get -o json pods")
c81b6a
         self.add_cmd_output("kubectl get -o json minions")
c81b6a
+        self.add_cmd_output("kubectl get -o json services")
c81b6a
         self.add_cmd_output("kubectl get -o json replicationController")
c81b6a
         self.add_cmd_output("kubectl get -o json events")
c81b6a
         self.add_cmd_output("journalctl -r -u kubelet")
c81b6a
 
c81b6a
+        if self.get_option('podslog'):
c81b6a
+            result = self.get_command_output("kubectl get pods")
c81b6a
+            if result['status'] == 0:
c81b6a
+                for line in result['output'].splitlines()[1:]:
c81b6a
+                    pod_name = line.split(" ")[0]
c81b6a
+                    self.add_cmd_output([
c81b6a
+                        "{0} log {1}".format("kubectl", pod_name)
c81b6a
+                    ])
c81b6a
+
c81b6a
 
c81b6a
 # vim: et ts=5 sw=4
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 259897b60b0e4ee00585a8d73521fa4e291eb8da Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Tue, 27 Jan 2015 12:54:31 -0500
c81b6a
Subject: [PATCH 32/38] [policies/redhat] automatically set tmp_dir in
c81b6a
 containers
c81b6a
c81b6a
Now that policies have the infrastructure to detect that they
c81b6a
are running in a container and to use the HOST environment
c81b6a
variable if present enable automatic setting of self._tmp_dir in
c81b6a
appriate container environments.
c81b6a
c81b6a
This causes reports to be automatically written to $HOST/var/tmp
c81b6a
when $HOST is set while still allowing users to override the tmp
c81b6a
path manually by specificying --tmp-dir=PATH.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/policies/redhat.py | 7 +++++--
c81b6a
 1 file changed, 5 insertions(+), 2 deletions(-)
c81b6a
c81b6a
diff --git a/sos/policies/redhat.py b/sos/policies/redhat.py
c81b6a
index f20359d..3cf40b3 100644
c81b6a
--- a/sos/policies/redhat.py
c81b6a
+++ b/sos/policies/redhat.py
c81b6a
@@ -79,14 +79,17 @@ class RedHatPolicy(LinuxPolicy):
c81b6a
         return False
c81b6a
 
c81b6a
     def _container_init(self):
c81b6a
-        """Check if sos is running in a container and if a host sysroot
c81b6a
-        has been passed in the environment.
c81b6a
+        """Check if sos is running in a container and perform container
c81b6a
+        specific initialisation based on ENV_HOST_SYSROOT.
c81b6a
         """
c81b6a
         if ENV_CONTAINER_UUID in os.environ:
c81b6a
             self._in_container = True
c81b6a
         if ENV_HOST_SYSROOT in os.environ:
c81b6a
             self._host_sysroot = os.environ[ENV_HOST_SYSROOT]
c81b6a
         use_sysroot = self._in_container and self._host_sysroot != '/'
c81b6a
+        if use_sysroot:
c81b6a
+            host_tmp_dir = os.path.abspath(self._host_sysroot + self._tmp_dir)
c81b6a
+            self._tmp_dir = host_tmp_dir
c81b6a
         return self._host_sysroot if use_sysroot else None
c81b6a
 
c81b6a
     def runlevel_by_service(self, name):
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From b880c6cb4668815b97841e6532450144a5f825f1 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Tue, 27 Jan 2015 13:53:34 -0500
c81b6a
Subject: [PATCH 33/38] [policies/redhat] add Red Hat Atomic Host policy
c81b6a
c81b6a
Add a new policy for the Red Hat Atomic Host.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/policies/redhat.py | 39 ++++++++++++++++++++++++++++++++++++++-
c81b6a
 1 file changed, 38 insertions(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/policies/redhat.py b/sos/policies/redhat.py
c81b6a
index 3cf40b3..9decd0a 100644
c81b6a
--- a/sos/policies/redhat.py
c81b6a
+++ b/sos/policies/redhat.py
c81b6a
@@ -37,6 +37,7 @@ class RedHatPolicy(LinuxPolicy):
c81b6a
     distro = "Red Hat"
c81b6a
     vendor = "Red Hat"
c81b6a
     vendor_url = "http://www.redhat.com/"
c81b6a
+    _redhat_release = '/etc/redhat-release'
c81b6a
     _tmp_dir = "/var/tmp"
c81b6a
     _rpmq_cmd = 'rpm -qa --queryformat "%{NAME}|%{VERSION}\\n"'
c81b6a
     _in_container = False
c81b6a
@@ -159,7 +160,7 @@ No changes will be made to system configuration.
c81b6a
     def check(self):
c81b6a
         """This method checks to see if we are running on RHEL. It returns True
c81b6a
         or False."""
c81b6a
-        return (os.path.isfile('/etc/redhat-release')
c81b6a
+        return (os.path.isfile(self._redhat_release)
c81b6a
                 and not os.path.isfile('/etc/fedora-release'))
c81b6a
 
c81b6a
     def dist_version(self):
c81b6a
@@ -193,6 +194,42 @@ No changes will be made to system configuration.
c81b6a
         return self.rhn_username() or self.host_name()
c81b6a
 
c81b6a
 
c81b6a
+class RedHatAtomicPolicy(RHELPolicy):
c81b6a
+    distro = "Red Hat Atomic Host"
c81b6a
+    msg = _("""\
c81b6a
+This command will collect diagnostic and configuration \
c81b6a
+information from this %(distro)s system.
c81b6a
+
c81b6a
+An archive containing the collected information will be \
c81b6a
+generated in %(tmpdir)s and may be provided to a %(vendor)s \
c81b6a
+support representative.
c81b6a
+
c81b6a
+Any information provided to %(vendor)s will be treated in \
c81b6a
+accordance with the published support policies at:\n
c81b6a
+  %(vendor_url)s
c81b6a
+
c81b6a
+The generated archive may contain data considered sensitive \
c81b6a
+and its content should be reviewed by the originating \
c81b6a
+organization before being passed to any third party.
c81b6a
+%(vendor_text)s
c81b6a
+""")
c81b6a
+
c81b6a
+    @classmethod
c81b6a
+    def check(self):
c81b6a
+        atomic = False
c81b6a
+        if ENV_HOST_SYSROOT not in os.environ:
c81b6a
+            return atomic
c81b6a
+        host_release = os.environ[ENV_HOST_SYSROOT] + self._redhat_release
c81b6a
+        if not os.path.exists(host_release):
c81b6a
+            return False
c81b6a
+        try:
c81b6a
+            for line in open(host_release, "r").read().splitlines():
c81b6a
+                atomic |= 'Atomic' in line
c81b6a
+        except:
c81b6a
+            pass
c81b6a
+        return atomic
c81b6a
+
c81b6a
+
c81b6a
 class FedoraPolicy(RedHatPolicy):
c81b6a
 
c81b6a
     distro = "Fedora"
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From cd0d5c1f06a69c115e674f87254cc7e056c9891a Mon Sep 17 00:00:00 2001
c81b6a
From: Jeremy Eder <jeder@redhat.com>
c81b6a
Date: Wed, 28 Jan 2015 12:54:14 +0000
c81b6a
Subject: [PATCH 34/38] [docker] add 'docker' to the package list for Red Hat
c81b6a
 distros
c81b6a
c81b6a
The docker package is named 'docker-io' in Fedora and 'docker'
c81b6a
in RHEL and other downstream products. Add the 'docker' name to
c81b6a
the package list in RedHatDocker to ensure the plugin runs.
c81b6a
c81b6a
Signed-off-by: Jeremy Eder <jeder@redhat.com>
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/docker.py | 2 +-
c81b6a
 1 file changed, 1 insertion(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/plugins/docker.py b/sos/plugins/docker.py
c81b6a
index 3e5bd4a..c5ea8a0 100644
c81b6a
--- a/sos/plugins/docker.py
c81b6a
+++ b/sos/plugins/docker.py
c81b6a
@@ -54,7 +54,7 @@ class Docker(Plugin):
c81b6a
 
c81b6a
 class RedHatDocker(Docker, RedHatPlugin):
c81b6a
 
c81b6a
-    packages = ('docker-io',)
c81b6a
+    packages = ('docker', 'docker-io')
c81b6a
 
c81b6a
     def setup(self):
c81b6a
         super(RedHatDocker, self).setup()
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From d4ae63afbf211a7234b7605bef037d827263bf70 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Wed, 28 Jan 2015 22:54:48 +0000
c81b6a
Subject: [PATCH 35/38] [plugins] automatically re-try chroot'ed commands in
c81b6a
 the host
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 12 +++++++++++-
c81b6a
 sos/utilities.py        |  5 +----
c81b6a
 2 files changed, 12 insertions(+), 5 deletions(-)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index b206470..7fa7ac6 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -520,13 +520,23 @@ class Plugin(object):
c81b6a
             root = self.sysroot
c81b6a
         else:
c81b6a
             root = None
c81b6a
+
c81b6a
         result = sos_get_command_output(prog, timeout=timeout, stderr=stderr,
c81b6a
                                         chroot=root, chdir=runat)
c81b6a
+
c81b6a
         if result['status'] == 124:
c81b6a
             self._log_warn("command '%s' timed out after %ds"
c81b6a
                            % (prog, timeout))
c81b6a
-        # 126 means 'found but not executable'
c81b6a
+
c81b6a
+        # command not found or not runnable
c81b6a
         if result['status'] == 126 or result['status'] == 127:
c81b6a
+            # automatically retry chroot'ed commands in the host namespace
c81b6a
+            if chroot and self.commons['cmdlineopts'].chroot != 'always':
c81b6a
+                self._log_info("command '%s' not found in %s - "
c81b6a
+                               "re-trying in host root"
c81b6a
+                               % (prog.split()[0], root))
c81b6a
+                return self.get_command_output(prog, timeout=timeout,
c81b6a
+                                               chroot=False, runat=runat)
c81b6a
             self._log_debug("could not run '%s': command not found" % prog)
c81b6a
         return result
c81b6a
 
c81b6a
diff --git a/sos/utilities.py b/sos/utilities.py
c81b6a
index 6475619..d3a1048 100644
c81b6a
--- a/sos/utilities.py
c81b6a
+++ b/sos/utilities.py
c81b6a
@@ -151,16 +151,13 @@ def sos_get_command_output(command, timeout=300, stderr=False,
c81b6a
                   stderr=STDOUT if stderr else PIPE,
c81b6a
                   bufsize=-1, env=cmd_env, close_fds=True,
c81b6a
                   preexec_fn=_child_prep_fn)
c81b6a
+        stdout, stderr = p.communicate()
c81b6a
     except OSError as e:
c81b6a
         if e.errno == errno.ENOENT:
c81b6a
             return {'status': 127, 'output': ""}
c81b6a
         else:
c81b6a
             raise e
c81b6a
 
c81b6a
-    stdout, stderr = p.communicate()
c81b6a
-
c81b6a
-    # Required hack while we still pass shell=True to Popen; a Popen
c81b6a
-    # call with shell=False for a non-existant binary will raise OSError.
c81b6a
     if p.returncode == 126 or p.returncode == 127:
c81b6a
         stdout = six.binary_type(b"")
c81b6a
 
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 6fe240a31c01d63957ce548f4415ca55859dc071 Mon Sep 17 00:00:00 2001
c81b6a
From: Neependra Khare <nkhare@redhat.com>
c81b6a
Date: Thu, 29 Jan 2015 18:23:53 +0000
c81b6a
Subject: [PATCH 36/38] [kubernetes] add journal output for kube services
c81b6a
c81b6a
Add journalctl output for the following kubernetes units:
c81b6a
c81b6a
  kube-apiserver
c81b6a
  kube-controller-manager
c81b6a
  kube-scheduler
c81b6a
  kube-proxy
c81b6a
c81b6a
Signed-off-by: Neependra Khare <nkhare@redhat.com>
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/kubernetes.py | 4 ++++
c81b6a
 1 file changed, 4 insertions(+)
c81b6a
c81b6a
diff --git a/sos/plugins/kubernetes.py b/sos/plugins/kubernetes.py
c81b6a
index 9c2df5e..38faeb2 100644
c81b6a
--- a/sos/plugins/kubernetes.py
c81b6a
+++ b/sos/plugins/kubernetes.py
c81b6a
@@ -36,6 +36,10 @@ class kubernetes(Plugin, RedHatPlugin):
c81b6a
         self.add_cmd_output("kubectl get -o json replicationController")
c81b6a
         self.add_cmd_output("kubectl get -o json events")
c81b6a
         self.add_cmd_output("journalctl -r -u kubelet")
c81b6a
+        self.add_cmd_output("journalctl -r -u kube-apiserver")
c81b6a
+        self.add_cmd_output("journalctl -r -u kube-controller-manager")
c81b6a
+        self.add_cmd_output("journalctl -r -u kube-scheduler")
c81b6a
+        self.add_cmd_output("journalctl -r -u kube-proxy")
c81b6a
 
c81b6a
         if self.get_option('podslog'):
c81b6a
             result = self.get_command_output("kubectl get pods")
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 8d69d74a5be6dac29fad2ab5d7206a0485969924 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Thu, 29 Jan 2015 20:30:31 +0000
c81b6a
Subject: [PATCH 37/38] [plugins] do not strip SYSROOT when copying link
c81b6a
 targets
c81b6a
c81b6a
The abspath() call in _copy_symlink returns a host-relative path
c81b6a
(when SYSROOT is not '/'). Pass this directly to _do_copy_path()
c81b6a
without stripping the SYSROOT path component.
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 3 ++-
c81b6a
 1 file changed, 2 insertions(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index 7fa7ac6..a7c0146 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -265,7 +265,8 @@ class Plugin(object):
c81b6a
         # the target stored in the original symlink
c81b6a
         linkdest = os.readlink(srcpath)
c81b6a
         dest = os.path.join(os.path.dirname(srcpath), linkdest)
c81b6a
-        # absolute path to the link target
c81b6a
+        # Absolute path to the link target. If SYSROOT != '/' this path
c81b6a
+        # is relative to the host root file system.
c81b6a
         absdest = os.path.normpath(dest)
c81b6a
         # adjust the target used inside the report to always be relative
c81b6a
         if os.path.isabs(linkdest):
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
c81b6a
From 76c04a773f1927359d264f59e4fbcc9363424882 Mon Sep 17 00:00:00 2001
c81b6a
From: "Bryn M. Reeves" <bmr@redhat.com>
c81b6a
Date: Thu, 29 Jan 2015 22:00:10 +0000
c81b6a
Subject: [PATCH 38/38] [plugins] trim leading '../' from links when sysroot is
c81b6a
 set
c81b6a
c81b6a
When SYSROOT is not '/' relative symlinks need to be trimmed to
c81b6a
remove the extra leading '../' returned by
c81b6a
abspath('/sysroot/...').
c81b6a
c81b6a
Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 3 +++
c81b6a
 sos/sosreport.py        | 1 +
c81b6a
 2 files changed, 4 insertions(+)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index a7c0146..a06c0b1 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -271,6 +271,9 @@ class Plugin(object):
c81b6a
         # adjust the target used inside the report to always be relative
c81b6a
         if os.path.isabs(linkdest):
c81b6a
             reldest = os.path.relpath(linkdest, os.path.dirname(srcpath))
c81b6a
+            # trim leading /sysroot
c81b6a
+            if self.use_sysroot():
c81b6a
+                reldest = reldest[len(os.sep + os.pardir):]
c81b6a
             self._log_debug("made link target '%s' relative as '%s'"
c81b6a
                             % (linkdest, reldest))
c81b6a
         else:
c81b6a
diff --git a/sos/sosreport.py b/sos/sosreport.py
c81b6a
index 27c756f..567e3df 100644
c81b6a
--- a/sos/sosreport.py
c81b6a
+++ b/sos/sosreport.py
c81b6a
@@ -1226,6 +1226,7 @@ class SoSReport(object):
c81b6a
                     self.ui_log.error(" %s while setting up plugins"
c81b6a
                                       % e.strerror)
c81b6a
                     self.ui_log.error("")
c81b6a
+                    self._exit(1)
c81b6a
                 if self.raise_plugins:
c81b6a
                     raise
c81b6a
                 self._log_plugin_exception(plugname, "setup")
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a
c81b6a
From bb6f546603d8e3199bda0bfe0f9b23f1da1cb8c9 Mon Sep 17 00:00:00 2001
c81b6a
From: Pavel Moravec <pmoravec@redhat.com>
c81b6a
Date: Mon, 13 Jul 2015 15:03:37 +0200
c81b6a
Subject: [PATCH] [plugin] pass stderr through _collect_cmd_output
c81b6a
c81b6a
Commit f06efd6 removed passing stderr in _collect_cmd_output to
c81b6a
get_cmd_output_now. That prevents passing stderr=False in several
c81b6a
scenarios.
c81b6a
c81b6a
This fix adds the argument to be passed back.
c81b6a
c81b6a
Resolves: #600
c81b6a
c81b6a
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
c81b6a
---
c81b6a
 sos/plugins/__init__.py | 2 +-
c81b6a
 1 file changed, 1 insertion(+), 1 deletion(-)
c81b6a
c81b6a
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
c81b6a
index a06c0b1..aed7496 100644
c81b6a
--- a/sos/plugins/__init__.py
c81b6a
+++ b/sos/plugins/__init__.py
c81b6a
@@ -696,7 +696,7 @@ class Plugin(object):
c81b6a
             self._log_info("collecting output of '%s'" % prog)
c81b6a
             self.get_cmd_output_now(prog, suggest_filename=suggest_filename,
c81b6a
                                     root_symlink=root_symlink, timeout=timeout,
c81b6a
-                                    chroot=chroot, runat=runat)
c81b6a
+                                    stderr=stderr, chroot=chroot, runat=runat)
c81b6a
 
c81b6a
     def _collect_strings(self):
c81b6a
         for string, file_name in self.copy_strings:
c81b6a
-- 
c81b6a
1.8.3.1
c81b6a