diff options
57 files changed, 276 insertions, 233 deletions
diff --git a/plugins_overview.py b/plugins_overview.py index 520cf300..96484cb4 100644 --- a/plugins_overview.py +++ b/plugins_overview.py @@ -46,7 +46,8 @@ def add_valid_item(dest, item): # split by comma; add each valid item to the `dest` list def add_all_items(method, dest, wrapopen=r'\(', wrapclose=r'\)'): regexp = f"{method}{wrapopen}(.*?){wrapclose}" - for match in re.findall(regexp, plugcontent, flags=re.MULTILINE | re.DOTALL): + for match in re.findall(regexp, plugcontent, + flags=re.MULTILINE | re.DOTALL): # tuple of distros ended by either (class|from|import) if isinstance(match, tuple): for item in list(match): diff --git a/sos/__init__.py b/sos/__init__.py index 412a8906..209d94d5 100644 --- a/sos/__init__.py +++ b/sos/__init__.py @@ -18,11 +18,11 @@ __version__ = "4.7.1" import os import sys +import gettext from argparse import ArgumentParser from sos.options import SosListOption -import gettext gettext_dir = "/usr/share/locale" gettext_app = "sos" gettext.bindtextdomain(gettext_app, gettext_dir) diff --git a/sos/archive.py b/sos/archive.py index 2ec60f51..e03b8cd6 100644 --- a/sos/archive.py +++ b/sos/archive.py @@ -73,6 +73,9 @@ class Archive(object): return self.log.debug(self._format_msg(msg)) + def name(self): + return self._name + # this is our contract to clients of the Archive class hierarchy. # All sub-classes need to implement these methods (or inherit concrete # implementations from a parent class. @@ -124,8 +127,7 @@ class Archive(object): An archive that is subsequently compressed or simply closing an archive that supports in-line handling. If method is automatic then the following methods are tried in order: xz, gzip""" - - self.close() + pass class FileCacheArchive(Archive): @@ -167,6 +169,7 @@ class FileCacheArchive(Archive): return os.path.join(self.sysroot, path) def _make_leading_paths(self, src, mode=0o700): + # pylint: disable=too-many-locals """Create leading path components The standard python `os.makedirs` is insufficient for our @@ -664,6 +667,9 @@ class FileCacheArchive(Archive): msg = f"gpg exited with code {r['status']}" raise Exception(msg) + def _build_archive(self, method): + pass + class TarFileArchive(FileCacheArchive): """ archive class using python TarFile to create tar archives""" @@ -673,8 +679,8 @@ class TarFileArchive(FileCacheArchive): def __init__(self, name, tmpdir, policy, threads, enc_opts, sysroot, manifest=None): - super(TarFileArchive, self).__init__(name, tmpdir, policy, threads, - enc_opts, sysroot, manifest) + super().__init__(name, tmpdir, policy, threads, + enc_opts, sysroot, manifest) self._suffix = "tar" self._archive_name = os.path.join( tmpdir, self.name() # lgtm [py/init-calls-subclass] @@ -724,7 +730,7 @@ class TarFileArchive(FileCacheArchive): def name_max(self): # GNU Tar format supports unlimited file name length. Just return # the limit of the underlying FileCacheArchive. - return super(TarFileArchive, self).name_max() + return super().name_max() def _build_archive(self, method): if method == 'auto': diff --git a/sos/cleaner/__init__.py b/sos/cleaner/__init__.py index 1379abf3..be8d8e03 100644 --- a/sos/cleaner/__init__.py +++ b/sos/cleaner/__init__.py @@ -13,13 +13,16 @@ import json import logging import os import shutil -import sos.cleaner.preppers import tempfile import fnmatch from concurrent.futures import ThreadPoolExecutor from datetime import datetime from pwd import getpwuid +from textwrap import fill + +import sos.cleaner.preppers + from sos import __version__ from sos.component import SoSComponent from sos.cleaner.parsers.ip_parser import SoSIPParser @@ -34,7 +37,6 @@ from sos.cleaner.archives.sos import (SoSReportArchive, SoSReportDirectory, from sos.cleaner.archives.generic import DataDirArchive, TarballArchive from sos.cleaner.archives.insights import InsightsArchive from sos.utilities import get_human_readable, import_module, ImporterHelper -from textwrap import fill class SoSCleaner(SoSComponent): @@ -97,7 +99,7 @@ class SoSCleaner(SoSComponent): hook_commons=None): if not in_place: # we are running `sos clean` directly - super(SoSCleaner, self).__init__(parser, args, cmdline) + super().__init__(parser, args, cmdline) self.from_cmdline = True else: # we are being hooked by either SoSReport or SoSCollector, don't @@ -665,7 +667,7 @@ third party. for archive in self.report_paths: self._prepare_archive_with_prepper(archive, prepper) - def obfuscate_report(self, archive): + def obfuscate_report(self, archive): # pylint: disable=too-many-branches """Individually handle each archive or directory we've discovered by running through each file therein. @@ -744,6 +746,7 @@ third party. f"{archive.archive_name}: {err}") def obfuscate_file(self, filename, short_name=None, arc_name=None): + # pylint: disable=too-many-locals """Obfuscate and individual file, line by line. Lines processed, even if no substitutions occur, are then written to a diff --git a/sos/cleaner/archives/generic.py b/sos/cleaner/archives/generic.py index 2ce6f09b..1a2d9db5 100644 --- a/sos/cleaner/archives/generic.py +++ b/sos/cleaner/archives/generic.py @@ -8,12 +8,11 @@ # # See the LICENSE file in the source distribution for further information. - -from sos.cleaner.archives import SoSObfuscationArchive - import os import tarfile +from sos.cleaner.archives import SoSObfuscationArchive + class DataDirArchive(SoSObfuscationArchive): """A plain directory on the filesystem that is not directly associated with diff --git a/sos/cleaner/archives/insights.py b/sos/cleaner/archives/insights.py index 872c6b36..391d9d21 100644 --- a/sos/cleaner/archives/insights.py +++ b/sos/cleaner/archives/insights.py @@ -8,11 +8,10 @@ # # See the LICENSE file in the source distribution for further information. +import tarfile from sos.cleaner.archives import SoSObfuscationArchive -import tarfile - class InsightsArchive(SoSObfuscationArchive): """This class represents archives generated by the insights-client utility diff --git a/sos/cleaner/archives/sos.py b/sos/cleaner/archives/sos.py index 4c58d09d..ace71d54 100644 --- a/sos/cleaner/archives/sos.py +++ b/sos/cleaner/archives/sos.py @@ -8,12 +8,11 @@ # # See the LICENSE file in the source distribution for further information. - -from sos.cleaner.archives import SoSObfuscationArchive - import os import tarfile +from sos.cleaner.archives import SoSObfuscationArchive + class SoSReportArchive(SoSObfuscationArchive): """This is the class representing an sos report, or in other words the diff --git a/sos/cleaner/mappings/hostname_map.py b/sos/cleaner/mappings/hostname_map.py index a0a85418..a19fd2a8 100644 --- a/sos/cleaner/mappings/hostname_map.py +++ b/sos/cleaner/mappings/hostname_map.py @@ -128,6 +128,7 @@ class SoSHostnameMap(SoSMap): return False def get(self, item): + # pylint: disable=too-many-branches prefix = '' suffix = '' final = None @@ -170,21 +171,21 @@ class SoSHostnameMap(SoSMap): elif not _host_substr and (_test[0].endswith('.') or item.endswith(_existing)): # new hostname in known domain - final = super(SoSHostnameMap, self).get(item) + final = super().get(item) break elif item.split(_test[0]): # string that includes existing FQDN obfuscation substring # so, only obfuscate the FQDN part try: itm = item.split(_test[0])[1] - final = _test[0] + super(SoSHostnameMap, self).get(itm) + final = _test[0] + super().get(itm) break except Exception: # fallback to still obfuscating the entire item pass if not final: - final = super(SoSHostnameMap, self).get(item) + final = super().get(item) return prefix + final + suffix def sanitize_item(self, item): diff --git a/sos/cleaner/mappings/mac_map.py b/sos/cleaner/mappings/mac_map.py index c9d38d16..464344ab 100644 --- a/sos/cleaner/mappings/mac_map.py +++ b/sos/cleaner/mappings/mac_map.py @@ -52,11 +52,11 @@ class SoSMacMap(SoSMap): def add(self, item): item = item.replace('-', ':').lower().strip('=.,').strip() - return super(SoSMacMap, self).add(item) + return super().add(item) def get(self, item): item = item.replace('-', ':').lower().strip('=.,').strip() - return super(SoSMacMap, self).get(item) + return super().get(item) def sanitize_item(self, item): """Randomize the device hextets, and append those to our 'vendor' diff --git a/sos/cleaner/parsers/__init__.py b/sos/cleaner/parsers/__init__.py index 73dbe656..e89e5a40 100644 --- a/sos/cleaner/parsers/__init__.py +++ b/sos/cleaner/parsers/__init__.py @@ -8,6 +8,8 @@ # # See the LICENSE file in the source distribution for further information. +# pylint: disable=no-member + import re diff --git a/sos/cleaner/parsers/hostname_parser.py b/sos/cleaner/parsers/hostname_parser.py index 642aa05d..2252ef46 100644 --- a/sos/cleaner/parsers/hostname_parser.py +++ b/sos/cleaner/parsers/hostname_parser.py @@ -23,7 +23,7 @@ class SoSHostnameParser(SoSCleanerParser): def __init__(self, config, skip_clean_files=[]): self.mapping = SoSHostnameMap() - super(SoSHostnameParser, self).__init__(config, skip_clean_files) + super().__init__(config, skip_clean_files) def parse_line(self, line): """This will be called for every line in every file we process, so that diff --git a/sos/cleaner/parsers/ip_parser.py b/sos/cleaner/parsers/ip_parser.py index f6d464a5..94db75f6 100644 --- a/sos/cleaner/parsers/ip_parser.py +++ b/sos/cleaner/parsers/ip_parser.py @@ -46,4 +46,4 @@ class SoSIPParser(SoSCleanerParser): def __init__(self, config, skip_clean_files=[]): self.mapping = SoSIPMap() - super(SoSIPParser, self).__init__(config, skip_clean_files) + super().__init__(config, skip_clean_files) diff --git a/sos/cleaner/parsers/ipv6_parser.py b/sos/cleaner/parsers/ipv6_parser.py index dfd7282a..9a0da497 100644 --- a/sos/cleaner/parsers/ipv6_parser.py +++ b/sos/cleaner/parsers/ipv6_parser.py @@ -37,7 +37,7 @@ class SoSIPv6Parser(SoSCleanerParser): def __init__(self, config, skip_clean_files=[]): self.mapping = SoSIPv6Map() - super(SoSIPv6Parser, self).__init__(config, skip_clean_files) + super().__init__(config, skip_clean_files) def get_map_contents(self): """Structure the dataset contents properly so that they can be reloaded diff --git a/sos/cleaner/parsers/keyword_parser.py b/sos/cleaner/parsers/keyword_parser.py index 3c6c442b..3d9d636c 100644 --- a/sos/cleaner/parsers/keyword_parser.py +++ b/sos/cleaner/parsers/keyword_parser.py @@ -22,7 +22,7 @@ class SoSKeywordParser(SoSCleanerParser): def __init__(self, config, skip_clean_files=[]): self.mapping = SoSKeywordMap() - super(SoSKeywordParser, self).__init__(config, skip_clean_files) + super().__init__(config, skip_clean_files) def _parse_line(self, line): return line, 0 diff --git a/sos/cleaner/parsers/mac_parser.py b/sos/cleaner/parsers/mac_parser.py index 78e40c3d..edf40d8a 100644 --- a/sos/cleaner/parsers/mac_parser.py +++ b/sos/cleaner/parsers/mac_parser.py @@ -8,10 +8,11 @@ # # See the LICENSE file in the source distribution for further information. +import re + from sos.cleaner.parsers import SoSCleanerParser from sos.cleaner.mappings.mac_map import SoSMacMap -import re # aa:bb:cc:fe:ff:dd:ee:ff IPV6_REG_8HEX = ( @@ -51,7 +52,7 @@ class SoSMacParser(SoSCleanerParser): def __init__(self, config, skip_clean_files=[]): self.mapping = SoSMacMap() - super(SoSMacParser, self).__init__(config, skip_clean_files) + super().__init__(config, skip_clean_files) def reduce_mac_match(self, match): """Strips away leading and trailing non-alphanum characters from any diff --git a/sos/cleaner/parsers/username_parser.py b/sos/cleaner/parsers/username_parser.py index c999ff55..b8825f9e 100644 --- a/sos/cleaner/parsers/username_parser.py +++ b/sos/cleaner/parsers/username_parser.py @@ -28,7 +28,7 @@ class SoSUsernameParser(SoSCleanerParser): def __init__(self, config, skip_clean_files=[]): self.mapping = SoSUsernameMap() - super(SoSUsernameParser, self).__init__(config, skip_clean_files) + super().__init__(config, skip_clean_files) def _parse_line(self, line): return line, 0 diff --git a/sos/collector/__init__.py b/sos/collector/__init__.py index 4cdb10a2..1a43083c 100644 --- a/sos/collector/__init__.py +++ b/sos/collector/__init__.py @@ -8,6 +8,8 @@ # # See the LICENSE file in the source distribution for further information. +# pylint: disable=too-many-locals,too-many-branches + import fnmatch import inspect import json @@ -145,7 +147,7 @@ class SoSCollector(SoSComponent): } def __init__(self, parser, parsed_args, cmdline_args): - super(SoSCollector, self).__init__(parser, parsed_args, cmdline_args) + super().__init__(parser, parsed_args, cmdline_args) os.umask(0o77) self.client_list = [] self.node_list = [] diff --git a/sos/collector/clusters/__init__.py b/sos/collector/clusters/__init__.py index 5a993d85..0dbd4a28 100644 --- a/sos/collector/clusters/__init__.py +++ b/sos/collector/clusters/__init__.py @@ -10,9 +10,9 @@ import logging +from threading import Lock from sos.options import ClusterOption from sos.utilities import bold -from threading import Lock class Cluster(): @@ -91,7 +91,7 @@ class Cluster(): return cls.__name__.lower() @classmethod - def display_help(cls, section): + def display_help(cls, section): # pylint: disable=too-many-branches if cls is Cluster: cls.display_self_help(section) return diff --git a/sos/collector/clusters/ocp.py b/sos/collector/clusters/ocp.py index cc878770..d4af8747 100644 --- a/sos/collector/clusters/ocp.py +++ b/sos/collector/clusters/ocp.py @@ -117,7 +117,7 @@ class ocp(Cluster): return _res['status'] == 0 def check_enabled(self): - if super(ocp, self).check_enabled(): + if super().check_enabled(): return True self.token = self.get_option('token') or os.getenv('SOSOCPTOKEN', None) if self.token: diff --git a/sos/collector/clusters/pacemaker.py b/sos/collector/clusters/pacemaker.py index 3c25fcaa..6c3ee302 100644 --- a/sos/collector/clusters/pacemaker.py +++ b/sos/collector/clusters/pacemaker.py @@ -10,9 +10,9 @@ import re +from xml.etree import ElementTree from sos.collector.clusters import Cluster from sos.utilities import sos_parse_version -from xml.etree import ElementTree class pacemaker(Cluster): diff --git a/sos/collector/exceptions.py b/sos/collector/exceptions.py index cb1c2314..233c9a9e 100644 --- a/sos/collector/exceptions.py +++ b/sos/collector/exceptions.py @@ -14,7 +14,7 @@ class InvalidPasswordException(Exception): def __init__(self): message = 'Invalid password provided' - super(InvalidPasswordException, self).__init__(message) + super().__init__(message) class TimeoutPasswordAuthException(Exception): @@ -23,7 +23,7 @@ class TimeoutPasswordAuthException(Exception): def __init__(self): message = 'Timeout hit while waiting for password validation' - super(TimeoutPasswordAuthException, self).__init__(message) + super().__init__(message) class PasswordRequestException(Exception): @@ -32,7 +32,7 @@ class PasswordRequestException(Exception): def __init__(self): message = 'Host requested password, but none provided' - super(PasswordRequestException, self).__init__(message) + super().__init__(message) class AuthPermissionDeniedException(Exception): @@ -40,7 +40,7 @@ class AuthPermissionDeniedException(Exception): def __init__(self): message = 'Permission denied while trying to authenticate' - super(AuthPermissionDeniedException, self).__init__(message) + super().__init__(message) class ConnectionException(Exception): @@ -49,7 +49,7 @@ class ConnectionException(Exception): def __init__(self, address='', port=''): message = (f"Could not connect to host {address} on specified port " f"{port}") - super(ConnectionException, self).__init__(message) + super().__init__(message) class CommandTimeoutException(Exception): @@ -59,7 +59,7 @@ class CommandTimeoutException(Exception): message = 'Timeout expired' if command: message += f" executing {command}" - super(CommandTimeoutException, self).__init__(message) + super().__init__(message) class ConnectionTimeoutException(Exception): @@ -67,7 +67,7 @@ class ConnectionTimeoutException(Exception): def __init__(self): message = 'Timeout expires while trying to connect' - super(ConnectionTimeoutException, self).__init__(message) + super().__init__(message) class ControlSocketMissingException(Exception): @@ -75,7 +75,7 @@ class ControlSocketMissingException(Exception): def __init__(self, path=''): message = f"SSH control socket {path} does not exist" - super(ControlSocketMissingException, self).__init__(message) + super().__init__(message) class ControlPersistUnsupportedException(Exception): @@ -83,7 +83,7 @@ class ControlPersistUnsupportedException(Exception): def __init__(self): message = 'ControlPersist unsupported by local SSH installation' - super(ControlPersistUnsupportedException, self).__init__(message) + super().__init__(message) class UnsupportedHostException(Exception): @@ -91,7 +91,7 @@ class UnsupportedHostException(Exception): def __init__(self): message = 'Host did not match any supported distributions' - super(UnsupportedHostException, self).__init__(message) + super().__init__(message) class InvalidTransportException(Exception): @@ -101,7 +101,7 @@ class InvalidTransportException(Exception): def __init__(self, transport=None): message = ("Connection failed: unknown or unsupported transport " f"{transport if transport else ''}") - super(InvalidTransportException, self).__init__(message) + super().__init__(message) class SaltStackMasterUnsupportedException(Exception): @@ -109,7 +109,7 @@ class SaltStackMasterUnsupportedException(Exception): def __init__(self): message = 'Master unsupported by local SaltStack installation' - super(SaltStackMasterUnsupportedException, self).__init__(message) + super().__init__(message) class JujuNotInstalledException(Exception): @@ -120,7 +120,7 @@ class JujuNotInstalledException(Exception): 'Juju is not installed, ' 'please ensure you have installed juju.' ) - super(JujuNotInstalledException, self).__init__(message) + super().__init__(message) __all__ = [ diff --git a/sos/collector/sosnode.py b/sos/collector/sosnode.py index 1efd6e5b..88044617 100644 --- a/sos/collector/sosnode.py +++ b/sos/collector/sosnode.py @@ -8,6 +8,8 @@ # # See the LICENSE file in the source distribution for further information. +# pylint: disable=too-many-branches + import fnmatch import inspect import logging @@ -838,36 +840,25 @@ class SosNode(): def retrieve_sosreport(self): """Collect the sosreport archive from the node""" - if self.sos_path: - if self.need_sudo or self.opts.become_root: - try: - self.make_archive_readable(self.sos_path) - except Exception: - self.log_error('Failed to make archive readable') - return False - self.log_info(f'Retrieving sos report from {self.address}') - self.ui_msg('Retrieving sos report...') + if self.need_sudo or self.opts.become_root: try: - ret = self.retrieve_file(self.sos_path) - except Exception as err: - self.log_error(err) - return False - if ret: - self.ui_msg('Successfully collected sos report') - self.file_list.append(self.sos_path.split('/')[-1]) - return True - else: - self.ui_msg('Failed to retrieve sos report') + self.make_archive_readable(self.sos_path) + except Exception: + self.log_error('Failed to make archive readable') return False + self.log_info(f'Retrieving sos report from {self.address}') + self.ui_msg('Retrieving sos report...') + try: + ret = self.retrieve_file(self.sos_path) + except Exception as err: + self.log_error(err) + return False + if ret: + self.ui_msg('Successfully collected sos report') + self.file_list.append(self.sos_path.split('/')[-1]) + return True else: - # sos sometimes fails but still returns a 0 exit code - if self.stderr.read(): - e = self.stderr.read() - else: - e = [x.strip() for x in self.stdout.readlines() if x.strip][-1] - self.soslog.error( - f'Failed to run sos report on {self.address}: {e}') - self.log_error(f'Failed to run sos report. {e}') + self.ui_msg('Failed to retrieve sos report') return False def remove_sos_archive(self): diff --git a/sos/collector/transports/__init__.py b/sos/collector/transports/__init__.py index 92bd9195..b501b4a5 100644 --- a/sos/collector/transports/__init__.py +++ b/sos/collector/transports/__init__.py @@ -10,10 +10,11 @@ import inspect import logging -import pexpect import re - from shlex import quote + +import pexpect + from sos.collector.exceptions import (ConnectionException, CommandTimeoutException) from sos.utilities import bold @@ -322,7 +323,7 @@ class RemoteTransport(): :type result: ``pexpect.spawn`` """ if index == 2: - if not self.opts.sudo_pw and not self.opt.nopasswd_sudo: + if not self.opts.sudo_pw and not self.opts.nopasswd_sudo: msg = ("Unable to run command: sudo password " "required but not provided") self.log_error(msg) diff --git a/sos/collector/transports/control_persist.py b/sos/collector/transports/control_persist.py index b8a979bf..839cbe65 100644 --- a/sos/collector/transports/control_persist.py +++ b/sos/collector/transports/control_persist.py @@ -10,8 +10,8 @@ import os -import pexpect import subprocess +import pexpect from sos.collector.transports import RemoteTransport from sos.collector.exceptions import (InvalidPasswordException, @@ -69,7 +69,7 @@ class SSHControlPersist(RemoteTransport): raise ControlPersistUnsupportedException return True - def _connect(self, password=''): + def _connect(self, password=''): # pylint: disable=too-many-branches """ Using ControlPersist, create the initial connection to the node. diff --git a/sos/collector/transports/oc.py b/sos/collector/transports/oc.py index e011b0f6..8a45db5a 100644 --- a/sos/collector/transports/oc.py +++ b/sos/collector/transports/oc.py @@ -204,7 +204,7 @@ class OCTransport(RemoteTransport): if cmd.startswith('oc'): return (f"oc -n {self.project} exec --request-timeout=0 " f"{self.pod_name} -- chroot /host {cmd}") - return super(OCTransport, self)._format_cmd_for_exec(cmd) + return super()._format_cmd_for_exec(cmd) def run_command(self, cmd, timeout=180, need_root=False, env=None, use_shell=False): @@ -214,8 +214,8 @@ class OCTransport(RemoteTransport): # since we always execute within a bash shell, force disable use_shell # to avoid double-quoting - return super(OCTransport, self).run_command(cmd, timeout, need_root, - env, use_shell=False) + return super().run_command(cmd, timeout, need_root, + env, use_shell=False) def _disconnect(self): if os.path.exists(self.pod_tmp_conf): diff --git a/sos/collector/transports/saltstack.py b/sos/collector/transports/saltstack.py index f0660c8a..70abbdff 100644 --- a/sos/collector/transports/saltstack.py +++ b/sos/collector/transports/saltstack.py @@ -39,7 +39,7 @@ class SaltStackMaster(RemoteTransport): Run a command on the remote host using SaltStack Master. If the output is json, convert it to a string. """ - ret = super(SaltStackMaster, self).run_command( + ret = super().run_command( cmd, timeout, need_root, env, use_shell) with contextlib.suppress(Exception): ret['output'] = self._convert_output_json(ret['output']) diff --git a/sos/help/__init__.py b/sos/help/__init__.py index c3625087..2de2f388 100644 --- a/sos/help/__init__.py +++ b/sos/help/__init__.py @@ -14,11 +14,11 @@ import sys import os from collections import OrderedDict +from textwrap import fill from sos.component import SoSComponent from sos.policies import import_policy from sos.report.plugins import Plugin from sos.utilities import bold, ImporterHelper -from textwrap import fill try: TERMSIZE = min(os.get_terminal_size().columns, 120) @@ -41,7 +41,7 @@ class SoSHelper(SoSComponent): } def __init__(self, parser, args, cmdline): - super(SoSHelper, self).__init__(parser, args, cmdline) + super().__init__(parser, args, cmdline) self.topic = self.opts.topic @classmethod diff --git a/sos/options.py b/sos/options.py index 6ec1e4a6..1ef5d1d6 100644 --- a/sos/options.py +++ b/sos/options.py @@ -239,6 +239,7 @@ class SoSOptions(): _update_from_section("global", config) _update_from_section(component, config) if config.has_section("plugin_options") and hasattr(self, 'plugopts'): + # pylint: disable=no-member for key, val in config.items("plugin_options"): if not key.split('.')[0] in self.skip_plugins: self.plugopts.append(key + '=' + val) diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py index 52f096ea..7a2997d0 100644 --- a/sos/policies/__init__.py +++ b/sos/policies/__init__.py @@ -1,3 +1,4 @@ + import logging import os import platform @@ -9,6 +10,7 @@ import string import sys from pwd import getpwuid +from textwrap import fill from sos.presets import (NO_PRESET, GENERIC_PRESETS, PRESETS_PATH, PresetDefaults, DESC, NOTE, OPTS) from sos.policies.package_managers import PackageManager @@ -17,7 +19,6 @@ from sos.utilities import (ImporterHelper, import_module, get_human_readable, from sos.report.plugins import IndependentPlugin, ExperimentalPlugin from sos.options import SoSOptions from sos import _sos as _ -from textwrap import fill def import_policy(name): @@ -234,7 +235,7 @@ any third party. the Policy `name_pattern` :rtype: ``str`` """ - name = self.get_local_name().split('.')[0] + name = self.get_local_name().split('.')[0] # pylint: disable=no-member case = self.case_id label = self.commons['cmdlineopts'].label date = '' @@ -252,6 +253,7 @@ any third party. else: nstr = self.name_pattern + # pylint: disable-next=no-member return self.sanitize_filename(time.strftime(nstr)) # for some specific binaries like "xz", we need to determine package diff --git a/sos/policies/distros/__init__.py b/sos/policies/distros/__init__.py index 6f832bf5..be291e83 100644 --- a/sos/policies/distros/__init__.py +++ b/sos/policies/distros/__init__.py @@ -8,6 +8,8 @@ # # See the LICENSE file in the source distribution for further information. +# pylint: disable=too-many-branches + import os import re @@ -91,9 +93,9 @@ class LinuxPolicy(Policy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(LinuxPolicy, self).__init__(sysroot=sysroot, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, + probe_runtime=probe_runtime, + remote_exec=remote_exec) if sysroot: self.sysroot = sysroot @@ -1018,6 +1020,7 @@ class LinuxPolicy(Policy): return '' def restart_sos_container(self): + # pylint: disable=no-member """Restarts the container created for sos collect if it has stopped. This is called immediately after create_sos_container() as the command @@ -1029,6 +1032,7 @@ class LinuxPolicy(Policy): return f"{self.container_runtime} start {self.sos_container_name}" def format_container_command(self, cmd): + # pylint: disable=no-member """Returns the command that allows us to exec into the created container for sos collect. diff --git a/sos/policies/distros/amazon.py b/sos/policies/distros/amazon.py index c19e7e2e..a888079a 100644 --- a/sos/policies/distros/amazon.py +++ b/sos/policies/distros/amazon.py @@ -8,8 +8,8 @@ # # See the LICENSE file in the source distribution for further information. -from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE import os +from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE class AmazonPolicy(RedHatPolicy): @@ -20,9 +20,9 @@ class AmazonPolicy(RedHatPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(AmazonPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) @classmethod def check(cls, remote=''): diff --git a/sos/policies/distros/anolis.py b/sos/policies/distros/anolis.py index 12822a74..79973be7 100644 --- a/sos/policies/distros/anolis.py +++ b/sos/policies/distros/anolis.py @@ -6,8 +6,8 @@ # # See the LICENSE file in the source distribution for further information. -from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE import os +from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE class AnolisPolicy(RedHatPolicy): @@ -18,9 +18,9 @@ class AnolisPolicy(RedHatPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(AnolisPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) @classmethod def check(cls, remote=''): diff --git a/sos/policies/distros/azure.py b/sos/policies/distros/azure.py index b521d1e1..6f0e45ad 100644 --- a/sos/policies/distros/azure.py +++ b/sos/policies/distros/azure.py @@ -8,9 +8,9 @@ # # See the LICENSE file in the source distribution for further information. +import os from sos.report.plugins import AzurePlugin from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE -import os class AzurePolicy(RedHatPolicy): @@ -23,9 +23,9 @@ class AzurePolicy(RedHatPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(AzurePolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) self.valid_subclasses += [AzurePlugin] @classmethod diff --git a/sos/policies/distros/circle.py b/sos/policies/distros/circle.py index 763348dd..8532963b 100644 --- a/sos/policies/distros/circle.py +++ b/sos/policies/distros/circle.py @@ -8,8 +8,8 @@ # # See the LICENSE file in the source distribution for further information. -from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE import os +from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE class CirclePolicy(RedHatPolicy): @@ -20,9 +20,9 @@ class CirclePolicy(RedHatPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(CirclePolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) @classmethod def check(cls, remote=''): diff --git a/sos/policies/distros/cos.py b/sos/policies/distros/cos.py index 217c41ce..bf28d03c 100644 --- a/sos/policies/distros/cos.py +++ b/sos/policies/distros/cos.py @@ -38,9 +38,9 @@ class CosPolicy(LinuxPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(CosPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) self.valid_subclasses += [CosPolicy] @classmethod diff --git a/sos/policies/distros/debian.py b/sos/policies/distros/debian.py index fe6bcd1d..26743792 100644 --- a/sos/policies/distros/debian.py +++ b/sos/policies/distros/debian.py @@ -6,12 +6,12 @@ # # See the LICENSE file in the source distribution for further information. +import os + from sos.report.plugins import DebianPlugin from sos.policies.distros import LinuxPolicy from sos.policies.package_managers.dpkg import DpkgPackageManager -import os - class DebianPolicy(LinuxPolicy): distro = "Debian" @@ -37,9 +37,9 @@ class DebianPolicy(LinuxPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(DebianPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) self.package_manager = DpkgPackageManager(chroot=self.sysroot, remote_exec=remote_exec) self.valid_subclasses += [DebianPlugin] diff --git a/sos/policies/distros/opencloudos.py b/sos/policies/distros/opencloudos.py index df510e55..ff3c8af5 100644 --- a/sos/policies/distros/opencloudos.py +++ b/sos/policies/distros/opencloudos.py @@ -7,8 +7,8 @@ # # See the LICENSE file in the source distribution for further information. -from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE import os +from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE class OpenCloudOSPolicy(RedHatPolicy): @@ -18,9 +18,9 @@ class OpenCloudOSPolicy(RedHatPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(OpenCloudOSPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) @classmethod def check(cls, remote=''): diff --git a/sos/policies/distros/openeuler.py b/sos/policies/distros/openeuler.py index 2781cf5d..2c361ec1 100644 --- a/sos/policies/distros/openeuler.py +++ b/sos/policies/distros/openeuler.py @@ -6,9 +6,9 @@ # # See the LICENSE file in the source distribution for further information. +import os from sos.report.plugins import OpenEulerPlugin from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE -import os class OpenEulerPolicy(RedHatPolicy): @@ -18,9 +18,9 @@ class OpenEulerPolicy(RedHatPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(OpenEulerPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) self.valid_subclasses += [OpenEulerPlugin] diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py index a72fe6d0..edea151e 100644 --- a/sos/policies/distros/redhat.py +++ b/sos/policies/distros/redhat.py @@ -57,9 +57,9 @@ class RedHatPolicy(LinuxPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(RedHatPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) self.usrmove = False self.package_manager = MultiPackageManager( @@ -235,9 +235,9 @@ support representative. def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(RHELPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) self.register_presets(RHEL_PRESETS) @classmethod @@ -354,13 +354,13 @@ support representative. fname = os.path.join(self.upload_directory, fname) return fname - def upload_sftp(self): + def upload_sftp(self): # pylint: disable=too-many-branches """Override the base upload_sftp to allow for setting an on-demand generated anonymous login for the RH SFTP server if a username and password are not given """ if RH_SFTP_HOST.split('//')[1] not in self.get_upload_url(): - return super(RHELPolicy, self).upload_sftp() + return super().upload_sftp() if not REQUESTS_LOADED: raise Exception("python3-requests is not installed and is required" @@ -426,8 +426,7 @@ support representative. f"{anon.status_code}): {anon.json()}" ) if _user and _token: - return super(RHELPolicy, self).upload_sftp(user=_user, - password=_token) + return super().upload_sftp(user=_user, password=_token) raise Exception("Could not retrieve valid or anonymous credentials") def upload_archive(self, archive): @@ -439,7 +438,7 @@ support representative. (not self.get_upload_user() or not self.get_upload_password()): self.upload_url = RH_SFTP_HOST - uploaded = super(RHELPolicy, self).upload_archive(archive) + uploaded = super().upload_archive(archive) except Exception as e: uploaded = False if not self.upload_url.startswith(RH_API_HOST): @@ -450,7 +449,7 @@ support representative. f"{e}. Trying {RH_SFTP_HOST}") ) self.upload_url = RH_SFTP_HOST - uploaded = super(RHELPolicy, self).upload_archive(archive) + uploaded = super().upload_archive(archive) return uploaded def dist_version(self): @@ -537,9 +536,9 @@ support representative. def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(RedHatCoreOSPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) @classmethod def check(cls, remote=''): @@ -608,9 +607,9 @@ class FedoraPolicy(RedHatPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(FedoraPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) @classmethod def check(cls, remote=''): @@ -624,7 +623,8 @@ class FedoraPolicy(RedHatPolicy): def fedora_version(self): pkg = self.pkg_by_name("fedora-release") or \ - self.all_pkgs_by_name_regex("fedora-release-.*")[-1] + self.package_manager.all_pkgs_by_name_regex( + "fedora-release-.*")[-1] return int(pkg["version"]) # vim: set et ts=4 sw=4 : diff --git a/sos/policies/distros/rocky.py b/sos/policies/distros/rocky.py index ce442540..c51f535c 100644 --- a/sos/policies/distros/rocky.py +++ b/sos/policies/distros/rocky.py @@ -8,8 +8,8 @@ # # See the LICENSE file in the source distribution for further information. -from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE import os +from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE class RockyPolicy(RedHatPolicy): @@ -22,9 +22,9 @@ class RockyPolicy(RedHatPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(RockyPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) @classmethod def check(cls, remote=''): diff --git a/sos/policies/distros/suse.py b/sos/policies/distros/suse.py index 9422daf8..8c7900e5 100644 --- a/sos/policies/distros/suse.py +++ b/sos/policies/distros/suse.py @@ -24,9 +24,9 @@ class SuSEPolicy(LinuxPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(SuSEPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) self.valid_subclasses += [SuSEPlugin, RedHatPlugin] self.usrmove = False @@ -77,9 +77,9 @@ No changes will be made to system configuration. def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(OpenSuSEPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) @classmethod def check(cls, remote): diff --git a/sos/policies/distros/ubuntu.py b/sos/policies/distros/ubuntu.py index 671187fe..8d63c972 100644 --- a/sos/policies/distros/ubuntu.py +++ b/sos/policies/distros/ubuntu.py @@ -6,6 +6,8 @@ # # See the LICENSE file in the source distribution for further information. +import os + from sos.report.plugins import UbuntuPlugin from sos.policies.distros.debian import DebianPolicy @@ -13,8 +15,6 @@ from sos.policies.package_managers.snap import SnapPackageManager from sos.policies.package_managers.dpkg import DpkgPackageManager from sos.policies.package_managers import MultiPackageManager -import os - class UbuntuPolicy(DebianPolicy): distro = "Ubuntu" @@ -32,9 +32,9 @@ class UbuntuPolicy(DebianPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(UbuntuPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) self.package_manager = MultiPackageManager( primary=DpkgPackageManager, @@ -83,7 +83,7 @@ class UbuntuPolicy(DebianPolicy): if self.upload_url.startswith(self._upload_url): return (self._upload_user, self._upload_password) else: - return super(UbuntuPolicy, self).get_upload_https_auth() + return super().get_upload_https_auth() def get_upload_url_string(self): if self.upload_url.startswith(self._upload_url): @@ -97,6 +97,6 @@ class UbuntuPolicy(DebianPolicy): return self._upload_url fname = os.path.basename(self.upload_archive_name) return self._upload_url + fname - return super(UbuntuPolicy, self).get_upload_url() + return super().get_upload_url() # vim: set et ts=4 sw=4 : diff --git a/sos/policies/distros/uniontechserver.py b/sos/policies/distros/uniontechserver.py index 15a8819b..29163dbf 100644 --- a/sos/policies/distros/uniontechserver.py +++ b/sos/policies/distros/uniontechserver.py @@ -6,8 +6,8 @@ # # See the LICENSE file in the source distribution for further information. -from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE import os +from sos.policies.distros.redhat import RedHatPolicy, OS_RELEASE class UnionTechPolicy(RedHatPolicy): @@ -17,9 +17,9 @@ class UnionTechPolicy(RedHatPolicy): def __init__(self, sysroot=None, init=None, probe_runtime=True, remote_exec=None): - super(UnionTechPolicy, self).__init__(sysroot=sysroot, init=init, - probe_runtime=probe_runtime, - remote_exec=remote_exec) + super().__init__(sysroot=sysroot, init=init, + probe_runtime=probe_runtime, + remote_exec=remote_exec) @classmethod def check(cls, remote=''): diff --git a/sos/policies/init_systems/systemd.py b/sos/policies/init_systems/systemd.py index 0f49197c..b76bcdf9 100644 --- a/sos/policies/init_systems/systemd.py +++ b/sos/policies/init_systems/systemd.py @@ -16,7 +16,7 @@ class SystemdInit(InitSystem): """InitSystem abstraction for SystemD systems""" def __init__(self, chroot=None): - super(SystemdInit, self).__init__( + super().__init__( init_cmd='systemctl', list_cmd='list-unit-files --type=service', query_cmd='status', diff --git a/sos/policies/package_managers/__init__.py b/sos/policies/package_managers/__init__.py index 594ca827..acea40b0 100644 --- a/sos/policies/package_managers/__init__.py +++ b/sos/policies/package_managers/__init__.py @@ -304,8 +304,7 @@ class MultiPackageManager(PackageManager): """ def __init__(self, primary, fallbacks, chroot=None, remote_exec=None): - super(MultiPackageManager, self).__init__(chroot=chroot, - remote_exec=remote_exec) + super().__init__(chroot=chroot, remote_exec=remote_exec) if not issubclass(primary, PackageManager): raise Exception( diff --git a/sos/policies/runtimes/crio.py b/sos/policies/runtimes/crio.py index d7726f8b..085a5ecd 100644 --- a/sos/policies/runtimes/crio.py +++ b/sos/policies/runtimes/crio.py @@ -9,9 +9,9 @@ # See the LICENSE file in the source distribution for further information. import json +from shlex import quote from sos.policies.runtimes import ContainerRuntime from sos.utilities import sos_get_command_output -from shlex import quote class CrioContainerRuntime(ContainerRuntime): diff --git a/sos/report/__init__.py b/sos/report/__init__.py index cf3d0d76..0edbdead 100644 --- a/sos/report/__init__.py +++ b/sos/report/__init__.py @@ -8,22 +8,25 @@ # # See the LICENSE file in the source distribution for further information. +# pylint: disable=too-many-branches,too-many-locals + import sys import traceback import os import errno import logging - +import hashlib +import pdb from datetime import datetime import glob + +from shutil import rmtree +from concurrent.futures import ThreadPoolExecutor, TimeoutError + import sos.report.plugins from sos.utilities import (ImporterHelper, SoSTimeoutError, bold, sos_get_command_output, TIMEOUT_DEFAULT, listdir, is_executable) -from shutil import rmtree -import hashlib -from concurrent.futures import ThreadPoolExecutor, TimeoutError -import pdb from sos import _sos as _ from sos import __version__ @@ -141,7 +144,7 @@ class SoSReport(SoSComponent): } def __init__(self, parser, args, cmdline): - super(SoSReport, self).__init__(parser, args, cmdline) + super().__init__(parser, args, cmdline) self.loaded_plugins = [] self.skipped_plugins = [] self.all_options = [] diff --git a/sos/report/plugins/__init__.py b/sos/report/plugins/__init__.py index 504b0f30..a7978b58 100644 --- a/sos/report/plugins/__init__.py +++ b/sos/report/plugins/__init__.py @@ -8,15 +8,10 @@ # # See the LICENSE file in the source distribution for further information. -""" This exports methods available for use by plugins for sos """ +# pylint: disable=too-many-locals,too-many-branches -from sos.utilities import (sos_get_command_output, import_module, grep, - fileobj, tail, is_executable, TIMEOUT_DEFAULT, - path_exists, path_isdir, path_isfile, path_islink, - listdir, path_join, bold, file_is_binary, - recursive_dict_values_by_key) +""" This exports methods available for use by plugins for sos """ -from sos.archive import P_FILE, P_LINK import contextlib import os import glob @@ -30,6 +25,14 @@ import textwrap from datetime import datetime +from sos.utilities import (sos_get_command_output, import_module, grep, + fileobj, tail, is_executable, TIMEOUT_DEFAULT, + path_exists, path_isdir, path_isfile, path_islink, + listdir, path_join, bold, file_is_binary, + recursive_dict_values_by_key) + +from sos.archive import P_FILE, P_LINK + def regex_findall(regex, fname): """Return a list of all non overlapping matches in the string(s)""" @@ -542,6 +545,7 @@ class Plugin(): # Default predicates predicate = None cmd_predicate = None + short_desc = "<no description available>" def __init__(self, commons): @@ -818,6 +822,7 @@ class Plugin(): ) if hasattr(cls, 'verify_packages'): + # pylint: disable=no-member section.add_text( "\nVerfies packages (when using --verify): " f"{', '.join(pkg for pkg in cls.verify_packages)}", @@ -1977,6 +1982,7 @@ class Plugin(): subdir=subdir) def _add_cmd_output(self, **kwargs): + # pylint: disable=no-member """Internal helper to add a single command to the collection list.""" pred = kwargs.pop('pred') if 'pred' in kwargs else SoSPredicate(self) if 'priority' not in kwargs: diff --git a/sos/report/plugins/foreman.py b/sos/report/plugins/foreman.py index 5ca4439e..2adc6313 100644 --- a/sos/report/plugins/foreman.py +++ b/sos/report/plugins/foreman.py @@ -182,6 +182,7 @@ class Foreman(Plugin): self.collect_proxies() def collect_foreman_db(self): + # pylint: disable=too-many-locals """ Collect foreman db and dynflow data """ days = f'{self.get_option("days")} days' interval = quote(days) diff --git a/sos/report/plugins/kubernetes.py b/sos/report/plugins/kubernetes.py index eb3e3747..66cbf810 100644 --- a/sos/report/plugins/kubernetes.py +++ b/sos/report/plugins/kubernetes.py @@ -11,9 +11,9 @@ from fnmatch import translate import re +import json from sos.report.plugins import (Plugin, RedHatPlugin, DebianPlugin, UbuntuPlugin, PluginOpt) -import json class Kubernetes(Plugin): @@ -191,37 +191,40 @@ class Kubernetes(Plugin): ) if self.get_option('podlogs'): - k_cmd = f'{self.kube_cmd} get -o json {knsp}' - ret = self.exec_cmd(f'{k_cmd} pods') - if ret['status'] == 0: - pods = json.loads(ret['output']) - # allow shell-style regex - reg = (translate(self.get_option('podlogs-filter')) if - self.get_option('podlogs-filter') else None) - for pod in pods["items"]: - if reg and not re.match(reg, pod["metadata"]["name"]): - continue - _subdir = (f'cluster-info/' - f'{pod["metadata"]["namespace"]}/podlogs/' - f'{pod["metadata"]["name"]}') - if "containers" in pod["spec"]: - for cont in pod["spec"]["containers"]: - pod_name = pod["metadata"]["name"] - cont_name = cont["name"] - self.add_cmd_output( - f'{self.kube_cmd} {knsp} logs ' - f'{pod_name} -c {cont_name}', - subdir=_subdir - ) - if "initContainers" in pod["spec"]: - for cont in pod["spec"]["initContainers"]: - pod_name = pod["metadata"]["name"] - cont_name = cont["name"] - self.add_cmd_output( - f'{self.kube_cmd} {knsp} logs ' - f'{pod_name} -c {cont_name}', - subdir=_subdir - ) + self._get_pod_logs(knsp) + + def _get_pod_logs(self, namespace): + k_cmd = f'{self.kube_cmd} get -o json {namespace}' + ret = self.exec_cmd(f'{k_cmd} pods') + if ret['status'] == 0: + pods = json.loads(ret['output']) + # allow shell-style regex + reg = (translate(self.get_option('podlogs-filter')) if + self.get_option('podlogs-filter') else None) + for pod in pods["items"]: + if reg and not re.match(reg, pod["metadata"]["name"]): + continue + _subdir = (f'cluster-info/' + f'{pod["metadata"]["namespace"]}/podlogs/' + f'{pod["metadata"]["name"]}') + if "containers" in pod["spec"]: + for cont in pod["spec"]["containers"]: + pod_name = pod["metadata"]["name"] + cont_name = cont["name"] + self.add_cmd_output( + f'{self.kube_cmd} {namespace} logs ' + f'{pod_name} -c {cont_name}', + subdir=_subdir + ) + if "initContainers" in pod["spec"]: + for cont in pod["spec"]["initContainers"]: + pod_name = pod["metadata"]["name"] + cont_name = cont["name"] + self.add_cmd_output( + f'{self.kube_cmd} {namespace} logs ' + f'{pod_name} -c {cont_name}', + subdir=_subdir + ) def collect_all_resources(self): """ Collect details about all resources """ diff --git a/sos/report/reporting.py b/sos/report/reporting.py index fc7b56a5..c08b6151 100644 --- a/sos/report/reporting.py +++ b/sos/report/reporting.py @@ -21,6 +21,8 @@ except ImportError: class Node(object): + data = {} + def __str__(self): return json.dumps(self.data) diff --git a/sos/utilities.py b/sos/utilities.py index 8f165ab4..c0a93ada 100644 --- a/sos/utilities.py +++ b/sos/utilities.py @@ -219,6 +219,7 @@ def sos_get_command_output(command, timeout=TIMEOUT_DEFAULT, stderr=False, chroot=None, chdir=None, env=None, foreground=False, binary=False, sizelimit=None, poller=None, to_file=False): + # pylint: disable=too-many-locals,too-many-branches """Execute a command and return a dictionary of status and output, optionally changing root or current working directory before executing command. @@ -498,7 +499,7 @@ class AsyncReader(threading.Thread): """ def __init__(self, channel, sizelimit, binary): - super(AsyncReader, self).__init__() + super().__init__() self.chan = channel self.binary = binary self.chunksize = 2048 diff --git a/tests/report_tests/plugin_tests/logs.py b/tests/report_tests/plugin_tests/logs.py index c3811b3f..2f4841fb 100644 --- a/tests/report_tests/plugin_tests/logs.py +++ b/tests/report_tests/plugin_tests/logs.py @@ -9,10 +9,9 @@ import random import os - -from sos_tests import StageOneReportTest, StageTwoReportTest from string import ascii_uppercase, digits from time import sleep +from sos_tests import StageOneReportTest, StageTwoReportTest class LogsPluginTest(StageOneReportTest): diff --git a/tests/sos_tests.py b/tests/sos_tests.py index 655a9472..a2cca0d9 100644 --- a/tests/sos_tests.py +++ b/tests/sos_tests.py @@ -6,13 +6,6 @@ # # See the LICENSE file in the source distribution for further information. - -from avocado.core.exceptions import TestSkipError -from avocado.core.output import LOG_UI -from avocado import Test -from avocado.utils import archive, process, distro, software_manager -from avocado.utils.cpu import get_arch -from avocado.utils.software_manager import distro_packages from fnmatch import fnmatch import glob @@ -24,6 +17,13 @@ import shutil import socket import re +from avocado.core.exceptions import TestSkipError +from avocado.core.output import LOG_UI +from avocado import Test +from avocado.utils import archive, process, distro, software_manager +from avocado.utils.cpu import get_arch +from avocado.utils.software_manager import distro_packages + SOS_TEST_DIR = os.path.dirname(os.path.realpath(__file__)) SOS_REPO_ROOT = os.path.realpath(os.path.join(SOS_TEST_DIR, '../')) SOS_PLUGIN_DIR = os.path.realpath( @@ -148,6 +148,8 @@ class BaseSoSTest(Test): if self._exception_expected: self.cmd_output = err.result else: + # pylint: disable=no-member + # We've already checked above for the result attribute for err msg = err.result.stderr.decode() or err.result.stdout.decode() # a little hacky, but using self.log methods here will not # print to console unless we ratchet up the verbosity for the @@ -399,6 +401,7 @@ class BaseSoSReportTest(BaseSoSTest): try: process.run(cmd, timeout=10) except Exception as err: + # pylint: disable=no-member if err.result.interrupted: self.error("Timeout while decrypting") if 'Bad session key' in err.result.stderr.decode(): @@ -473,7 +476,7 @@ class BaseSoSReportTest(BaseSoSTest): f"--tmp-dir {self.tmpdir} {self.sos_cmd}") def _execute_sos_cmd(self): - super(BaseSoSReportTest, self)._execute_sos_cmd() + super()._execute_sos_cmd() self.archive = re.findall( '/.*sosreport-.*tar.*', self.cmd_output.stdout @@ -489,7 +492,7 @@ class BaseSoSReportTest(BaseSoSTest): return None def setUp(self): - super(BaseSoSReportTest, self).setUp() + super().setUp() self.archive_path = self._get_archive_path() def get_name_in_archive(self, fname): @@ -834,12 +837,12 @@ class StageTwoReportTest(BaseSoSReportTest): self.packages['centos'] = self.packages['rhel'] self.packages['centos-stream'] = self.packages['rhel'] - super(StageTwoReportTest, self).setUp() + super().setUp() def tearDown(self): if self.end_of_test_case: self.teardown_mocking() - super(StageTwoReportTest, self).tearDown() + super().tearDown() def teardown_mocking(self): """Undo any and all mocked setup that we did for tests diff --git a/tests/unittests/option_tests.py b/tests/unittests/option_tests.py index 65b53608..a1fa4807 100644 --- a/tests/unittests/option_tests.py +++ b/tests/unittests/option_tests.py @@ -30,7 +30,7 @@ class MockPlugin(Plugin): ] def __init__(self, commons): - super(MockPlugin, self).__init__(commons=commons) + super().__init__(commons=commons) class GlobalOptionTest(unittest.TestCase): diff --git a/tests/unittests/plugin_tests.py b/tests/unittests/plugin_tests.py index 4be93780..d659fb3d 100644 --- a/tests/unittests/plugin_tests.py +++ b/tests/unittests/plugin_tests.py @@ -12,13 +12,12 @@ import shutil import random from io import StringIO - +from string import ascii_lowercase from sos.report.plugins import (Plugin, regex_findall, _mangle_command, PluginOpt) from sos.archive import TarFileArchive from sos.policies.distros import LinuxPolicy from sos.policies.init_systems import InitSystem -from string import ascii_lowercase PATH = os.path.dirname(__file__) @@ -20,11 +20,11 @@ foreman_tests = [testenv:flake8] deps = flake8 -commands = flake8 sos tests +commands = flake8 [testenv:pylint] deps = pylint -commands = pylint --rcfile=tox.ini sos tests +commands = pylint --rcfile=tox.ini . [testenv:unit_tests] basepython = python3 @@ -57,6 +57,13 @@ deps = commands = nosetests -v --with-coverage --cover-package=sos tests/unittests --cover-html +[flake8] +exclude = + .git, + .tox, + debian, + docs + [pylint] # C0114, # missing-module-docstring # C0115, # missing-class-docstring @@ -72,5 +79,13 @@ enable = R0912, # too-many-branches R0914, # too-many-locals R1725, # super-with-arguments - W1404 # implicit-str-concat + W1404, # implicit-str-concat + W4901, # deprecated-module + W4902, # deprecated-method + W4903 # deprecated-argument max-line-length = 79 +recursive = y +ignore = + .git, + .tox, + debian |