diff options
-rw-r--r-- | sos/report/__init__.py | 2 | ||||
-rw-r--r-- | sos/report/plugins/__init__.py | 69 | ||||
-rw-r--r-- | tests/report_tests/plugin_tests/collet_manual_tests.py (renamed from tests/report_tests/plugin_tests/string_collection_tests.py) | 10 | ||||
-rw-r--r-- | tests/unittests/plugin_tests.py | 19 |
4 files changed, 83 insertions, 17 deletions
diff --git a/sos/report/__init__.py b/sos/report/__init__.py index 4770ec12..bbf73d21 100644 --- a/sos/report/__init__.py +++ b/sos/report/__init__.py @@ -1328,7 +1328,7 @@ class SoSReport(SoSComponent): ) self.ui_progress(status_line) try: - plug.collect() + plug.collect_plugin() # certain exceptions can cause either of these lists to no # longer contain the plugin, which will result in sos hanging # so we can't blindly call remove() on these two. diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py index c77627ab..42032d50 100644 --- a/sos/report/plugins/__init__.py +++ b/sos/report/plugins/__init__.py @@ -17,6 +17,7 @@ from sos.utilities import (sos_get_command_output, import_module, grep, recursive_dict_values_by_key) from sos.archive import P_FILE +import contextlib import os import glob import re @@ -615,6 +616,7 @@ class Plugin(): self.manifest.add_list('files', []) self.manifest.add_field('strings', {}) self.manifest.add_field('containers', {}) + self.manifest.add_field('collections', {}) def set_default_cmd_environment(self, env_vars): """ @@ -3076,7 +3078,7 @@ class Plugin(): def _collect_tailed_files(self): for _file, _size in self._tail_files_list: - self._log_info("collecting tail of '%s' due to size limit" % _file) + self._log_info(f"collecting tail of '{_file}' due to size limit") file_name = _file if file_name[0] == os.sep: file_name = file_name.lstrip(os.sep) @@ -3105,7 +3107,71 @@ class Plugin(): self._log_debug("could not add string '%s': %s" % (file_name, e)) + def _collect_manual(self): + """Kick off manual collections performed by the plugin. These manual + collections are anything the plugin collects outside of existing + files and/or command output. Anything the plugin manually compiles or + constructs for data that is included in the final archive. + + Plugins will need to define these collections by overriding the + ``collect()`` method, similar to how plugins define their own + ``setup()`` methods. + """ + try: + self.collect() + except Exception as err: + self._log_error(f"Error during plugin collections: {err}") + def collect(self): + """If a plugin needs to manually compile some data for a collection, + that should be specified here by overriding this method. + + These collections are run last during a plugin's execution, and as such + are more likely to be interrupted by timeouts than file or command + output collections. + """ + pass + + @contextlib.contextmanager + def collection_file(self, fname, subdir=None, tags=[]): + """Handles creating and managing files within a plugin's subdirectory + within the archive, and is intended to be used to save manually + compiled data generated during a plugin's ``_collect_manual()`` step + of the collection phase. + + Plugins should call this method using a ``with`` context manager. + + :param fname: The name of the file within the plugin directory + :type fname: ``str`` + + :param subdir: If needed, specify a subdir to write the file to + :type subdir: ``str`` + + :param tags: Tags to be added to this file in the manifest + :type tags: ``str`` or ``list`` of ``str``s + """ + try: + start = time() + _pfname = self._make_command_filename(fname, subdir=subdir) + self.archive.check_path(_pfname, P_FILE) + _name = self.archive.dest_path(_pfname) + _file = open(_name, 'w') + self._log_debug(f"manual collection file opened: {_name}") + yield _file + _file.close() + end = time() + run = end - start + self._log_info(f"manual collection '{fname}' finished in {run}") + if isinstance(tags, str): + tags = [tags] + self.manifest.collections[fname.split('.')[0]] = { + 'filepath': _pfname, + 'tags': tags + } + except Exception as err: + self._log_info(f"Error with collection file '{fname}': {err}") + + def collect_plugin(self): """Collect the data for a plugin.""" start = time() self._collect_copy_specs() @@ -3113,6 +3179,7 @@ class Plugin(): self._collect_tailed_files() self._collect_cmds() self._collect_strings() + self._collect_manual() fields = (self.name(), time() - start) self._log_debug("collected plugin '%s' in %s" % fields) diff --git a/tests/report_tests/plugin_tests/string_collection_tests.py b/tests/report_tests/plugin_tests/collet_manual_tests.py index 05091a2e..fdcda526 100644 --- a/tests/report_tests/plugin_tests/string_collection_tests.py +++ b/tests/report_tests/plugin_tests/collet_manual_tests.py @@ -10,8 +10,8 @@ from sos_tests import StageOneReportTest -class CollectStringTest(StageOneReportTest): - """Test to ensure that add_string_as_file() is working for plugins that +class CollectManualTest(StageOneReportTest): + """Test to ensure that collect() is working for plugins that directly call it as part of their collections :avocado: tags=stageone @@ -30,8 +30,8 @@ class CollectStringTest(StageOneReportTest): def test_no_strings_dir(self): self.assertFileNotCollected('sos_strings/') - def test_manifest_strings_correct(self): + def test_manifest_collections_correct(self): pkgman = self.get_plugin_manifest('unpackaged') - self.assertTrue(pkgman['strings']['unpackaged']) + self.assertTrue(pkgman['collections']['unpackaged']) pyman = self.get_plugin_manifest('python') - self.assertTrue(pyman['strings']['digests_json']) + self.assertTrue(pyman['collections']['digests']) diff --git a/tests/unittests/plugin_tests.py b/tests/unittests/plugin_tests.py index 1d7cc96d..8170a1dd 100644 --- a/tests/unittests/plugin_tests.py +++ b/tests/unittests/plugin_tests.py @@ -296,7 +296,7 @@ class PluginTests(unittest.TestCase): }) p.archive = MockArchive() p.setup() - p.collect() + p.collect_plugin() self.assertEquals(p.archive.m, {}) def test_postproc_default_on(self): @@ -358,10 +358,10 @@ class AddCopySpecTests(unittest.TestCase): self.mp.sysroot = '/' fn = create_file(2) # create 2MB file, consider a context manager self.mp.add_copy_spec(fn, 1) - content, fname, _tags = self.mp.copy_strings[0] - self.assertTrue("tailed" in fname) + fname, _size = self.mp._tail_files_list[0] + self.assertTrue(fname == fn) self.assertTrue("tmp" in fname) - self.assertEquals(1024 * 1024, len(content)) + self.assertEquals(1024 * 1024, _size) os.unlink(fn) def test_bad_filename(self): @@ -388,10 +388,9 @@ class AddCopySpecTests(unittest.TestCase): create_file(2, dir=tmpdir) create_file(2, dir=tmpdir) self.mp.add_copy_spec(tmpdir + "/*", 1) - self.assertEquals(len(self.mp.copy_strings), 1) - content, fname, _tags = self.mp.copy_strings[0] - self.assertTrue("tailed" in fname) - self.assertEquals(1024 * 1024, len(content)) + self.assertEquals(len(self.mp._tail_files_list), 1) + fname, _size = self.mp._tail_files_list[0] + self.assertEquals(1024 * 1024, _size) shutil.rmtree(tmpdir) def test_multiple_files_no_limit(self): @@ -450,7 +449,7 @@ class RegexSubTests(unittest.TestCase): def test_no_replacements(self): self.mp.sysroot = '/' self.mp.add_copy_spec(j("tail_test.txt")) - self.mp.collect() + self.mp.collect_plugin() replacements = self.mp.do_file_sub( j("tail_test.txt"), r"wont_match", "foobar") self.assertEquals(0, replacements) @@ -459,7 +458,7 @@ class RegexSubTests(unittest.TestCase): # test uses absolute paths self.mp.sysroot = '/' self.mp.add_copy_spec(j("tail_test.txt")) - self.mp.collect() + self.mp.collect_plugin() replacements = self.mp.do_file_sub( j("tail_test.txt"), r"(tail)", "foobar") self.assertEquals(1, replacements) |