Source code for common.rpm
#!/usr/bin/env python2
# Author(s): Milan Falesnik <mfalesni@redhat.com>
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Copyright (c) 2012 Red Hat, Inc. All rights reserved.
#
# This copyrighted material is made available to anyone wishing
# to use, modify, copy, or redistribute it subject to the terms
# and conditions of the GNU General Public License version 2.
#
# This program is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied
# warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
# PURPOSE. See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public
# License along with this program; if not, write to the Free
# Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import os
import re
import pytest
import common.shell
import common.rpm
import conftest as fixtures
[docs]class RPMPackageFailure(Exception):
pass
[docs]class RPMScriptletFailure(Exception):
pass
RPM_PROBLEMS_MESSAGES = { "S": "size",
"M": "mode",
"5": "MD5 checksum",
"D": "major and minor numbers",
"L": "symbolic link contents",
"U": "owner",
"G": "group",
"T": "modification time"}
""" Contains messages reported by RPM tests for each problem """
[docs]def check_for_errors(text):
""" This function checks for errors in text and returns text unchanged
:param text: text to be checked
:type text: ``str``
:returns: text
:rtype: ``str``
"""
errors = {'failure in rpm package': RPMPackageFailure, 'scriptlet failed, exit status 1': RPMScriptletFailure}
for error in errors.keys():
if error in text:
raise errors.keys()[error](text)
return text
[docs]def keys_import(keydir="/etc/pki/rpm-gpg"):
""" This function imports all keys in directory '/etc/pki/rpm-gpg' by default
"""
file_list = os.listdir(keydir)
for key_file in file_list:
common.shell.run("rpm --import %s/%s" % (keydir, key_file))
[docs]def signature_lines(package_lines):
""" Returns lines with signature informations of package
:param package_lines: rpm --verify output for the package
:type package_lines: ``list[str]``
:returns: List of lines speaking about signatures
:rtype: ``list(str)``
"""
sig = re.compile("[Ss]ignature")
for line in package_lines:
if sig.search(line):
yield line.split("#", 1)[-1].lstrip()
[docs]def wrong_files_lines(package_lines):
""" Returns lines with problem files
:param package_lines: rpm --verify output for the package
:type package_lines: ``list[str]``
:returns: List of lines speaking about wrong something about files
:rtype: ``list(str)``
"""
release = fixtures.rhel_release()
for line in package_lines:
line = line.strip()
if len(line) > 0:
# RHEL5 has 8 dots
# RHEL6 has 9 dots
if (not line.startswith(".........") and release.major == 6) or (not line.startswith("........") and release.major == 5):
yield line
[docs]def verify_package_files(package):
""" Verifies package in RPM database.
Checks output of the rpm -Vvv and looks for files, which have some problems (see http://www.rpm.org/max-rpm/s1-rpm-verify-output.html)
When using RHEL5, $? is ignored.
:param package: Package to check
:type package: ``str``
:returns: Bool whether verification succeeded
:rtype: ``bool``
"""
problems = []
source, stderr, rc = common.shell.command_stderr("rpm -Vvv %s" % package)
source = source.strip().split("\n")
if int(rc) != 0 and fixtures.rhel_release().major != 5: # RHEL5 will ignore returncode
problems.append("RPM $?=%d" % int(rc))
for line in common.rpm.wrong_files_lines(source):
status_type, filename = line.split("/", 1)
filename = "/" + filename
status_type = re.sub(r"\s+", " ", status_type).strip().split()
status = status_type[0]
file_type = ""
if len(status_type) > 1:
file_type = status_type[1].strip()
# if file_type == "c":
# continue
status_problems = []
for key in RPM_PROBLEMS_MESSAGES:
if key in status:
status_problems.append(RPM_PROBLEMS_MESSAGES[key])
if len(status_problems) == 0:
status_problems.append(status)
#TODO config?
problems.append("file %s has problems with %s" % (filename, ", ".join(status_problems) ) )
return problems
[docs]def verify_package_signed(package):
""" Verifies package in RPM database.
Checks for signature.
:param package: Package to check
:type package: ``str``
:returns: Bool whether verification of signature succeeded
:rtype: ``bool``
"""
problems = []
stdout, stderr, rc = common.shell.command_stderr("rpm -qvv %s" % package)
stderr = stderr.strip().split("\n")
if int(rc) != 0:
problems.append("RPM $?=%d" % int(rc))
for line in common.rpm.signature_lines(stderr):
fields = [x.strip() for x in line.rsplit(", key ID", 1)]
key_status = None
if re.match("^[0-9a-z]+$", fields[1]):
# RHEL 5
key_status = fields[0]
else:
# RHEL 6
key_status = fields[1]
key_status = key_status.rsplit(":", 1)[1].strip() # The key info is on the right side of the colon
if not key_status.upper() == "OK":
problems.append("No key signature")
return problems
[docs]def package_problems(package):
""" This functions returns reported problems with package
:param package: Package to check
:type package: ``str``
:returns: ``STDOUT`` of rpm -V
:rtype: ``str``
"""
return common.shell.run("rpm -Vvv %s" % package, None)
[docs]def package_build_host(package):
""" Returns build host of the package.
:param package: Package to check
:type package: ``str``
:returns: Build host of the package
:rtype: ``str``
"""
return common.shell.run("rpm -q --qf \"%%{BUILDHOST}\" %s" % package).strip()
[docs]def package_installed(package):
""" Returns whether is package installed or not
:param package: Package name
:type package: ``str``
:returns: ``True`` when package is installed, otherwise ``False``
:rtype: ``bool``
"""
try:
assert common.shell.Run.command("rpm -q %s" % package)
return True
except AssertionError:
return False
[docs]def q(package, qf=None):
""" Performs a 'rpm -q' command with optional --qf parameter
:param package: Package to query
:type package: ``str``
:param qf: ``--qf`` parameter value
:type qf: ``str``
:returns: Package informations
:rtype: ``str``
"""
cmd = "rpm -q "
if qf != None:
cmd += "--qf \"%s\" " % qf
cmd += package
return common.shell.run(cmd)
[docs]def qa(qf=None):
""" Performs a 'rpm -qa' command with optional --qf parameter
:param qf: ``--qf`` parameter value
:type qf: ``str``
:returns: Package informations
:rtype: ``str``
"""
cmd = "rpm -qa"
if qf != None:
cmd += " --qf \"%s\"" % qf
return common.shell.run(cmd)
[docs]def e(package):
""" Performs a 'rpm -e' command
:param package: Package to be removed
:type package: ``str``
"""
return common.shell.run("rpm -e %s" % package)
[docs]def ql(package):
""" Performs a 'rpm -ql' command
:param package: Package to be listed
:type package: ``str``
"""
return common.shell.run("rpm -ql %s" % package)