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.

107 lines
3.6 KiB

10 months ago
import json
import os
from leapp.libraries.common import mounting, utils
from leapp.libraries.stdlib import api
from leapp.models import fields, RepositoryData, RepositoryFile
try:
import dnf
except ImportError:
api.current_logger().warning('repofileutils.py: failed to import dnf')
def _parse_repository(repoid, repo_data):
def asbool(x):
return x == '1'
prepared = {'repoid': repoid, 'additional_fields': {}}
for key in repo_data.keys():
if key in RepositoryData.fields:
if isinstance(RepositoryData.fields[key], fields.Boolean):
repo_data[key] = asbool(repo_data[key])
prepared[key] = repo_data[key]
else:
prepared['additional_fields'][key] = repo_data[key]
prepared['additional_fields'] = json.dumps(prepared['additional_fields'])
return RepositoryData(**prepared)
def parse_repofile(repofile):
"""
Parse the given repo file.
:param repofile: Path to the repo file
:type repofile: str
:rtype: RepositoryFile
"""
data = []
with open(repofile, mode='r') as fp:
cp = utils.parse_config(fp, strict=False)
for repoid in cp.sections():
data.append(_parse_repository(repoid, dict(cp.items(repoid))))
return RepositoryFile(file=repofile, data=data)
def get_repodirs():
"""
Return all directories yum scans for repository files, if they exist.
By default, the possible paths on RHEL should be:
['/etc/yum.repos.d', '/etc/yum/repos.d', '/etc/distro.repos.d']
ATTENTION: Requires the dnf module to be present.
TODO: Get repodirs inside given context.
"""
with dnf.base.Base() as base:
base.conf.read(priority=dnf.conf.PRIO_MAINCONFIG)
return list({os.path.realpath(d) for d in base.conf.reposdir if os.path.isdir(d)})
def get_parsed_repofiles(context=mounting.NotIsolatedActions(base_dir='/')):
"""
Scan all repositories on the system.
Repositories are scanned under repository directories (as reported by dnf)
of the given context. By default the context is the host system.
ATTENTION: Do not forget to ensure the redhat.repo file is regenerated
by RHSM when used.
:param context: An instance of a mounting.IsolatedActions class
:type context: mounting.IsolatedActions class
:rtype: List(RepositoryFile)
"""
repofiles = []
cmd = ['find', '-L'] + get_repodirs() + ['-maxdepth', '1', '-type', 'f', '-name', '*.repo']
repofiles_paths = context.call(cmd, split=True)['stdout']
for repofile_path in repofiles_paths:
repofile = parse_repofile(context.full_path(repofile_path))
# we want full path in cotext, not the real full path
repofile.file = repofile_path
repofiles.append(repofile)
return repofiles
def _invert_dict(data):
"""{a: [b]} -> {b: [a]}"""
inv_dict = {}
for key in data.keys():
for value in data[key]:
inv_dict[value] = inv_dict.get(value, []) + [key]
return inv_dict
def get_duplicate_repositories(repofiles):
"""
Return dict of duplicate repositories {repoid: [repofile_path]}
A repository is defined multiple times if it exists in multiple repofiles.
Redefinition inside one repository file is ignored (same in DNF).
:param repofiles:
:type repofiles: List(RepositoryFile)
:rtype: dict {repoid: repofilepath}
"""
rf_repos = {repofile.file: [repo.repoid for repo in repofile.data] for repofile in repofiles}
repos = _invert_dict(rf_repos)
return {repo: set(rfiles) for repo, rfiles in repos.items() if len(set(rfiles)) > 1}