diff options
-rw-r--r-- | sos/report/plugins/__init__.py | 17 | ||||
-rw-r--r-- | sos/utilities.py | 19 | ||||
-rw-r--r-- | tests/report_tests/plugin_tests/logs.py | 4 |
3 files changed, 35 insertions, 5 deletions
diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py index 4720bad3..0fabbbd0 100644 --- a/sos/report/plugins/__init__.py +++ b/sos/report/plugins/__init__.py @@ -1935,6 +1935,7 @@ class Plugin(object): 'parameters': cmd.split(' ')[1:], 'exec': cmd, 'filepath': None, + 'truncated': result['truncated'], 'return_code': result['status'], 'run_time': time() - start, 'tags': _tags @@ -1966,6 +1967,10 @@ class Plugin(object): self._log_debug("collected output of '%s' in %s (changes=%s)" % (cmd.split()[0], run_time, changes)) + if result['truncated']: + self._log_info("collected output of '%s' was truncated" + % cmd.split()[0]) + if suggest_filename: outfn = self._make_command_filename(suggest_filename, subdir) else: @@ -1973,10 +1978,22 @@ class Plugin(object): outfn_strip = outfn[len(self.commons['cmddir'])+1:] + if result['truncated']: + linkfn = outfn + outfn = outfn.replace('sos_commands', 'sos_strings') + '.tailed' + if binary: self.archive.add_binary(result['output'], outfn) else: self.archive.add_string(result['output'], outfn) + if result['truncated']: + # we need to manually build the relative path from the paths that + # exist within the build dir to properly drop these symlinks + _outfn_path = os.path.join(self.archive.get_archive_path(), outfn) + _link_path = os.path.join(self.archive.get_archive_path(), linkfn) + rpath = os.path.relpath(_outfn_path, _link_path) + rpath = rpath.replace('../', '', 1) + self.archive.add_link(rpath, linkfn) if root_symlink: self.archive.add_link(outfn, root_symlink) diff --git a/sos/utilities.py b/sos/utilities.py index 8c36b27a..fb13979a 100644 --- a/sos/utilities.py +++ b/sos/utilities.py @@ -159,12 +159,13 @@ def sos_get_command_output(command, timeout=300, stderr=False, raise SoSTimeoutError time.sleep(0.01) stdout = reader.get_contents() + truncated = reader.is_full while p.poll() is None: pass except OSError as e: if e.errno == errno.ENOENT: - return {'status': 127, 'output': ""} + return {'status': 127, 'output': "", 'truncated': ''} else: raise e @@ -173,7 +174,8 @@ def sos_get_command_output(command, timeout=300, stderr=False, return { 'status': p.returncode, - 'output': stdout + 'output': stdout, + 'truncated': truncated } @@ -224,11 +226,11 @@ class AsyncReader(threading.Thread): self.chan = channel self.binary = binary self.chunksize = 2048 - slots = None + self.slots = None if sizelimit: sizelimit = sizelimit * 1048576 # convert to bytes - slots = int(sizelimit / self.chunksize) - self.deque = deque(maxlen=slots) + self.slots = int(sizelimit / self.chunksize) + self.deque = deque(maxlen=self.slots) self.running = True self.start() @@ -264,6 +266,13 @@ class AsyncReader(threading.Thread): else: return b''.join(ln for ln in self.deque) + @property + def is_full(self): + """Checks if the deque is full, implying that output was truncated""" + if not self.slots: + return False + return len(self.deque) == self.slots + class ImporterHelper(object): """Provides a list of modules that can be imported in a package. diff --git a/tests/report_tests/plugin_tests/logs.py b/tests/report_tests/plugin_tests/logs.py index 9148bbff..cc6a8365 100644 --- a/tests/report_tests/plugin_tests/logs.py +++ b/tests/report_tests/plugin_tests/logs.py @@ -67,3 +67,7 @@ class LogsSizeLimitTest(StageTwoReportTest): assert jsize <= 105906176, "Collected journal is larger than 100MB" assert jsize > 27262976, "Collected journal limited by --log-size" + def test_journal_tailed_and_linked(self): + self.assertFileCollected('sos_strings/logs/journalctl_--no-pager_--catalog_--boot.tailed') + journ = self.get_name_in_archive('sos_commands/logs/journalctl_--no-pager_--catalog_--boot') + assert os.path.islink(journ), "Journal in sos_commands/logs is not a symlink" |