forked from msvsphere/leapp-repository
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
117 lines
4.2 KiB
117 lines
4.2 KiB
10 months ago
|
from leapp.libraries import stdlib
|
||
|
from leapp.models import InstalledRPM
|
||
|
|
||
|
|
||
|
def get_installed_rpms():
|
||
|
rpm_cmd = [
|
||
|
'/bin/rpm',
|
||
|
'-qa',
|
||
|
'--queryformat',
|
||
|
r'%{NAME}|%{VERSION}|%{RELEASE}|%|EPOCH?{%{EPOCH}}:{0}||%|PACKAGER?{%{PACKAGER}}:{(none)}||%|'
|
||
|
r'ARCH?{%{ARCH}}:{}||%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{(none)}|}|\n'
|
||
|
]
|
||
|
try:
|
||
|
return stdlib.run(rpm_cmd, split=True)['stdout']
|
||
|
except stdlib.CalledProcessError as err:
|
||
|
error = 'Execution of {CMD} returned {RC}. Unable to find installed packages.'.format(CMD=err.command,
|
||
|
RC=err.exit_code)
|
||
|
stdlib.api.current_logger().error(error)
|
||
|
return []
|
||
|
|
||
|
|
||
|
def create_lookup(model, field, keys, context=stdlib.api):
|
||
|
"""
|
||
|
Create a lookup set from one of the model fields.
|
||
|
|
||
|
:param model: model class
|
||
|
:param field: model field, its value will be taken for lookup data
|
||
|
:param key: property of the field's data that will be used to build a resulting set
|
||
|
:param context: context of the execution
|
||
|
"""
|
||
|
data = getattr(next((m for m in context.consume(model)), model()), field)
|
||
|
try:
|
||
|
return {tuple(getattr(obj, key) for key in keys) for obj in data} if data else set()
|
||
|
except TypeError:
|
||
|
# data is not iterable, not lookup can be built
|
||
|
stdlib.api.current_logger().error(
|
||
|
"{model}.{field}.{keys} is not iterable, can't build lookup".format(
|
||
|
model=model, field=field, keys=keys))
|
||
|
return set()
|
||
|
|
||
|
|
||
|
def has_package(model, package_name, arch=None, version=None, release=None, context=stdlib.api):
|
||
|
"""
|
||
|
Expects a model InstalledRedHatSignedRPM or InstalledUnsignedRPM.
|
||
|
Can be useful in cases like a quick item presence check, ex. check in actor that
|
||
|
a certain package is installed.
|
||
|
|
||
|
:param model: model class
|
||
|
:param package_name: package to be checked
|
||
|
:param arch: filter by architecture. None means all arches.
|
||
|
:param version: filter by version. None means all versions.
|
||
|
:param release: filter by release. None means all releases.
|
||
|
"""
|
||
|
if not (isinstance(model, type) and issubclass(model, InstalledRPM)):
|
||
|
return False
|
||
|
keys = ['name']
|
||
|
if arch:
|
||
|
keys.append('arch')
|
||
|
if version:
|
||
|
keys.append('version')
|
||
|
if release:
|
||
|
keys.append('release')
|
||
|
|
||
|
attributes = [package_name]
|
||
|
attributes += [attr for attr in (arch, version, release) if attr is not None]
|
||
|
rpm_lookup = create_lookup(model, field='items', keys=keys, context=context)
|
||
|
return tuple(attributes) in rpm_lookup
|
||
|
|
||
|
|
||
|
def _read_rpm_modifications(config):
|
||
|
"""
|
||
|
Ask RPM database whether the configuration file was modified.
|
||
|
|
||
|
:param config: a config file to check
|
||
|
"""
|
||
|
try:
|
||
|
return stdlib.run(['rpm', '-Vf', config], split=True, checked=False)['stdout']
|
||
|
except OSError as err:
|
||
|
error = 'Failed to check the modification status of the file {}: {}'.format(config, str(err))
|
||
|
stdlib.api.current_logger().error(error)
|
||
|
return []
|
||
|
|
||
|
|
||
|
def _parse_config_modification(data, config):
|
||
|
"""
|
||
|
Handle the output of rpm verify command to figure out if configuration file was modified.
|
||
|
|
||
|
:param data: output of the rpm verify
|
||
|
:param config: a config file to check
|
||
|
"""
|
||
|
|
||
|
# First assume it is not modified -- empty data says it is not modified
|
||
|
modified = False
|
||
|
for line in data:
|
||
|
parts = line.split(' ')
|
||
|
# The last part of the line is the actual file we care for
|
||
|
if parts[-1] == config:
|
||
|
# First part contains information, if the size and digest differ
|
||
|
if '5' in parts[0] or 'S' in parts[0]:
|
||
|
modified = True
|
||
|
# Ignore any other files lurking here
|
||
|
|
||
|
return modified
|
||
|
|
||
|
|
||
|
def check_file_modification(config):
|
||
|
"""
|
||
|
Check if the given configuration file tracked by RPM was modified
|
||
|
|
||
|
This is useful when figuring out if the file will be replaced by the rpm on the upgrade
|
||
|
or we need to take care of the upgrade manually.
|
||
|
|
||
|
:param config: The configuration file to check
|
||
|
"""
|
||
|
output = _read_rpm_modifications(config)
|
||
|
return _parse_config_modification(output, config)
|