class TarFileArchiveTest(unittest.TestCase): def setUp(self): self.tf = TarFileArchive('test') def tearDown(self): os.unlink(self.tf.name()) def check_for_file(self, filename): rtf = tarfile.open('test.tar') rtf.getmember(filename) rtf.close() def test_create(self): self.tf.close() self.assertTrue(os.path.exists('test.tar')) def test_add_file(self): self.tf.add_file('tests/ziptest') self.tf.close() self.check_for_file('test/tests/ziptest') def test_add_dir(self): self.tf.add_file('tests/') self.tf.close() self.check_for_file('test/tests/ziptest') def test_add_renamed(self): self.tf.add_file('tests/ziptest', dest='tests/ziptest_renamed') self.tf.close() self.check_for_file('test/tests/ziptest_renamed') def test_add_renamed_dir(self): self.tf.add_file('tests/', 'tests_renamed/') self.tf.close() self.check_for_file('test/tests_renamed/ziptest') def test_add_string(self): self.tf.add_string('this is content', 'tests/string_test.txt') self.tf.close() self.check_for_file('test/tests/string_test.txt') def test_get_file(self): self.tf.add_string('this is my content', 'tests/string_test.txt') afp = self.tf.open_file('tests/string_test.txt') self.assertEquals('this is my content', afp.read()) def test_overwrite_file(self): self.tf.add_string('this is my content', 'tests/string_test.txt') self.tf.add_string('this is my new content', 'tests/string_test.txt') afp = self.tf.open_file('tests/string_test.txt') self.assertEquals('this is my new content', afp.read()) def test_make_link(self): self.tf.add_file('tests/ziptest') self.tf.add_link('tests/ziptest', 'link_name') self.tf.close() self.check_for_file('test/link_name') def test_compress(self): name = self.tf.compress("gzip")
class SoSReport(object): msg = _("""This utility will collect some detailed information about the hardware and setup of your %(distroa)s system. The information is collected and an archive is packaged under /tmp, which you can send to a support representative. %(distrob)s will use this information for diagnostic purposes ONLY and it will be considered confidential information. This process may take a while to complete. No changes will be made to your system. """ % {'distroa':__distro__, 'distrob':__distro__}) def __init__(self, opts): self.loaded_plugins = deque() self.skipped_plugins = deque() self.all_options = deque() self.xml_report = XmlReport() try: import signal signal.signal(signal.SIGTERM, self.get_exit_handler()) except Exception: pass # not available in java, but we don't care self.opts, self.args = parse_options(opts) self.tempfile_util = TempFileUtil(tmp_dir=self.opts.tmp_dir) self._set_debug() self._read_config() self.policy = sos.policies.load() self._is_root = self.policy.is_root() self._set_directories() self._setup_logging() self.policy.setCommons(self.get_commons()) self.print_header() self.load_plugins() self._set_tunables() self._check_for_unknown_plugins() self._set_plugin_options() def print_header(self): self.ui_log.info("\n%s\n" % _("sosreport (version %s)" % (__version__,))) def get_commons(self): return { 'cmddir': self.cmddir, 'logdir': self.logdir, 'rptdir': self.rptdir, 'soslog': self.soslog, 'policy': self.policy, 'verbosity': self.opts.verbosity, 'xmlreport': self.xml_report, 'cmdlineopts': self.opts, 'config': self.config } def get_temp_file(self): return self.tempfile_util.new() def _set_archive(self): if self.opts.compression_type not in ('auto', 'zip', 'bzip2', 'gzip', 'xz'): raise Exception("Invalid compression type specified. Options are: auto, zip, bzip2, gzip and xz") archive_name = os.path.join(self.opts.tmp_dir,self.policy.getArchiveName()) if self.opts.compression_type == 'auto': auto_archive = self.policy.preferedArchive() self.archive = auto_archive(archive_name) elif self.opts.compression_type == 'zip': self.archive = ZipFileArchive(archive_name) else: self.archive = TarFileArchive(archive_name) def _set_directories(self): self.cmddir = 'sos_commands' self.logdir = 'sos_logs' self.rptdir = 'sos_reports' def _set_debug(self): if self.opts.debug: sys.excepthook = self._exception self.raise_plugins = True else: self.raise_plugins = False @staticmethod def _exception(etype, eval_, etrace): """ Wrap exception in debugger if not in tty """ if hasattr(sys, 'ps1') or not sys.stderr.isatty(): # we are in interactive mode or we don't have a tty-like # device, so we call the default hook sys.__excepthook__(etype, eval_, etrace) else: import traceback, pdb # we are NOT in interactive mode, print the exception... traceback.print_exception(etype, eval_, etrace, limit=2, file=sys.stdout) print # ...then start the debugger in post-mortem mode. pdb.pm() def _exit(self, error=0): raise SystemExit() # sys.exit(error) def _exit_nice(self): for plugname, plugin in self.loaded_plugins: plugin.exit_please() self.ui_log.info("All processes ended, cleaning up.") self._exit(1) def get_exit_handler(self): def exit_handler(signum, frame): self._exit_nice() return exit_handler def _read_config(self): self.config = ConfigParser.ConfigParser() if self.opts.config_file: config_file = self.opts.config_file else: config_file = '/etc/sos.conf' try: self.config.readfp(open(config_file)) except IOError: pass def _setup_logging(self): if not sys.stdin.isatty(): self.opts.nocolors = True self.opts.batch = True # main soslog self.soslog = logging.getLogger('sos') self.soslog.setLevel(logging.DEBUG) self.sos_log_file = self.get_temp_file() self.sos_log_file.close() flog = logging.FileHandler(self.sos_log_file.name) flog.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) flog.setLevel(logging.INFO) self.soslog.addHandler(flog) if not self.opts.silent: console = logging.StreamHandler(sys.stderr) console.setFormatter(logging.Formatter('%(message)s')) if self.opts.verbosity > 1: console.setLevel(logging.DEBUG) elif self.opts.verbosity > 0: console.setLevel(logging.INFO) else: console.setLevel(logging.FATAL) self.soslog.addHandler(console) # ui log self.ui_log = logging.getLogger('sos.ui') self.ui_log.setLevel(logging.INFO) self.sos_ui_log_file = self.get_temp_file() self.sos_ui_log_file.close() ui_fhandler = logging.FileHandler(self.sos_ui_log_file.name) ui_fhandler.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(message)s')) self.ui_log.addHandler(ui_fhandler) if not self.opts.silent: ui_console = logging.StreamHandler(sys.stdout) ui_console.setFormatter(logging.Formatter('%(message)s')) ui_console.setLevel(logging.INFO) self.ui_log.addHandler(ui_console) # profile logging if self.opts.profiler: self.proflog = logging.getLogger('sosprofile') self.proflog.setLevel(logging.DEBUG) self.sos_profile_log_file = self.get_temp_file() plog = logging.FileHandler(self.sos_profile_log_file.name) plog.setFormatter(logging.Formatter('%(message)s')) plog.setLevel(logging.DEBUG) self.proflog.addHandler(plog) def _finish_logging(self): logging.shutdown() # the logging module seems to persist in the jython/jboss/eap world # so the handlers need to be removed for logger in [logging.getLogger(x) for x in ('sos', 'sosprofile', 'sos.ui')]: for h in logger.handlers: logger.removeHandler(h) if getattr(self, "sos_log_file", None): self.archive.add_file(self.sos_log_file.name, dest=os.path.join('sos_logs', 'sos.log')) if getattr(self, "sos_profile_log_file", None): self.archive.add_file(self.sos_profile_log_file.name, dest=os.path.join('sos_logs', 'profile.log')) if getattr(self, "sos_ui_log_file", None): self.archive.add_file(self.sos_ui_log_file.name, dest=os.path.join('sos_logs', 'ui.log')) def _get_disabled_plugins(self): disabled = [] if self.config.has_option("plugins", "disable"): disabled = [plugin.strip() for plugin in self.config.get("plugins", "disable").split(',')] return disabled def _is_skipped(self, plugin_name): return (plugin_name in self.opts.noplugins or plugin_name in self._get_disabled_plugins()) def _is_inactive(self, plugin_name, pluginClass): return (not pluginClass(self.get_commons()).checkenabled() and not plugin_name in self.opts.enableplugins and not plugin_name in self.opts.onlyplugins) def _is_not_default(self, plugin_name, pluginClass): return (not pluginClass(self.get_commons()).defaultenabled() and not plugin_name in self.opts.enableplugins and not plugin_name in self.opts.onlyplugins) def _is_not_specified(self, plugin_name): return (self.opts.onlyplugins and not plugin_name in self.opts.onlyplugins) def _skip(self, plugin_class, reason="unknown"): self.skipped_plugins.append(( plugin_class.name(), plugin_class(self.get_commons()), reason )) def _load(self, plugin_class): self.loaded_plugins.append(( plugin_class.name(), plugin_class(self.get_commons()) )) def load_plugins(self): helper = ImporterHelper(package_path=os.path.join('sos', 'plugins')) plugins = helper.get_modules() self.plugin_names = deque() # validate and load plugins for plug in plugins: plugbase, ext = os.path.splitext(plug) try: plugin_classes = import_plugin(plugbase) for plugin_class in plugin_classes: if not self.policy.validatePlugin(plugin_class): self.soslog.debug(_("plugin %s does not validate, skipping") % plug) self._skip(plugin_class, "does not validate") continue if plugin_class.requires_root and not self._is_root: self.soslog.debug(_("plugin %s requires root permissions to execute, skipping") % plug) self._skip(plugin_class, "requires root") continue # plug-in is valid, let's decide whether run it or not self.plugin_names.append(plugbase) if any((self._is_skipped(plugbase), self._is_inactive(plugbase, plugin_class), self._is_not_default(plugbase, plugin_class), self._is_not_specified(plugbase), )): self._skip(plugin_class, "inactive") continue self._load(plugin_class) except Exception, e: self.soslog.warning(_("plugin %s does not install, skipping: %s") % (plug, e)) if self.raise_plugins: raise