Blame SOURCES/sos-bz2011537-estimate-only-option.patch

92cd8f
From 5b245b1e449c6a05d09034bcb8290bffded79327 Mon Sep 17 00:00:00 2001
92cd8f
From: Pavel Moravec <pmoravec@redhat.com>
92cd8f
Date: Wed, 8 Sep 2021 17:04:58 +0200
92cd8f
Subject: [PATCH] [report] Implement --estimate-only
92cd8f
92cd8f
Add report option --estimate-only to estimate disk space requirements
92cd8f
when running a sos report.
92cd8f
92cd8f
Resolves: #2673
92cd8f
92cd8f
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
92cd8f
---
92cd8f
 man/en/sos-report.1    | 13 +++++++-
92cd8f
 sos/report/__init__.py | 74 ++++++++++++++++++++++++++++++++++++++++--
92cd8f
 2 files changed, 84 insertions(+), 3 deletions(-)
92cd8f
92cd8f
diff --git a/man/en/sos-report.1 b/man/en/sos-report.1
92cd8f
index 36b337df..e8efc8f8 100644
92cd8f
--- a/man/en/sos-report.1
92cd8f
+++ b/man/en/sos-report.1
92cd8f
@@ -14,7 +14,7 @@ sos report \- Collect and package diagnostic and support data
92cd8f
           [--preset preset] [--add-preset add_preset]\fR
92cd8f
           [--del-preset del_preset] [--desc description]\fR
92cd8f
           [--batch] [--build] [--debug] [--dry-run]\fR
92cd8f
-          [--label label] [--case-id id]\fR
92cd8f
+          [--estimate-only] [--label label] [--case-id id]\fR
92cd8f
           [--threads threads]\fR
92cd8f
           [--plugin-timeout TIMEOUT]\fR
92cd8f
           [--cmd-timeout TIMEOUT]\fR
92cd8f
@@ -317,6 +317,17 @@ output, or string data from the system. The resulting logs may be used
92cd8f
 to understand the actions that sos would have taken without the dry run
92cd8f
 option.
92cd8f
 .TP
92cd8f
+.B \--estimate-only
92cd8f
+Estimate disk space requirements when running sos report. This can be valuable
92cd8f
+to prevent sosreport working dir to consume all free disk space. No plugin data
92cd8f
+is available at the end.
92cd8f
+
92cd8f
+Plugins will be collected sequentially, size of collected files and commands outputs
92cd8f
+will be calculated and the plugin files will be immediatelly deleted prior execution
92cd8f
+of the next plugin. This still can consume whole free disk space, though. Please note,
92cd8f
+size estimations may not be accurate for highly utilized systems due to changes between
92cd8f
+an estimate and a real execution.
92cd8f
+.TP
92cd8f
 .B \--upload
92cd8f
 If specified, attempt to upload the resulting archive to a vendor defined location.
92cd8f
 
92cd8f
diff --git a/sos/report/__init__.py b/sos/report/__init__.py
92cd8f
index 82484f1d..b033f621 100644
92cd8f
--- a/sos/report/__init__.py
92cd8f
+++ b/sos/report/__init__.py
92cd8f
@@ -86,6 +86,7 @@ class SoSReport(SoSComponent):
92cd8f
         'desc': '',
92cd8f
         'domains': [],
92cd8f
         'dry_run': False,
92cd8f
+        'estimate_only': False,
92cd8f
         'experimental': False,
92cd8f
         'enable_plugins': [],
92cd8f
         'keywords': [],
92cd8f
@@ -137,6 +138,7 @@ class SoSReport(SoSComponent):
92cd8f
         self._args = args
92cd8f
         self.sysroot = "/"
92cd8f
         self.preset = None
92cd8f
+        self.estimated_plugsizes = {}
92cd8f
 
92cd8f
         self.print_header()
92cd8f
         self._set_debug()
92cd8f
@@ -223,6 +225,11 @@ class SoSReport(SoSComponent):
92cd8f
                                 help="Description for a new preset",)
92cd8f
         report_grp.add_argument("--dry-run", action="store_true",
92cd8f
                                 help="Run plugins but do not collect data")
92cd8f
+        report_grp.add_argument("--estimate-only", action="store_true",
92cd8f
+                                help="Approximate disk space requirements for "
92cd8f
+                                     "a real sos run; disables --clean and "
92cd8f
+                                     "--collect, sets --threads=1 and "
92cd8f
+                                     "--no-postproc")
92cd8f
         report_grp.add_argument("--experimental", action="store_true",
92cd8f
                                 dest="experimental", default=False,
92cd8f
                                 help="enable experimental plugins")
92cd8f
@@ -700,6 +700,33 @@ class SoSReport(SoSComponent):
92cd8f
                 self.all_options.append((plugin, plugin_name, optname,
92cd8f
                                          optparm))
92cd8f
 
92cd8f
+    def _set_estimate_only(self):
92cd8f
+        # set estimate-only mode by enforcing some options settings
92cd8f
+        # and return a corresponding log messages string
92cd8f
+        msg = "\nEstimate-only mode enabled"
92cd8f
+        ext_msg = []
92cd8f
+        if self.opts.threads > 1:
92cd8f
+            ext_msg += ["--threads=%s overriden to 1" % self.opts.threads, ]
92cd8f
+            self.opts.threads = 1
92cd8f
+        if not self.opts.build:
92cd8f
+            ext_msg += ["--build enabled", ]
92cd8f
+            self.opts.build = True
92cd8f
+        if not self.opts.no_postproc:
92cd8f
+            ext_msg += ["--no-postproc enabled", ]
92cd8f
+            self.opts.no_postproc = True
92cd8f
+        if self.opts.clean:
92cd8f
+            ext_msg += ["--clean disabled", ]
92cd8f
+            self.opts.clean = False
92cd8f
+        if self.opts.upload:
92cd8f
+            ext_msg += ["--upload* options disabled", ]
92cd8f
+            self.opts.upload = False
92cd8f
+        if ext_msg:
92cd8f
+            msg += ", which overrides some options:\n  " + "\n  ".join(ext_msg)
92cd8f
+        else:
92cd8f
+            msg += "."
92cd8f
+        msg += "\n\n"
92cd8f
+        return msg
92cd8f
+
92cd8f
     def _report_profiles_and_plugins(self):
92cd8f
         self.ui_log.info("")
92cd8f
         if len(self.loaded_plugins):
92cd8f
@@ -875,10 +909,12 @@ class SoSReport(SoSComponent):
92cd8f
         return True
92cd8f
 
92cd8f
     def batch(self):
92cd8f
+        msg = self.policy.get_msg()
92cd8f
+        if self.opts.estimate_only:
92cd8f
+            msg += self._set_estimate_only()
92cd8f
         if self.opts.batch:
92cd8f
-            self.ui_log.info(self.policy.get_msg())
92cd8f
+            self.ui_log.info(msg)
92cd8f
         else:
92cd8f
-            msg = self.policy.get_msg()
92cd8f
             msg += _("Press ENTER to continue, or CTRL-C to quit.\n")
92cd8f
             try:
92cd8f
                 input(msg)
92cd8f
@@ -1011,6 +1047,22 @@ class SoSReport(SoSComponent):
92cd8f
                 self.running_plugs.remove(plugin[1])
92cd8f
                 self.loaded_plugins[plugin[0]-1][1].set_timeout_hit()
92cd8f
                 pool._threads.clear()
92cd8f
+        if self.opts.estimate_only:
92cd8f
+            from pathlib import Path
92cd8f
+            tmpdir_path = Path(self.archive.get_tmp_dir())
92cd8f
+            self.estimated_plugsizes[plugin[1]] = sum(
92cd8f
+                    [f.stat().st_size for f in tmpdir_path.glob('**/*')
92cd8f
+                     if (os.path.isfile(f) and not os.path.islink(f))])
92cd8f
+            # remove whole tmp_dir content - including "sos_commands" and
92cd8f
+            # similar dirs that will be re-created on demand by next plugin
92cd8f
+            # if needed; it is less error-prone approach than skipping
92cd8f
+            # deletion of some dirs but deleting their content
92cd8f
+            for f in os.listdir(self.archive.get_tmp_dir()):
92cd8f
+                f = os.path.join(self.archive.get_tmp_dir(), f)
92cd8f
+                if os.path.isdir(f):
92cd8f
+                    rmtree(f)
92cd8f
+                else:
92cd8f
+                    os.unlink(f)
92cd8f
         return True
92cd8f
 
92cd8f
     def collect_plugin(self, plugin):
92cd8f
@@ -1330,6 +1382,24 @@ class SoSReport(SoSComponent):
92cd8f
             self.policy.display_results(archive, directory, checksum,
92cd8f
                                         map_file=map_file)
92cd8f
 
92cd8f
+        if self.opts.estimate_only:
92cd8f
+            from sos.utilities import get_human_readable
92cd8f
+            _sum = get_human_readable(sum(self.estimated_plugsizes.values()))
92cd8f
+            self.ui_log.info("Estimated disk space requirement for whole "
92cd8f
+                             "uncompressed sos report directory: %s" % _sum)
92cd8f
+            bigplugins = sorted(self.estimated_plugsizes.items(),
92cd8f
+                                key=lambda x: x[1], reverse=True)[:3]
92cd8f
+            bp_out = ",  ".join("%s: %s" %
92cd8f
+                                (p, get_human_readable(v, precision=0))
92cd8f
+                                for p, v in bigplugins)
92cd8f
+            self.ui_log.info("Three biggest plugins:  %s" % bp_out)
92cd8f
+            self.ui_log.info("")
92cd8f
+            self.ui_log.info("Please note the estimation is relevant to the "
92cd8f
+                             "current options.")
92cd8f
+            self.ui_log.info("Be aware that the real disk space requirements "
92cd8f
+                             "might be different.")
92cd8f
+            self.ui_log.info("")
92cd8f
+
92cd8f
         if self.opts.upload or self.opts.upload_url:
92cd8f
             if not self.opts.build:
92cd8f
                 try:
92cd8f
-- 
92cd8f
2.31.1
92cd8f
92cd8f
From 7ae47e6c0717c0b56c3368008dd99a87f7f436d5 Mon Sep 17 00:00:00 2001
92cd8f
From: Pavel Moravec <pmoravec@redhat.com>
92cd8f
Date: Wed, 13 Oct 2021 20:21:16 +0200
92cd8f
Subject: [PATCH] [report] Count with sos_logs and sos_reports in
92cd8f
 --estimate-only
92cd8f
92cd8f
Currently, we estimate just plugins' disk space and ignore sos_logs
92cd8f
or sos_reports directories - although they can occupy nontrivial disk
92cd8f
space as well.
92cd8f
92cd8f
Resolves: #2723
92cd8f
92cd8f
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
92cd8f
---
92cd8f
 sos/report/__init__.py | 8 ++++++++
92cd8f
 1 file changed, 8 insertions(+)
92cd8f
92cd8f
diff --git a/sos/report/__init__.py b/sos/report/__init__.py
92cd8f
index e35c7e8d..7feb31ee 100644
92cd8f
--- a/sos/report/__init__.py
92cd8f
+++ b/sos/report/__init__.py
92cd8f
@@ -1380,6 +1380,14 @@ class SoSReport(SoSComponent):
92cd8f
 
92cd8f
         if self.opts.estimate_only:
92cd8f
             from sos.utilities import get_human_readable
92cd8f
+            from pathlib import Path
92cd8f
+            # add sos_logs, sos_reports dirs, etc., basically everything
92cd8f
+            # that remained in self.tmpdir after plugins' contents removal
92cd8f
+            # that still will be moved to the sos report final directory path
92cd8f
+            tmpdir_path = Path(self.tmpdir)
92cd8f
+            self.estimated_plugsizes['sos_logs_reports'] = sum(
92cd8f
+                    [f.stat().st_size for f in tmpdir_path.glob('**/*')])
92cd8f
+
92cd8f
             _sum = get_human_readable(sum(self.estimated_plugsizes.values()))
92cd8f
             self.ui_log.info("Estimated disk space requirement for whole "
92cd8f
                              "uncompressed sos report directory: %s" % _sum)
92cd8f
-- 
92cd8f
2.31.1
92cd8f
92cd8f
From 4293f3317505661e8f32ba94ad87310996fa1626 Mon Sep 17 00:00:00 2001
92cd8f
From: Eric Desrochers <eric.desrochers@canonical.com>
92cd8f
Date: Tue, 19 Oct 2021 12:18:40 -0400
92cd8f
Subject: [PATCH] [report] check for symlink before rmtree when opt
92cd8f
 estimate-only is use
92cd8f
92cd8f
Check if the dir is also symlink before performing rmtree()
92cd8f
method so that unlink() method can be used instead.
92cd8f
92cd8f
Traceback (most recent call last):
92cd8f
  File "./bin/sos", line 22, in <module>
92cd8f
    sos.execute()
92cd8f
  File "/tmp/sos/sos/__init__.py", line 186, in execute
92cd8f
    self._component.execute()
92cd8f
OSError: Cannot call rmtree on a symbolic link
92cd8f
92cd8f
Closes: #2727
92cd8f
92cd8f
Signed-off-by: Eric Desrochers <eric.desrochers@canonical.com>
92cd8f
---
92cd8f
 sos/report/__init__.py | 2 +-
92cd8f
 1 file changed, 1 insertion(+), 1 deletion(-)
92cd8f
92cd8f
diff --git a/sos/report/__init__.py b/sos/report/__init__.py
92cd8f
index 7feb31ee..1b5bc97d 100644
92cd8f
--- a/sos/report/__init__.py
92cd8f
+++ b/sos/report/__init__.py
92cd8f
@@ -1059,7 +1059,7 @@ class SoSReport(SoSComponent):
92cd8f
             # deletion of some dirs but deleting their content
92cd8f
             for f in os.listdir(self.archive.get_tmp_dir()):
92cd8f
                 f = os.path.join(self.archive.get_tmp_dir(), f)
92cd8f
-                if os.path.isdir(f):
92cd8f
+                if os.path.isdir(f) and not os.path.islink(f):
92cd8f
                     rmtree(f)
92cd8f
                 else:
92cd8f
                     os.unlink(f)
92cd8f
-- 
92cd8f
2.31.1
92cd8f
92cd8f
From 589d47c93257b55bc796ef6ac25b88c974ee3d72 Mon Sep 17 00:00:00 2001
92cd8f
From: Pavel Moravec <pmoravec@redhat.com>
92cd8f
Date: Mon, 8 Nov 2021 16:38:24 +0100
92cd8f
Subject: [PATCH] [report] Calculate sizes of dirs, symlinks and manifest in
92cd8f
 estimate mode
92cd8f
92cd8f
Enhance --estimate-mode to calculate sizes of also:
92cd8f
- symlinks
92cd8f
- directories themselves
92cd8f
- manifest.json file
92cd8f
92cd8f
Use os.lstat() method instead of os.stat() to properly calculate the
92cd8f
sizes (and not destinations of symlinks, e.g.).
92cd8f
92cd8f
Print five biggest plugins instead of three as sos logs and reports do
92cd8f
stand as one "plugin" in the list, often.
92cd8f
92cd8f
Resolves: #2752
92cd8f
92cd8f
Signed-off-by: Pavel Moravec <pmoravec@redhat.com>
92cd8f
---
92cd8f
 sos/report/__init__.py | 56 +++++++++++++++++++++---------------------
92cd8f
 1 file changed, 28 insertions(+), 28 deletions(-)
92cd8f
92cd8f
diff --git a/sos/report/__init__.py b/sos/report/__init__.py
92cd8f
index 10952566..a4c92acc 100644
92cd8f
--- a/sos/report/__init__.py
92cd8f
+++ b/sos/report/__init__.py
92cd8f
@@ -1050,8 +1050,7 @@ class SoSReport(SoSComponent):
92cd8f
             from pathlib import Path
92cd8f
             tmpdir_path = Path(self.archive.get_tmp_dir())
92cd8f
             self.estimated_plugsizes[plugin[1]] = sum(
92cd8f
-                    [f.stat().st_size for f in tmpdir_path.glob('**/*')
92cd8f
-                     if (os.path.isfile(f) and not os.path.islink(f))])
92cd8f
+                    [f.lstat().st_size for f in tmpdir_path.glob('**/*')])
92cd8f
             # remove whole tmp_dir content - including "sos_commands" and
92cd8f
             # similar dirs that will be re-created on demand by next plugin
92cd8f
             # if needed; it is less error-prone approach than skipping
92cd8f
@@ -1273,6 +1272,33 @@ class SoSReport(SoSComponent):
92cd8f
                 short_name='manifest.json'
92cd8f
             )
92cd8f
 
92cd8f
+        # print results in estimate mode (to include also just added manifest)
92cd8f
+        if self.opts.estimate_only:
92cd8f
+            from sos.utilities import get_human_readable
92cd8f
+            from pathlib import Path
92cd8f
+            # add sos_logs, sos_reports dirs, etc., basically everything
92cd8f
+            # that remained in self.tmpdir after plugins' contents removal
92cd8f
+            # that still will be moved to the sos report final directory path
92cd8f
+            tmpdir_path = Path(self.tmpdir)
92cd8f
+            self.estimated_plugsizes['sos_logs_reports'] = sum(
92cd8f
+                    [f.lstat().st_size for f in tmpdir_path.glob('**/*')])
92cd8f
+
92cd8f
+            _sum = get_human_readable(sum(self.estimated_plugsizes.values()))
92cd8f
+            self.ui_log.info("Estimated disk space requirement for whole "
92cd8f
+                             "uncompressed sos report directory: %s" % _sum)
92cd8f
+            bigplugins = sorted(self.estimated_plugsizes.items(),
92cd8f
+                                key=lambda x: x[1], reverse=True)[:5]
92cd8f
+            bp_out = ",  ".join("%s: %s" %
92cd8f
+                                (p, get_human_readable(v, precision=0))
92cd8f
+                                for p, v in bigplugins)
92cd8f
+            self.ui_log.info("Five biggest plugins:  %s" % bp_out)
92cd8f
+            self.ui_log.info("")
92cd8f
+            self.ui_log.info("Please note the estimation is relevant to the "
92cd8f
+                             "current options.")
92cd8f
+            self.ui_log.info("Be aware that the real disk space requirements "
92cd8f
+                             "might be different.")
92cd8f
+            self.ui_log.info("")
92cd8f
+
92cd8f
         # package up and compress the results
92cd8f
         if not self.opts.build:
92cd8f
             old_umask = os.umask(0o077)
92cd8f
@@ -1377,32 +1403,6 @@ class SoSReport(SoSComponent):
92cd8f
             self.policy.display_results(archive, directory, checksum,
92cd8f
                                         map_file=map_file)
92cd8f
 
92cd8f
-        if self.opts.estimate_only:
92cd8f
-            from sos.utilities import get_human_readable
92cd8f
-            from pathlib import Path
92cd8f
-            # add sos_logs, sos_reports dirs, etc., basically everything
92cd8f
-            # that remained in self.tmpdir after plugins' contents removal
92cd8f
-            # that still will be moved to the sos report final directory path
92cd8f
-            tmpdir_path = Path(self.tmpdir)
92cd8f
-            self.estimated_plugsizes['sos_logs_reports'] = sum(
92cd8f
-                    [f.stat().st_size for f in tmpdir_path.glob('**/*')])
92cd8f
-
92cd8f
-            _sum = get_human_readable(sum(self.estimated_plugsizes.values()))
92cd8f
-            self.ui_log.info("Estimated disk space requirement for whole "
92cd8f
-                             "uncompressed sos report directory: %s" % _sum)
92cd8f
-            bigplugins = sorted(self.estimated_plugsizes.items(),
92cd8f
-                                key=lambda x: x[1], reverse=True)[:3]
92cd8f
-            bp_out = ",  ".join("%s: %s" %
92cd8f
-                                (p, get_human_readable(v, precision=0))
92cd8f
-                                for p, v in bigplugins)
92cd8f
-            self.ui_log.info("Three biggest plugins:  %s" % bp_out)
92cd8f
-            self.ui_log.info("")
92cd8f
-            self.ui_log.info("Please note the estimation is relevant to the "
92cd8f
-                             "current options.")
92cd8f
-            self.ui_log.info("Be aware that the real disk space requirements "
92cd8f
-                             "might be different.")
92cd8f
-            self.ui_log.info("")
92cd8f
-
92cd8f
         if self.opts.upload or self.opts.upload_url:
92cd8f
             if not self.opts.build:
92cd8f
                 try:
92cd8f
-- 
92cd8f
2.31.1
92cd8f