diff options
-rw-r--r-- | man/en/sos-report.1 | 13 | ||||
-rw-r--r-- | sos/report/__init__.py | 74 |
2 files changed, 84 insertions, 3 deletions
diff --git a/man/en/sos-report.1 b/man/en/sos-report.1 index 36b337df..e8efc8f8 100644 --- a/man/en/sos-report.1 +++ b/man/en/sos-report.1 @@ -14,7 +14,7 @@ sos report \- Collect and package diagnostic and support data [--preset preset] [--add-preset add_preset]\fR [--del-preset del_preset] [--desc description]\fR [--batch] [--build] [--debug] [--dry-run]\fR - [--label label] [--case-id id]\fR + [--estimate-only] [--label label] [--case-id id]\fR [--threads threads]\fR [--plugin-timeout TIMEOUT]\fR [--cmd-timeout TIMEOUT]\fR @@ -317,6 +317,17 @@ output, or string data from the system. The resulting logs may be used to understand the actions that sos would have taken without the dry run option. .TP +.B \--estimate-only +Estimate disk space requirements when running sos report. This can be valuable +to prevent sosreport working dir to consume all free disk space. No plugin data +is available at the end. + +Plugins will be collected sequentially, size of collected files and commands outputs +will be calculated and the plugin files will be immediatelly deleted prior execution +of the next plugin. This still can consume whole free disk space, though. Please note, +size estimations may not be accurate for highly utilized systems due to changes between +an estimate and a real execution. +.TP .B \--upload If specified, attempt to upload the resulting archive to a vendor defined location. diff --git a/sos/report/__init__.py b/sos/report/__init__.py index 82484f1d..b033f621 100644 --- a/sos/report/__init__.py +++ b/sos/report/__init__.py @@ -86,6 +86,7 @@ class SoSReport(SoSComponent): 'desc': '', 'domains': [], 'dry_run': False, + 'estimate_only': False, 'experimental': False, 'enable_plugins': [], 'keywords': [], @@ -137,6 +138,7 @@ class SoSReport(SoSComponent): self._args = args self.sysroot = "/" self.preset = None + self.estimated_plugsizes = {} self.print_header() self._set_debug() @@ -223,6 +225,11 @@ class SoSReport(SoSComponent): help="Description for a new preset",) report_grp.add_argument("--dry-run", action="store_true", help="Run plugins but do not collect data") + report_grp.add_argument("--estimate-only", action="store_true", + help="Approximate disk space requirements for " + "a real sos run; disables --clean and " + "--collect, sets --threads=1 and " + "--no-postproc") report_grp.add_argument("--experimental", action="store_true", dest="experimental", default=False, help="enable experimental plugins") @@ -702,6 +709,33 @@ class SoSReport(SoSComponent): for opt in plugin.options: self.all_options.append(plugin.options[opt]) + def _set_estimate_only(self): + # set estimate-only mode by enforcing some options settings + # and return a corresponding log messages string + msg = "\nEstimate-only mode enabled" + ext_msg = [] + if self.opts.threads > 1: + ext_msg += ["--threads=%s overriden to 1" % self.opts.threads, ] + self.opts.threads = 1 + if not self.opts.build: + ext_msg += ["--build enabled", ] + self.opts.build = True + if not self.opts.no_postproc: + ext_msg += ["--no-postproc enabled", ] + self.opts.no_postproc = True + if self.opts.clean: + ext_msg += ["--clean disabled", ] + self.opts.clean = False + if self.opts.upload: + ext_msg += ["--upload* options disabled", ] + self.opts.upload = False + if ext_msg: + msg += ", which overrides some options:\n " + "\n ".join(ext_msg) + else: + msg += "." + msg += "\n\n" + return msg + def _report_profiles_and_plugins(self): self.ui_log.info("") if len(self.loaded_plugins): @@ -875,10 +909,12 @@ class SoSReport(SoSComponent): return True def batch(self): + msg = self.policy.get_msg() + if self.opts.estimate_only: + msg += self._set_estimate_only() if self.opts.batch: - self.ui_log.info(self.policy.get_msg()) + self.ui_log.info(msg) else: - msg = self.policy.get_msg() msg += _("Press ENTER to continue, or CTRL-C to quit.\n") try: input(msg) @@ -1011,6 +1047,22 @@ class SoSReport(SoSComponent): self.running_plugs.remove(plugin[1]) self.loaded_plugins[plugin[0]-1][1].set_timeout_hit() pool._threads.clear() + if self.opts.estimate_only: + from pathlib import Path + tmpdir_path = Path(self.archive.get_tmp_dir()) + self.estimated_plugsizes[plugin[1]] = sum( + [f.stat().st_size for f in tmpdir_path.glob('**/*') + if (os.path.isfile(f) and not os.path.islink(f))]) + # remove whole tmp_dir content - including "sos_commands" and + # similar dirs that will be re-created on demand by next plugin + # if needed; it is less error-prone approach than skipping + # deletion of some dirs but deleting their content + for f in os.listdir(self.archive.get_tmp_dir()): + f = os.path.join(self.archive.get_tmp_dir(), f) + if os.path.isdir(f): + rmtree(f) + else: + os.unlink(f) return True def collect_plugin(self, plugin): @@ -1330,6 +1382,24 @@ class SoSReport(SoSComponent): self.policy.display_results(archive, directory, checksum, map_file=map_file) + if self.opts.estimate_only: + from sos.utilities import get_human_readable + _sum = get_human_readable(sum(self.estimated_plugsizes.values())) + self.ui_log.info("Estimated disk space requirement for whole " + "uncompressed sos report directory: %s" % _sum) + bigplugins = sorted(self.estimated_plugsizes.items(), + key=lambda x: x[1], reverse=True)[:3] + bp_out = ", ".join("%s: %s" % + (p, get_human_readable(v, precision=0)) + for p, v in bigplugins) + self.ui_log.info("Three biggest plugins: %s" % bp_out) + self.ui_log.info("") + self.ui_log.info("Please note the estimation is relevant to the " + "current options.") + self.ui_log.info("Be aware that the real disk space requirements " + "might be different.") + self.ui_log.info("") + if self.opts.upload or self.opts.upload_url: if not self.opts.build: try: |