aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJake Hunsaker <jhunsake@redhat.com>2020-12-17 16:45:54 -0500
committerJake Hunsaker <jhunsake@redhat.com>2021-01-04 11:55:11 -0500
commit0c1098fa55d4fd4101193d8609b3845f90dddb35 (patch)
tree588e4e48ce2f952f65112e589c9f7185a070f24c
parent516f42719cdb98b7b0d035b115e7f9037616499d (diff)
downloadsos-0c1098fa55d4fd4101193d8609b3845f90dddb35.tar.gz
[PackageManager] Separate PackageManager from policies
Moves `PackageManager` out from `sos/policies/__init__.py` into a new `sos/policies/package_managers` subdir. Future commits will aim to canonicalize package manager subclasses for policies to use, and ease the creation of new reusable package managers. Related: #2349 Signed-off-by: Jake Hunsaker <jhunsake@redhat.com>
-rw-r--r--sos/policies/__init__.py236
-rw-r--r--sos/policies/distros/debian.py2
-rw-r--r--sos/policies/distros/redhat.py3
-rw-r--r--sos/policies/distros/suse.py2
-rw-r--r--sos/policies/package_managers/__init__.py245
-rw-r--r--tests/policy_tests.py3
6 files changed, 253 insertions, 238 deletions
diff --git a/sos/policies/__init__.py b/sos/policies/__init__.py
index 3dc7e1ad..a9172b2e 100644
--- a/sos/policies/__init__.py
+++ b/sos/policies/__init__.py
@@ -1,23 +1,18 @@
import os
-import re
import platform
import time
import json
-import fnmatch
import tempfile
import random
import string
from pwd import getpwuid
-from sos.utilities import (ImporterHelper,
- import_module,
- shell_out,
- get_human_readable)
+from sos.policies.package_managers import PackageManager
+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
-from pipes import quote
PRESETS_PATH = "/etc/sos/presets.d"
@@ -50,233 +45,6 @@ def load(cache={}, sysroot=None, init=None, probe_runtime=True,
return cache['policy']
-class PackageManager(object):
- """Encapsulates a package manager. If you provide a query_command to the
- constructor it should print each package on the system in the following
- format::
-
- package name|package.version
-
- You may also subclass this class and provide a get_pkg_list method to
- build the list of packages and versions.
-
- :cvar query_command: The command to use for querying packages
- :vartype query_command: ``str`` or ``None``
-
- :cvar verify_command: The command to use for verifying packages
- :vartype verify_command: ``str`` or ``None``
-
- :cvar verify_filter: Optional filter to use for controlling package
- verification
- :vartype verify_filter: ``str or ``None``
-
- :cvar files_command: The command to use for getting file lists for packages
- :vartype files_command: ``str`` or ``None``
-
- :cvar chroot: Perform a chroot when executing `files_command`
- :vartype chroot: ``bool``
-
- :cvar remote_exec: If package manager is on a remote system (e.g. for
- sos collect), prepend this SSH command to run remotely
- :vartype remote_exec: ``str`` or ``None``
- """
-
- query_command = None
- verify_command = None
- verify_filter = None
- chroot = None
- files = None
-
- def __init__(self, chroot=None, query_command=None,
- verify_command=None, verify_filter=None,
- files_command=None, remote_exec=None):
- self.packages = {}
- self.files = []
-
- self.query_command = query_command if query_command else None
- self.verify_command = verify_command if verify_command else None
- self.verify_filter = verify_filter if verify_filter else None
- self.files_command = files_command if files_command else None
-
- # if needed, append the remote command to these so that this returns
- # the remote package details, not local
- if remote_exec:
- for cmd in ['query_command', 'verify_command', 'files_command']:
- if getattr(self, cmd) is not None:
- _cmd = getattr(self, cmd)
- setattr(self, cmd, "%s %s" % (remote_exec, quote(_cmd)))
-
- if chroot:
- self.chroot = chroot
-
- def all_pkgs_by_name(self, name):
- """
- Get a list of packages that match name.
-
- :param name: The name of the package
- :type name: ``str``
-
- :returns: List of all packages matching `name`
- :rtype: ``list``
- """
- return fnmatch.filter(self.all_pkgs().keys(), name)
-
- def all_pkgs_by_name_regex(self, regex_name, flags=0):
- """
- Get a list of packages that match regex_name.
-
- :param regex_name: The regex to use for matching package names against
- :type regex_name: ``str``
-
- :param flags: Flags for the `re` module when matching `regex_name`
-
- :returns: All packages matching `regex_name`
- :rtype: ``list``
- """
- reg = re.compile(regex_name, flags)
- return [pkg for pkg in self.all_pkgs().keys() if reg.match(pkg)]
-
- def pkg_by_name(self, name):
- """
- Get a single package that matches name.
-
- :param name: The name of the package
- :type name: ``str``
-
- :returns: The first package that matches `name`
- :rtype: ``str``
- """
- pkgmatches = self.all_pkgs_by_name(name)
- if (len(pkgmatches) != 0):
- return self.all_pkgs_by_name(name)[-1]
- else:
- return None
-
- def get_pkg_list(self):
- """Returns a dictionary of packages in the following
- format::
-
- {'package_name': {'name': 'package_name',
- 'version': 'major.minor.version'}}
-
- """
- if self.query_command:
- cmd = self.query_command
- pkg_list = shell_out(
- cmd, timeout=0, chroot=self.chroot
- ).splitlines()
-
- for pkg in pkg_list:
- if '|' not in pkg:
- continue
- elif pkg.count("|") == 1:
- name, version = pkg.split("|")
- release = None
- elif pkg.count("|") == 2:
- name, version, release = pkg.split("|")
- self.packages[name] = {
- 'name': name,
- 'version': version.split(".")
- }
- release = release if release else None
- self.packages[name]['release'] = release
-
- return self.packages
-
- def pkg_version(self, pkg):
- """Returns the entry in self.packages for pkg if it exists
-
- :param pkg: The name of the package
- :type pkg: ``str``
-
- :returns: Package name and version, if package exists
- :rtype: ``dict`` if found, else ``None``
- """
- pkgs = self.all_pkgs()
- if pkg in pkgs:
- return pkgs[pkg]
- return None
-
- def all_pkgs(self):
- """
- Get a list of all packages.
-
- :returns: All packages, with name and version, installed on the system
- :rtype: ``dict``
- """
- if not self.packages:
- self.packages = self.get_pkg_list()
- return self.packages
-
- def pkg_nvra(self, pkg):
- """Get the name, version, release, and architecture for a package
-
- :param pkg: The name of the package
- :type pkg: ``str``
-
- :returns: name, version, release, and arch of the package
- :rtype: ``tuple``
- """
- fields = pkg.split("-")
- version, release, arch = fields[-3:]
- name = "-".join(fields[:-3])
- return (name, version, release, arch)
-
- def all_files(self):
- """
- Get a list of files known by the package manager
-
- :returns: All files known by the package manager
- :rtype: ``list``
- """
- if self.files_command and not self.files:
- cmd = self.files_command
- files = shell_out(cmd, timeout=0, chroot=self.chroot)
- self.files = files.splitlines()
- return self.files
-
- def build_verify_command(self, packages):
- """build_verify_command(self, packages) -> str
- Generate a command to verify the list of packages given
- in ``packages`` using the native package manager's
- verification tool.
-
- The command to be executed is returned as a string that
- may be passed to a command execution routine (for e.g.
- ``sos_get_command_output()``.
-
- :param packages: a string, or a list of strings giving
- package names to be verified.
- :returns: a string containing an executable command
- that will perform verification of the given
- packages.
- :rtype: str or ``NoneType``
- """
- if not self.verify_command:
- return None
-
- # The re.match(pkg) used by all_pkgs_by_name_regex() may return
- # an empty list (`[[]]`) when no package matches: avoid building
- # an rpm -V command line with the empty string as the package
- # list in this case.
- by_regex = self.all_pkgs_by_name_regex
- verify_list = filter(None, map(by_regex, packages))
-
- # No packages after regex match?
- if not verify_list:
- return None
-
- verify_packages = ""
- for package_list in verify_list:
- for package in package_list:
- if any([f in package for f in self.verify_filter]):
- continue
- if len(verify_packages):
- verify_packages += " "
- verify_packages += package
- return self.verify_command + " " + verify_packages
-
-
#: Constants for on-disk preset fields
DESC = "desc"
NOTE = "note"
diff --git a/sos/policies/distros/debian.py b/sos/policies/distros/debian.py
index bef711b0..29c74dc9 100644
--- a/sos/policies/distros/debian.py
+++ b/sos/policies/distros/debian.py
@@ -8,7 +8,7 @@
from sos.report.plugins import DebianPlugin
from sos.policies.distros import LinuxPolicy
-from sos.policies import PackageManager
+from sos.policies.package_managers import PackageManager
import os
diff --git a/sos/policies/distros/redhat.py b/sos/policies/distros/redhat.py
index 8c3ae621..589d0213 100644
--- a/sos/policies/distros/redhat.py
+++ b/sos/policies/distros/redhat.py
@@ -13,8 +13,9 @@ import sys
import re
from sos.report.plugins import RedHatPlugin
-from sos.policies import PackageManager, PresetDefaults
+from sos.policies import PresetDefaults
from sos.policies.distros import LinuxPolicy
+from sos.policies.package_managers import PackageManager
from sos import _sos as _
from sos.options import SoSOptions
diff --git a/sos/policies/distros/suse.py b/sos/policies/distros/suse.py
index a4d73f56..928f4abb 100644
--- a/sos/policies/distros/suse.py
+++ b/sos/policies/distros/suse.py
@@ -12,7 +12,7 @@ import sys
from sos.report.plugins import RedHatPlugin, SuSEPlugin
from sos.policies.distros import LinuxPolicy
-from sos.policies import PackageManager
+from sos.policies.package_managers import PackageManager
from sos import _sos as _
diff --git a/sos/policies/package_managers/__init__.py b/sos/policies/package_managers/__init__.py
new file mode 100644
index 00000000..f9ce2a09
--- /dev/null
+++ b/sos/policies/package_managers/__init__.py
@@ -0,0 +1,245 @@
+# Copyright 2020 Red Hat, Inc. Jake Hunsaker <jhunsake@redhat.com>
+
+# This file is part of the sos project: https://github.com/sosreport/sos
+#
+# This copyrighted material is made available to anyone wishing to use,
+# modify, copy, or redistribute it subject to the terms and conditions of
+# version 2 of the GNU General Public License.
+#
+# See the LICENSE file in the source distribution for further information.
+
+import re
+import fnmatch
+
+from sos.utilities import shell_out
+from pipes import quote
+
+
+class PackageManager():
+ """Encapsulates a package manager. If you provide a query_command to the
+ constructor it should print each package on the system in the following
+ format::
+
+ package name|package.version
+
+ You may also subclass this class and provide a get_pkg_list method to
+ build the list of packages and versions.
+
+ :cvar query_command: The command to use for querying packages
+ :vartype query_command: ``str`` or ``None``
+
+ :cvar verify_command: The command to use for verifying packages
+ :vartype verify_command: ``str`` or ``None``
+
+ :cvar verify_filter: Optional filter to use for controlling package
+ verification
+ :vartype verify_filter: ``str or ``None``
+
+ :cvar files_command: The command to use for getting file lists for packages
+ :vartype files_command: ``str`` or ``None``
+
+ :cvar chroot: Perform a chroot when executing `files_command`
+ :vartype chroot: ``bool``
+
+ :cvar remote_exec: If package manager is on a remote system (e.g. for
+ sos collect), prepend this SSH command to run remotely
+ :vartype remote_exec: ``str`` or ``None``
+ """
+
+ query_command = None
+ verify_command = None
+ verify_filter = None
+ chroot = None
+ files = None
+
+ def __init__(self, chroot=None, query_command=None,
+ verify_command=None, verify_filter=None,
+ files_command=None, remote_exec=None):
+ self.packages = {}
+ self.files = []
+
+ self.query_command = query_command if query_command else None
+ self.verify_command = verify_command if verify_command else None
+ self.verify_filter = verify_filter if verify_filter else None
+ self.files_command = files_command if files_command else None
+
+ # if needed, append the remote command to these so that this returns
+ # the remote package details, not local
+ if remote_exec:
+ for cmd in ['query_command', 'verify_command', 'files_command']:
+ if getattr(self, cmd) is not None:
+ _cmd = getattr(self, cmd)
+ setattr(self, cmd, "%s %s" % (remote_exec, quote(_cmd)))
+
+ if chroot:
+ self.chroot = chroot
+
+ def all_pkgs_by_name(self, name):
+ """
+ Get a list of packages that match name.
+
+ :param name: The name of the package
+ :type name: ``str``
+
+ :returns: List of all packages matching `name`
+ :rtype: ``list``
+ """
+ return fnmatch.filter(self.all_pkgs().keys(), name)
+
+ def all_pkgs_by_name_regex(self, regex_name, flags=0):
+ """
+ Get a list of packages that match regex_name.
+
+ :param regex_name: The regex to use for matching package names against
+ :type regex_name: ``str``
+
+ :param flags: Flags for the `re` module when matching `regex_name`
+
+ :returns: All packages matching `regex_name`
+ :rtype: ``list``
+ """
+ reg = re.compile(regex_name, flags)
+ return [pkg for pkg in self.all_pkgs().keys() if reg.match(pkg)]
+
+ def pkg_by_name(self, name):
+ """
+ Get a single package that matches name.
+
+ :param name: The name of the package
+ :type name: ``str``
+
+ :returns: The first package that matches `name`
+ :rtype: ``str``
+ """
+ pkgmatches = self.all_pkgs_by_name(name)
+ if (len(pkgmatches) != 0):
+ return self.all_pkgs_by_name(name)[-1]
+ else:
+ return None
+
+ def get_pkg_list(self):
+ """Returns a dictionary of packages in the following
+ format::
+
+ {'package_name': {'name': 'package_name',
+ 'version': 'major.minor.version'}}
+
+ """
+ if self.query_command:
+ cmd = self.query_command
+ pkg_list = shell_out(
+ cmd, timeout=0, chroot=self.chroot
+ ).splitlines()
+
+ for pkg in pkg_list:
+ if '|' not in pkg:
+ continue
+ elif pkg.count("|") == 1:
+ name, version = pkg.split("|")
+ release = None
+ elif pkg.count("|") == 2:
+ name, version, release = pkg.split("|")
+ self.packages[name] = {
+ 'name': name,
+ 'version': version.split(".")
+ }
+ release = release if release else None
+ self.packages[name]['release'] = release
+
+ return self.packages
+
+ def pkg_version(self, pkg):
+ """Returns the entry in self.packages for pkg if it exists
+
+ :param pkg: The name of the package
+ :type pkg: ``str``
+
+ :returns: Package name and version, if package exists
+ :rtype: ``dict`` if found, else ``None``
+ """
+ pkgs = self.all_pkgs()
+ if pkg in pkgs:
+ return pkgs[pkg]
+ return None
+
+ def all_pkgs(self):
+ """
+ Get a list of all packages.
+
+ :returns: All packages, with name and version, installed on the system
+ :rtype: ``dict``
+ """
+ if not self.packages:
+ self.packages = self.get_pkg_list()
+ return self.packages
+
+ def pkg_nvra(self, pkg):
+ """Get the name, version, release, and architecture for a package
+
+ :param pkg: The name of the package
+ :type pkg: ``str``
+
+ :returns: name, version, release, and arch of the package
+ :rtype: ``tuple``
+ """
+ fields = pkg.split("-")
+ version, release, arch = fields[-3:]
+ name = "-".join(fields[:-3])
+ return (name, version, release, arch)
+
+ def all_files(self):
+ """
+ Get a list of files known by the package manager
+
+ :returns: All files known by the package manager
+ :rtype: ``list``
+ """
+ if self.files_command and not self.files:
+ cmd = self.files_command
+ files = shell_out(cmd, timeout=0, chroot=self.chroot)
+ self.files = files.splitlines()
+ return self.files
+
+ def build_verify_command(self, packages):
+ """build_verify_command(self, packages) -> str
+ Generate a command to verify the list of packages given
+ in ``packages`` using the native package manager's
+ verification tool.
+
+ The command to be executed is returned as a string that
+ may be passed to a command execution routine (for e.g.
+ ``sos_get_command_output()``.
+
+ :param packages: a string, or a list of strings giving
+ package names to be verified.
+ :returns: a string containing an executable command
+ that will perform verification of the given
+ packages.
+ :rtype: str or ``NoneType``
+ """
+ if not self.verify_command:
+ return None
+
+ # The re.match(pkg) used by all_pkgs_by_name_regex() may return
+ # an empty list (`[[]]`) when no package matches: avoid building
+ # an rpm -V command line with the empty string as the package
+ # list in this case.
+ by_regex = self.all_pkgs_by_name_regex
+ verify_list = filter(None, map(by_regex, packages))
+
+ # No packages after regex match?
+ if not verify_list:
+ return None
+
+ verify_packages = ""
+ for package_list in verify_list:
+ for package in package_list:
+ if any([f in package for f in self.verify_filter]):
+ continue
+ if len(verify_packages):
+ verify_packages += " "
+ verify_packages += package
+ return self.verify_command + " " + verify_packages
+
+
+# vim: set et ts=4 sw=4 :
diff --git a/tests/policy_tests.py b/tests/policy_tests.py
index 263ea352..4b248b70 100644
--- a/tests/policy_tests.py
+++ b/tests/policy_tests.py
@@ -7,7 +7,8 @@
# See the LICENSE file in the source distribution for further information.
import unittest
-from sos.policies import Policy, PackageManager, import_policy
+from sos.policies import Policy, import_policy
+from sos.policies.package_managers import PackageManager
from sos.report.plugins import (Plugin, IndependentPlugin,
RedHatPlugin, DebianPlugin)