Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Establish atix main #1

Open
wants to merge 24 commits into
base: atix-main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
221d1f2
[ATIX] Add DebProfile to Profilemanager
Aug 1, 2019
5432dc2
[ATIX] Move repofile to rhsm subpackage
Aug 8, 2019
5abc786
[ATIX] Generate enabled_repos from repofile
Aug 8, 2019
90a4243
[ATIX] Fix report enabled deb-repositories only
m-bucher Oct 20, 2020
db6bcdc
[ATIX] Allow syspurpose file to be written
sbernhard Dec 7, 2021
4f6026d
[ATIX] Add handling of params in repository urls
hstct Jan 17, 2023
e86d256
[ATIX] Use repofile from rhsm subpackage
goarsna Jun 28, 2023
3f9ba06
[ATIX] Remove failing method annotations
goarsna Jun 28, 2023
2aed02b
[ATIX] Adjust katello script for Python3
goarsna Jun 28, 2023
850ccc8
[ATIX] Fix APT component selection
quba42 Jul 3, 2023
f408516
[ATIX] Fix failing profile upload with SCA enabled
m-bucher Feb 2, 2024
f79cf5f
[ATIX] Add deb repo-setting Signed-By
m-bucher May 24, 2024
8c8167a
WIP Fix setup.py style
sbernhard Jul 29, 2024
c871278
WIP Fix baseurl style in profile
sbernhard Jul 29, 2024
09c01db
WIP Fix debprofile style in profile
sbernhard Jul 29, 2024
1c6f311
WIP Fix repofile style
sbernhard Jul 29, 2024
ba92938
WIP Fix cache style
sbernhard Jul 29, 2024
7ca91fb
WIP Fix syspurpose style
sbernhard Jul 29, 2024
0f6741a
WIP - fix tests. repofile moved from sub-man to rhsm
sbernhard Aug 1, 2024
085b20e
WIP: fix repolib tests for apt. mock apt
sbernhard Aug 1, 2024
a15dad4
WIP: fix repofile.py - the /etc/apt/trusted.gpg.d dir may not exist
sbernhard Aug 2, 2024
752175a
WIP: setup.py package profile update packaged twice
sbernhard Aug 2, 2024
9273491
fix test
sbernhard Aug 8, 2024
214d088
WIP: fix repofile mappings.items() issue
sbernhard Aug 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 16 additions & 99 deletions src/rhsm/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,36 @@

import importlib.util
import rpm
import os.path
from typing import List, Union

from rhsm import ourjson as json
from rhsm.utils import suppress_output
from iniparse import SafeConfigParser, ConfigParser
from rhsm.repofile import get_repo_file_classes
from cloud_what import provider

try:
import dnf
from rhsm.repofile import dnf
except ImportError:
dnf = None

try:
import libdnf
from rhsm.repofile import libdnf
except ImportError:
libdnf = None

try:
import yum
from rhsm.repofile import yum
except ImportError:
yum = None

use_zypper: bool = importlib.util.find_spec("zypp_plugin") is not None

try:
import apt
from rhsm.repofile import apt
except ImportError:
apt = None

use_zypper: bool = importlib.util.find_spec("zypp_plugin") is not None


if use_zypper:
REPOSITORY_PATH = "/etc/rhsm/zypper.repos.d/redhat.repo"
else:
Expand Down Expand Up @@ -196,111 +196,28 @@ def collect(self) -> List[dict]:
return self.content


class EnabledRepos:
def __generate(self) -> List[dict]:
if not os.path.exists(self.repofile):
return []

# Unfortuantely, we can not use the SafeConfigParser for zypper repo
# files because the repository urls contains strings which the
# SafeConfigParser don't like. It would crash with
# ConfigParser.InterpolationSyntaxError: '%' must be followed by '%' or '('
if use_zypper:
config = ConfigParser()
else:
config = SafeConfigParser()
config.read(self.repofile)
enabled_sections = [section for section in config.sections() if config.getboolean(section, "enabled")]
enabled_repos = []
for section in enabled_sections:
try:
enabled_repos.append(
{
"repositoryid": section,
"baseurl": [self._format_baseurl(config.get(section, "baseurl"))],
}
)
except ImportError:
break
return enabled_repos

def __init__(self, repo_file: str) -> None:
"""
Initialize EnabledRepos
:param repo_file: A repo file path used to filter the report.
"""
if dnf is not None:
self.db = dnf.dnf.Base()
elif yum is not None:
self.yb = yum.YumBase()

self.repofile: str = repo_file
self.content: List[dict] = self.__generate()

def __str__(self) -> str:
return str(self.content)

def _format_baseurl(self, repo_url: str) -> str:
"""
Returns a well formatted baseurl string
:param repo_url: a repo URL that you want to format
"""
if use_zypper:
return self._cut_question_mark(repo_url)
else:
mappings = self._obtain_mappings()
return repo_url.replace("$releasever", mappings["releasever"]).replace(
"$basearch", mappings["basearch"]
)

def _cut_question_mark(self, repo_url) -> str:
"""
Returns a string where everything after the first occurrence of '?' is truncated
:param repo_url: a repo URL that you want to modify
"""
return repo_url[: repo_url.find("?")]

@suppress_output
def _obtain_mappings(self) -> dict:
"""
returns a hash with "basearch" and "releasever" set. This will try dnf first, and them yum if dnf is
not installed.
"""
if dnf is not None:
return self._obtain_mappings_dnf()
elif yum is not None:
return self._obtain_mappings_yum()
else:
log.error("Unable to load module for any supported package manager (dnf, yum).")
raise ImportError

def _obtain_mappings_dnf(self) -> dict:
return {
"releasever": self.db.conf.substitutions["releasever"],
"basearch": self.db.conf.substitutions["basearch"],
}

def _obtain_mappings_yum(self) -> dict:
return {"releasever": self.yb.conf.yumvar["releasever"], "basearch": self.yb.conf.yumvar["basearch"]}


class EnabledReposProfile:
"""
Collect information about enabled repositories
"""

def __init__(self, repo_file: str = REPOSITORY_PATH) -> None:
self._enabled_repos: EnabledRepos = EnabledRepos(repo_file)
self._content = []
for repo_file_cls, _ in get_repo_file_classes():
repo_file = repo_file_cls()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not rather be:
repo_file = repo_file_cls(repo_file)
This might have further implications, since apparently for now this expects to always use the repo_file_cls()'s default-value for path

repo_file.read()
self._content.extend(repo_file.enabled_repos())
self._content.sort(key=lambda x: x['baseurl'])

def __eq__(self, other: "EnabledReposProfile") -> bool:
return self._enabled_repos.content == other._enabled_repos.content
return self._content == other._content

def collect(self) -> List[dict]:
"""
Gather list of enabled repositories
:return: List of enabled repositories
"""
return self._enabled_repos.content
return self._content


class Package:
Expand Down
91 changes: 85 additions & 6 deletions src/rhsm/repofile.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,30 @@
import sys

try:
import apt
from debian.deb822 import Deb822
except ImportError:
apt = None

HAS_DEB822 = True
try:
import dnf
except ImportError:
HAS_DEB822 = False
dnf = None

try:
import libdnf
except ImportError:
libdnf = None

try:
import yum
except ImportError:
yum = None

try:
import zypp
except ImportError:
zypp = None

from subscription_manager import utils
from subscription_manager.certdirectory import Path
Expand Down Expand Up @@ -77,7 +96,7 @@ class Repo(dict):

def __init__(self, repo_id: str, existing_values: List = None):
super().__init__()
if HAS_DEB822 is True:
if apt is not None:
self.PROPERTIES["arches"] = (1, None)

# existing_values is a list of 2-tuples
Expand Down Expand Up @@ -139,7 +158,6 @@ def from_ent_cert_content(
repoid_vars = [part[1:] for part in repo_parts if part.startswith("$")]
if HAS_YUM and repoid_vars:
repo["ui_repoid_vars"] = " ".join(repoid_vars)

# If no GPG key URL is specified, turn gpgcheck off:
gpg_url = content.gpg
if not gpg_url:
Expand Down Expand Up @@ -383,6 +401,9 @@ def create(self) -> None:
def fix_content(self, content: str) -> str:
return content

def enabled_repos(self):
raise NotImplementedError("'enabled_repos' is not implemented for this repo_file.")

@classmethod
def installed(cls) -> bool:
return os.path.exists(Path.abs(cls.PATH))
Expand All @@ -392,7 +413,7 @@ def server_value_repo_file(cls) -> "RepoFileBase":
return cls("var/lib/rhsm/repo_server_val/")


if HAS_DEB822:
if apt is not None:

class AptRepoFile(RepoFileBase):
PATH: str = "etc/apt/sources.list.d"
Expand Down Expand Up @@ -485,6 +506,12 @@ def fix_content(self, content):

return apt_cont

def enabled_repos(self):
return [
{'repositoryid': repo822['id'], 'baseurl': [repo822['baseurl']]}
for repo822 in self.repos822
]


class YumRepoFile(RepoFileBase, ConfigParser):
PATH = "etc/yum.repos.d/"
Expand Down Expand Up @@ -560,6 +587,54 @@ def section(self, section: str) -> "Repo":
if self.has_section(section):
return Repo(section, self.items(section))

def enabled_repos(self):
result = []
try:
enabled_sections = [section for section in self.sections() if config.getboolean(section, "enabled")]
for section in enabled_sections:
result.append(
{
"repositoryid": section,
"baseurl": [self._replace_vars(self.get(section, "baseurl"))]
}
)
except ImportError:
pass
return result

def _replace_vars(self, repo_url):
"""
returns a string with "$basearch" and "$releasever" replaced.

:param repo_url: a repo URL that you want to replace $basearch and $releasever in.
:type path: str
"""
mappings = self._obtain_mappings()
for key, value in mappings:
repo_url = repo_url.replace(key, value)
return repo_url

def _obtain_mappings(self):
"""
returns a hash with "basearch" and "releasever" set. This will try dnf first, and then yum if dnf is
not installed.
"""
if dnf is not None:
return self._obtain_mappings_dnf()
elif yum is not None:
return self._obtain_mappings_yum()
else:
log.error('Unable to load module for any supported package manager (dnf, yum).')
raise ImportError

def _obtain_mappings_dnf(self):
db = dnf.dnf.Base()
return {'$releasever': db.conf.substitutions['releasever'], '$basearch': db.conf.substitutions['basearch']}

def _obtain_mappings_yum(self):
yb = yum.YumBase()
return {'$releasever': yb.conf.yumvar['releasever'], '$basearch': yb.conf.yumvar['basearch']}


class ZypperRepoFile(YumRepoFile):
"""
Expand Down Expand Up @@ -690,10 +765,14 @@ def fix_content(self, content: "Content") -> str:
def server_value_repo_file(cls) -> "ZypperRepoFile":
return cls("var/lib/rhsm/repo_server_val/", "zypper_{}".format(cls.NAME))

def _obtain_mappings(self):
db = zypp.ZConfig.instance()
return {'$basearch': str(db.systemArchitecture())}


def init_repo_file_classes() -> List[Tuple[type(RepoFileBase), str]]:
repo_file_classes: List[type(RepoFileBase)] = [YumRepoFile, ZypperRepoFile]
if HAS_DEB822:
if apt is not None:
repo_file_classes.append(AptRepoFile)
_repo_files: List[Tuple[type(RepoFileBase), type(RepoFileBase)]] = [
(RepoFile, RepoFile.server_value_repo_file) for RepoFile in repo_file_classes if RepoFile.installed()
Expand Down
2 changes: 1 addition & 1 deletion src/subscription_manager/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,7 +558,7 @@ def _sync_with_server(
_combined_profile: List[Dict] = [
{
"content_type": key,
"profile": value
"profile": value,
}
for key, value in combined_profile.items()
]
Expand Down