import lorax-28.14.55-2.el8
This commit is contained in:
parent
8723d8b0f3
commit
2ad18cbf03
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
|||||||
SOURCES/lorax-28.14.42.tar.gz
|
SOURCES/lorax-28.14.55.tar.gz
|
||||||
|
@ -1 +1 @@
|
|||||||
cac7865a2a3a7bae71dd2bc3f93c51f96c60a903 SOURCES/lorax-28.14.42.tar.gz
|
fe7ec2654b4cacbf6212a8384d9cedf3f9228b57 SOURCES/lorax-28.14.55.tar.gz
|
||||||
|
@ -1,137 +0,0 @@
|
|||||||
From 7f62934dbefd97aada667583ff6763b02a346bf6 Mon Sep 17 00:00:00 2001
|
|
||||||
From: "Brian C. Lane" <bcl@redhat.com>
|
|
||||||
Date: Tue, 21 Apr 2020 15:31:00 -0700
|
|
||||||
Subject: [PATCH 1/3] lorax: Add --skip-branding cmdline argument
|
|
||||||
|
|
||||||
Also document how branding currently works. See docs/lorax.rst
|
|
||||||
|
|
||||||
Resolves: rhbz#1826479
|
|
||||||
---
|
|
||||||
docs/lorax.rst | 23 +++++++++++++++++++++++
|
|
||||||
src/pylorax/__init__.py | 6 ++++--
|
|
||||||
src/pylorax/cmdline.py | 2 ++
|
|
||||||
src/pylorax/treebuilder.py | 13 ++++++++++++-
|
|
||||||
src/sbin/lorax | 3 ++-
|
|
||||||
5 files changed, 43 insertions(+), 4 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/docs/lorax.rst b/docs/lorax.rst
|
|
||||||
index 65c866bc..f1bed0ad 100644
|
|
||||||
--- a/docs/lorax.rst
|
|
||||||
+++ b/docs/lorax.rst
|
|
||||||
@@ -54,6 +54,29 @@ Under ``./results/`` will be the release tree files: .discinfo, .treeinfo, every
|
|
||||||
goes onto the boot.iso, the pxeboot directory, and the boot.iso under ``./images/``.
|
|
||||||
|
|
||||||
|
|
||||||
+Branding
|
|
||||||
+--------
|
|
||||||
+
|
|
||||||
+By default lorax will search for the first package that provides ``system-release``
|
|
||||||
+that doesn't start with ``generic-`` and will install it. It then selects a
|
|
||||||
+corresponding logo package by using the first part of the system-release package and
|
|
||||||
+appending ``-logos`` to it. eg. fedora-release and fedora-logos.
|
|
||||||
+
|
|
||||||
+Custom Branding
|
|
||||||
+~~~~~~~~~~~~~~~
|
|
||||||
+
|
|
||||||
+If ``--skip-branding`` is passed to lorax it will skip selecting the
|
|
||||||
+``system-release``, and logos packages and leave it up to the user to pass any
|
|
||||||
+branding related packages to lorax using ``--installpkgs``. When using
|
|
||||||
+``skip-branding`` you must make sure that you provide all of the expected files,
|
|
||||||
+otherwise Anaconda may not work as expected. See the contents of ``fedora-release``
|
|
||||||
+and ``fedora-logos`` for examples of what to include.
|
|
||||||
+
|
|
||||||
+Note that this does not prevent something else in the dependency tree from
|
|
||||||
+causing these packages to be included. Using ``--excludepkgs`` may help if they
|
|
||||||
+are unexpectedly included.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
Running inside of mock
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
diff --git a/src/pylorax/__init__.py b/src/pylorax/__init__.py
|
|
||||||
index acc4e5f4..2ccfb950 100644
|
|
||||||
--- a/src/pylorax/__init__.py
|
|
||||||
+++ b/src/pylorax/__init__.py
|
|
||||||
@@ -186,7 +186,8 @@ class Lorax(BaseLoraxClass):
|
|
||||||
add_arch_templates=None,
|
|
||||||
add_arch_template_vars=None,
|
|
||||||
verify=True,
|
|
||||||
- user_dracut_args=None):
|
|
||||||
+ user_dracut_args=None,
|
|
||||||
+ skip_branding=False):
|
|
||||||
|
|
||||||
assert self._configured
|
|
||||||
|
|
||||||
@@ -266,7 +267,8 @@ class Lorax(BaseLoraxClass):
|
|
||||||
installpkgs=installpkgs,
|
|
||||||
excludepkgs=excludepkgs,
|
|
||||||
add_templates=add_templates,
|
|
||||||
- add_template_vars=add_template_vars)
|
|
||||||
+ add_template_vars=add_template_vars,
|
|
||||||
+ skip_branding=skip_branding)
|
|
||||||
|
|
||||||
logger.info("installing runtime packages")
|
|
||||||
rb.install()
|
|
||||||
diff --git a/src/pylorax/cmdline.py b/src/pylorax/cmdline.py
|
|
||||||
index 3af1956a..869fc150 100644
|
|
||||||
--- a/src/pylorax/cmdline.py
|
|
||||||
+++ b/src/pylorax/cmdline.py
|
|
||||||
@@ -107,6 +107,8 @@ def lorax_parser(dracut_default=""):
|
|
||||||
help="Size of root filesystem in GiB. Defaults to 3.")
|
|
||||||
optional.add_argument("--noverifyssl", action="store_true", default=False,
|
|
||||||
help="Do not verify SSL certificates")
|
|
||||||
+ optional.add_argument("--skip-branding", action="store_true", default=False,
|
|
||||||
+ help="Disable automatic branding package selection. Use --installpkgs to add custom branding.")
|
|
||||||
|
|
||||||
# dracut arguments
|
|
||||||
dracut_group = parser.add_argument_group("dracut arguments")
|
|
||||||
diff --git a/src/pylorax/treebuilder.py b/src/pylorax/treebuilder.py
|
|
||||||
index 1055b6a2..a93018b2 100644
|
|
||||||
--- a/src/pylorax/treebuilder.py
|
|
||||||
+++ b/src/pylorax/treebuilder.py
|
|
||||||
@@ -72,7 +72,8 @@ class RuntimeBuilder(object):
|
|
||||||
def __init__(self, product, arch, dbo, templatedir=None,
|
|
||||||
installpkgs=None, excludepkgs=None,
|
|
||||||
add_templates=None,
|
|
||||||
- add_template_vars=None):
|
|
||||||
+ add_template_vars=None,
|
|
||||||
+ skip_branding=False):
|
|
||||||
root = dbo.conf.installroot
|
|
||||||
# use a copy of product so we can modify it locally
|
|
||||||
product = product.copy()
|
|
||||||
@@ -88,8 +89,18 @@ class RuntimeBuilder(object):
|
|
||||||
self._excludepkgs = excludepkgs or []
|
|
||||||
self._runner.defaults = self.vars
|
|
||||||
self.dbo.reset()
|
|
||||||
+ self._skip_branding = skip_branding
|
|
||||||
|
|
||||||
def _install_branding(self):
|
|
||||||
+ """Select the branding from the available 'system-release' packages
|
|
||||||
+ The *best* way to control this is to have a single package in the repo provide 'system-release'
|
|
||||||
+ When there are more than 1 package it will:
|
|
||||||
+ - Make a list of the available packages
|
|
||||||
+ - If there are one or more non-generic packages, use the first one after sorting
|
|
||||||
+ """
|
|
||||||
+ if self._skip_branding:
|
|
||||||
+ return
|
|
||||||
+
|
|
||||||
release = None
|
|
||||||
q = self.dbo.sack.query()
|
|
||||||
a = q.available()
|
|
||||||
diff --git a/src/sbin/lorax b/src/sbin/lorax
|
|
||||||
index 8ba77c80..453f2d3e 100755
|
|
||||||
--- a/src/sbin/lorax
|
|
||||||
+++ b/src/sbin/lorax
|
|
||||||
@@ -192,7 +192,8 @@ def main():
|
|
||||||
add_arch_templates=opts.add_arch_templates,
|
|
||||||
add_arch_template_vars=parsed_add_arch_template_vars,
|
|
||||||
remove_temp=True, verify=opts.verify,
|
|
||||||
- user_dracut_args=opts.dracut_args)
|
|
||||||
+ user_dracut_args=opts.dracut_args,
|
|
||||||
+ skip_branding=opts.skip_branding)
|
|
||||||
|
|
||||||
# Release the lock on the tempdir
|
|
||||||
os.close(dir_fd)
|
|
||||||
--
|
|
||||||
2.25.4
|
|
||||||
|
|
@ -1,371 +0,0 @@
|
|||||||
From 808f01eedefeb2eca2521ec372f2fa680f71cb86 Mon Sep 17 00:00:00 2001
|
|
||||||
From: "Brian C. Lane" <bcl@redhat.com>
|
|
||||||
Date: Wed, 22 Apr 2020 10:00:07 -0700
|
|
||||||
Subject: [PATCH 2/3] Move get_dnf_base_object into a module
|
|
||||||
|
|
||||||
This allows it to be imported by tests.
|
|
||||||
|
|
||||||
Related: rhbz#1826479
|
|
||||||
---
|
|
||||||
src/pylorax/dnfbase.py | 176 +++++++++++++++++++++++++++++++++++++++++
|
|
||||||
src/sbin/lorax | 154 +-----------------------------------
|
|
||||||
2 files changed, 178 insertions(+), 152 deletions(-)
|
|
||||||
create mode 100644 src/pylorax/dnfbase.py
|
|
||||||
|
|
||||||
diff --git a/src/pylorax/dnfbase.py b/src/pylorax/dnfbase.py
|
|
||||||
new file mode 100644
|
|
||||||
index 00000000..ccdaf7ec
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/src/pylorax/dnfbase.py
|
|
||||||
@@ -0,0 +1,176 @@
|
|
||||||
+#
|
|
||||||
+# Copyright (C) 2020 Red Hat, Inc.
|
|
||||||
+#
|
|
||||||
+# This program is free software; you can redistribute it and/or modify
|
|
||||||
+# it under the terms of the GNU General Public License as published by
|
|
||||||
+# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
+# (at your option) any later version.
|
|
||||||
+#
|
|
||||||
+# 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, see <http://www.gnu.org/licenses/>.
|
|
||||||
+#
|
|
||||||
+import logging
|
|
||||||
+log = logging.getLogger("lorax")
|
|
||||||
+
|
|
||||||
+import dnf
|
|
||||||
+import os
|
|
||||||
+import shutil
|
|
||||||
+
|
|
||||||
+from pylorax import DEFAULT_PLATFORM_ID
|
|
||||||
+from pylorax.sysutils import flatconfig
|
|
||||||
+
|
|
||||||
+def get_dnf_base_object(installroot, sources, mirrorlists=None, repos=None,
|
|
||||||
+ enablerepos=None, disablerepos=None,
|
|
||||||
+ tempdir="/var/tmp", proxy=None, releasever="8",
|
|
||||||
+ cachedir=None, logdir=None, sslverify=True):
|
|
||||||
+ """ Create a dnf Base object and setup the repositories and installroot
|
|
||||||
+
|
|
||||||
+ :param string installroot: Full path to the installroot
|
|
||||||
+ :param list sources: List of source repo urls to use for the installation
|
|
||||||
+ :param list enablerepos: List of repo names to enable
|
|
||||||
+ :param list disablerepos: List of repo names to disable
|
|
||||||
+ :param list mirrorlist: List of mirrors to use
|
|
||||||
+ :param string tempdir: Path of temporary directory
|
|
||||||
+ :param string proxy: http proxy to use when fetching packages
|
|
||||||
+ :param string releasever: Release version to pass to dnf
|
|
||||||
+ :param string cachedir: Directory to use for caching packages
|
|
||||||
+ :param bool noverifyssl: Set to True to ignore the CA of ssl certs. eg. use self-signed ssl for https repos.
|
|
||||||
+
|
|
||||||
+ If tempdir is not set /var/tmp is used.
|
|
||||||
+ If cachedir is None a dnf.cache directory is created inside tmpdir
|
|
||||||
+ """
|
|
||||||
+ def sanitize_repo(repo):
|
|
||||||
+ """Convert bare paths to file:/// URIs, and silently reject protocols unhandled by yum"""
|
|
||||||
+ if repo.startswith("/"):
|
|
||||||
+ return "file://{0}".format(repo)
|
|
||||||
+ elif any(repo.startswith(p) for p in ('http://', 'https://', 'ftp://', 'file://')):
|
|
||||||
+ return repo
|
|
||||||
+ else:
|
|
||||||
+ return None
|
|
||||||
+
|
|
||||||
+ mirrorlists = mirrorlists or []
|
|
||||||
+
|
|
||||||
+ # sanitize the repositories
|
|
||||||
+ sources = list(sanitize_repo(r) for r in sources)
|
|
||||||
+ mirrorlists = list(sanitize_repo(r) for r in mirrorlists)
|
|
||||||
+
|
|
||||||
+ # remove invalid repositories
|
|
||||||
+ sources = list(r for r in sources if r)
|
|
||||||
+ mirrorlists = list(r for r in mirrorlists if r)
|
|
||||||
+
|
|
||||||
+ if not cachedir:
|
|
||||||
+ cachedir = os.path.join(tempdir, "dnf.cache")
|
|
||||||
+ if not os.path.isdir(cachedir):
|
|
||||||
+ os.mkdir(cachedir)
|
|
||||||
+
|
|
||||||
+ if not logdir:
|
|
||||||
+ logdir = os.path.join(tempdir, "dnf.logs")
|
|
||||||
+ if not os.path.isdir(logdir):
|
|
||||||
+ os.mkdir(logdir)
|
|
||||||
+
|
|
||||||
+ dnfbase = dnf.Base()
|
|
||||||
+ conf = dnfbase.conf
|
|
||||||
+ conf.logdir = logdir
|
|
||||||
+ conf.cachedir = cachedir
|
|
||||||
+
|
|
||||||
+ conf.install_weak_deps = False
|
|
||||||
+ conf.releasever = releasever
|
|
||||||
+ conf.installroot = installroot
|
|
||||||
+ conf.prepend_installroot('persistdir')
|
|
||||||
+ conf.tsflags.append('nodocs')
|
|
||||||
+
|
|
||||||
+ if proxy:
|
|
||||||
+ conf.proxy = proxy
|
|
||||||
+
|
|
||||||
+ if sslverify == False:
|
|
||||||
+ conf.sslverify = False
|
|
||||||
+
|
|
||||||
+ # DNF 3.2 needs to have module_platform_id set, otherwise depsolve won't work correctly
|
|
||||||
+ if not os.path.exists("/etc/os-release"):
|
|
||||||
+ log.warning("/etc/os-release is missing, cannot determine platform id, falling back to %s", DEFAULT_PLATFORM_ID)
|
|
||||||
+ platform_id = DEFAULT_PLATFORM_ID
|
|
||||||
+ else:
|
|
||||||
+ os_release = flatconfig("/etc/os-release")
|
|
||||||
+ platform_id = os_release.get("PLATFORM_ID", DEFAULT_PLATFORM_ID)
|
|
||||||
+ log.info("Using %s for module_platform_id", platform_id)
|
|
||||||
+ conf.module_platform_id = platform_id
|
|
||||||
+
|
|
||||||
+ # Add .repo files
|
|
||||||
+ if repos:
|
|
||||||
+ reposdir = os.path.join(tempdir, "dnf.repos")
|
|
||||||
+ if not os.path.isdir(reposdir):
|
|
||||||
+ os.mkdir(reposdir)
|
|
||||||
+ for r in repos:
|
|
||||||
+ shutil.copy2(r, reposdir)
|
|
||||||
+ conf.reposdir = [reposdir]
|
|
||||||
+ dnfbase.read_all_repos()
|
|
||||||
+
|
|
||||||
+ # add the sources
|
|
||||||
+ for i, r in enumerate(sources):
|
|
||||||
+ if "SRPM" in r or "srpm" in r:
|
|
||||||
+ log.info("Skipping source repo: %s", r)
|
|
||||||
+ continue
|
|
||||||
+ repo_name = "lorax-repo-%d" % i
|
|
||||||
+ repo = dnf.repo.Repo(repo_name, conf)
|
|
||||||
+ repo.baseurl = [r]
|
|
||||||
+ if proxy:
|
|
||||||
+ repo.proxy = proxy
|
|
||||||
+ repo.enable()
|
|
||||||
+ dnfbase.repos.add(repo)
|
|
||||||
+ log.info("Added '%s': %s", repo_name, r)
|
|
||||||
+ log.info("Fetching metadata...")
|
|
||||||
+ try:
|
|
||||||
+ repo.load()
|
|
||||||
+ except dnf.exceptions.RepoError as e:
|
|
||||||
+ log.error("Error fetching metadata for %s: %s", repo_name, e)
|
|
||||||
+ return None
|
|
||||||
+
|
|
||||||
+ # add the mirrorlists
|
|
||||||
+ for i, r in enumerate(mirrorlists):
|
|
||||||
+ if "SRPM" in r or "srpm" in r:
|
|
||||||
+ log.info("Skipping source repo: %s", r)
|
|
||||||
+ continue
|
|
||||||
+ repo_name = "lorax-mirrorlist-%d" % i
|
|
||||||
+ repo = dnf.repo.Repo(repo_name, conf)
|
|
||||||
+ repo.mirrorlist = r
|
|
||||||
+ if proxy:
|
|
||||||
+ repo.proxy = proxy
|
|
||||||
+ repo.enable()
|
|
||||||
+ dnfbase.repos.add(repo)
|
|
||||||
+ log.info("Added '%s': %s", repo_name, r)
|
|
||||||
+ log.info("Fetching metadata...")
|
|
||||||
+ try:
|
|
||||||
+ repo.load()
|
|
||||||
+ except dnf.exceptions.RepoError as e:
|
|
||||||
+ log.error("Error fetching metadata for %s: %s", repo_name, e)
|
|
||||||
+ return None
|
|
||||||
+
|
|
||||||
+ # Enable repos listed on the cmdline
|
|
||||||
+ for r in enablerepos:
|
|
||||||
+ repolist = dnfbase.repos.get_matching(r)
|
|
||||||
+ if not repolist:
|
|
||||||
+ log.warning("%s is an unknown repo, not enabling it", r)
|
|
||||||
+ else:
|
|
||||||
+ repolist.enable()
|
|
||||||
+ log.info("Enabled repo %s", r)
|
|
||||||
+
|
|
||||||
+ # Disable repos listed on the cmdline
|
|
||||||
+ for r in disablerepos:
|
|
||||||
+ repolist = dnfbase.repos.get_matching(r)
|
|
||||||
+ if not repolist:
|
|
||||||
+ log.warning("%s is an unknown repo, not disabling it", r)
|
|
||||||
+ else:
|
|
||||||
+ repolist.disable()
|
|
||||||
+ log.info("Disabled repo %s", r)
|
|
||||||
+
|
|
||||||
+ dnfbase.fill_sack(load_system_repo=False)
|
|
||||||
+ dnfbase.read_comps()
|
|
||||||
+
|
|
||||||
+ return dnfbase
|
|
||||||
+
|
|
||||||
+
|
|
||||||
diff --git a/src/sbin/lorax b/src/sbin/lorax
|
|
||||||
index 453f2d3e..3ae174fd 100755
|
|
||||||
--- a/src/sbin/lorax
|
|
||||||
+++ b/src/sbin/lorax
|
|
||||||
@@ -36,9 +36,9 @@ import dnf
|
|
||||||
import dnf.logging
|
|
||||||
import librepo
|
|
||||||
import pylorax
|
|
||||||
-from pylorax import DRACUT_DEFAULT, DEFAULT_PLATFORM_ID, log_selinux_state
|
|
||||||
+from pylorax import DRACUT_DEFAULT, log_selinux_state
|
|
||||||
from pylorax.cmdline import lorax_parser
|
|
||||||
-from pylorax.sysutils import flatconfig
|
|
||||||
+from pylorax.dnfbase import get_dnf_base_object
|
|
||||||
|
|
||||||
def exit_handler(tempdir):
|
|
||||||
"""Handle cleanup of tmpdir, if it still exists
|
|
||||||
@@ -198,155 +198,5 @@ def main():
|
|
||||||
# Release the lock on the tempdir
|
|
||||||
os.close(dir_fd)
|
|
||||||
|
|
||||||
-def get_dnf_base_object(installroot, sources, mirrorlists=None, repos=None,
|
|
||||||
- enablerepos=None, disablerepos=None,
|
|
||||||
- tempdir="/var/tmp", proxy=None, releasever="8",
|
|
||||||
- cachedir=None, logdir=None, sslverify=True):
|
|
||||||
- """ Create a dnf Base object and setup the repositories and installroot
|
|
||||||
-
|
|
||||||
- :param string installroot: Full path to the installroot
|
|
||||||
- :param list sources: List of source repo urls to use for the installation
|
|
||||||
- :param list enablerepos: List of repo names to enable
|
|
||||||
- :param list disablerepos: List of repo names to disable
|
|
||||||
- :param list mirrorlist: List of mirrors to use
|
|
||||||
- :param string tempdir: Path of temporary directory
|
|
||||||
- :param string proxy: http proxy to use when fetching packages
|
|
||||||
- :param string releasever: Release version to pass to dnf
|
|
||||||
- :param string cachedir: Directory to use for caching packages
|
|
||||||
- :param bool noverifyssl: Set to True to ignore the CA of ssl certs. eg. use self-signed ssl for https repos.
|
|
||||||
-
|
|
||||||
- If tempdir is not set /var/tmp is used.
|
|
||||||
- If cachedir is None a dnf.cache directory is created inside tmpdir
|
|
||||||
- """
|
|
||||||
- def sanitize_repo(repo):
|
|
||||||
- """Convert bare paths to file:/// URIs, and silently reject protocols unhandled by yum"""
|
|
||||||
- if repo.startswith("/"):
|
|
||||||
- return "file://{0}".format(repo)
|
|
||||||
- elif any(repo.startswith(p) for p in ('http://', 'https://', 'ftp://', 'file://')):
|
|
||||||
- return repo
|
|
||||||
- else:
|
|
||||||
- return None
|
|
||||||
-
|
|
||||||
- mirrorlists = mirrorlists or []
|
|
||||||
-
|
|
||||||
- # sanitize the repositories
|
|
||||||
- sources = list(sanitize_repo(r) for r in sources)
|
|
||||||
- mirrorlists = list(sanitize_repo(r) for r in mirrorlists)
|
|
||||||
-
|
|
||||||
- # remove invalid repositories
|
|
||||||
- sources = list(r for r in sources if r)
|
|
||||||
- mirrorlists = list(r for r in mirrorlists if r)
|
|
||||||
-
|
|
||||||
- if not cachedir:
|
|
||||||
- cachedir = os.path.join(tempdir, "dnf.cache")
|
|
||||||
- if not os.path.isdir(cachedir):
|
|
||||||
- os.mkdir(cachedir)
|
|
||||||
-
|
|
||||||
- if not logdir:
|
|
||||||
- logdir = os.path.join(tempdir, "dnf.logs")
|
|
||||||
- if not os.path.isdir(logdir):
|
|
||||||
- os.mkdir(logdir)
|
|
||||||
-
|
|
||||||
- dnfbase = dnf.Base()
|
|
||||||
- conf = dnfbase.conf
|
|
||||||
- conf.logdir = logdir
|
|
||||||
- conf.cachedir = cachedir
|
|
||||||
-
|
|
||||||
- conf.install_weak_deps = False
|
|
||||||
- conf.releasever = releasever
|
|
||||||
- conf.installroot = installroot
|
|
||||||
- conf.prepend_installroot('persistdir')
|
|
||||||
- conf.tsflags.append('nodocs')
|
|
||||||
-
|
|
||||||
- if proxy:
|
|
||||||
- conf.proxy = proxy
|
|
||||||
-
|
|
||||||
- if sslverify == False:
|
|
||||||
- conf.sslverify = False
|
|
||||||
-
|
|
||||||
- # DNF 3.2 needs to have module_platform_id set, otherwise depsolve won't work correctly
|
|
||||||
- if not os.path.exists("/etc/os-release"):
|
|
||||||
- log.warning("/etc/os-release is missing, cannot determine platform id, falling back to %s", DEFAULT_PLATFORM_ID)
|
|
||||||
- platform_id = DEFAULT_PLATFORM_ID
|
|
||||||
- else:
|
|
||||||
- os_release = flatconfig("/etc/os-release")
|
|
||||||
- platform_id = os_release.get("PLATFORM_ID", DEFAULT_PLATFORM_ID)
|
|
||||||
- log.info("Using %s for module_platform_id", platform_id)
|
|
||||||
- conf.module_platform_id = platform_id
|
|
||||||
-
|
|
||||||
- # Add .repo files
|
|
||||||
- if repos:
|
|
||||||
- reposdir = os.path.join(tempdir, "dnf.repos")
|
|
||||||
- if not os.path.isdir(reposdir):
|
|
||||||
- os.mkdir(reposdir)
|
|
||||||
- for r in repos:
|
|
||||||
- shutil.copy2(r, reposdir)
|
|
||||||
- conf.reposdir = [reposdir]
|
|
||||||
- dnfbase.read_all_repos()
|
|
||||||
-
|
|
||||||
- # add the sources
|
|
||||||
- for i, r in enumerate(sources):
|
|
||||||
- if "SRPM" in r or "srpm" in r:
|
|
||||||
- log.info("Skipping source repo: %s", r)
|
|
||||||
- continue
|
|
||||||
- repo_name = "lorax-repo-%d" % i
|
|
||||||
- repo = dnf.repo.Repo(repo_name, conf)
|
|
||||||
- repo.baseurl = [r]
|
|
||||||
- if proxy:
|
|
||||||
- repo.proxy = proxy
|
|
||||||
- repo.enable()
|
|
||||||
- dnfbase.repos.add(repo)
|
|
||||||
- log.info("Added '%s': %s", repo_name, r)
|
|
||||||
- log.info("Fetching metadata...")
|
|
||||||
- try:
|
|
||||||
- repo.load()
|
|
||||||
- except dnf.exceptions.RepoError as e:
|
|
||||||
- log.error("Error fetching metadata for %s: %s", repo_name, e)
|
|
||||||
- return None
|
|
||||||
-
|
|
||||||
- # add the mirrorlists
|
|
||||||
- for i, r in enumerate(mirrorlists):
|
|
||||||
- if "SRPM" in r or "srpm" in r:
|
|
||||||
- log.info("Skipping source repo: %s", r)
|
|
||||||
- continue
|
|
||||||
- repo_name = "lorax-mirrorlist-%d" % i
|
|
||||||
- repo = dnf.repo.Repo(repo_name, conf)
|
|
||||||
- repo.mirrorlist = r
|
|
||||||
- if proxy:
|
|
||||||
- repo.proxy = proxy
|
|
||||||
- repo.enable()
|
|
||||||
- dnfbase.repos.add(repo)
|
|
||||||
- log.info("Added '%s': %s", repo_name, r)
|
|
||||||
- log.info("Fetching metadata...")
|
|
||||||
- try:
|
|
||||||
- repo.load()
|
|
||||||
- except dnf.exceptions.RepoError as e:
|
|
||||||
- log.error("Error fetching metadata for %s: %s", repo_name, e)
|
|
||||||
- return None
|
|
||||||
-
|
|
||||||
- # Enable repos listed on the cmdline
|
|
||||||
- for r in enablerepos:
|
|
||||||
- repolist = dnfbase.repos.get_matching(r)
|
|
||||||
- if not repolist:
|
|
||||||
- log.warning("%s is an unknown repo, not enabling it", r)
|
|
||||||
- else:
|
|
||||||
- repolist.enable()
|
|
||||||
- log.info("Enabled repo %s", r)
|
|
||||||
-
|
|
||||||
- # Disable repos listed on the cmdline
|
|
||||||
- for r in disablerepos:
|
|
||||||
- repolist = dnfbase.repos.get_matching(r)
|
|
||||||
- if not repolist:
|
|
||||||
- log.warning("%s is an unknown repo, not disabling it", r)
|
|
||||||
- else:
|
|
||||||
- repolist.disable()
|
|
||||||
- log.info("Disabled repo %s", r)
|
|
||||||
-
|
|
||||||
- dnfbase.fill_sack(load_system_repo=False)
|
|
||||||
- dnfbase.read_comps()
|
|
||||||
-
|
|
||||||
- return dnfbase
|
|
||||||
-
|
|
||||||
-
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
--
|
|
||||||
2.25.4
|
|
||||||
|
|
@ -1,141 +0,0 @@
|
|||||||
From c85cc83e1a574e31b41ab06221eb3b6fae6c6e4c Mon Sep 17 00:00:00 2001
|
|
||||||
From: "Brian C. Lane" <bcl@redhat.com>
|
|
||||||
Date: Wed, 22 Apr 2020 10:02:41 -0700
|
|
||||||
Subject: [PATCH 3/3] tests: Add tests for branding
|
|
||||||
|
|
||||||
Related: rhbz#1826479
|
|
||||||
---
|
|
||||||
tests/pylorax/test_treebuilder.py | 121 ++++++++++++++++++++++++++++++
|
|
||||||
1 file changed, 121 insertions(+)
|
|
||||||
create mode 100644 tests/pylorax/test_treebuilder.py
|
|
||||||
|
|
||||||
diff --git a/tests/pylorax/test_treebuilder.py b/tests/pylorax/test_treebuilder.py
|
|
||||||
new file mode 100644
|
|
||||||
index 00000000..0660ec1f
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/tests/pylorax/test_treebuilder.py
|
|
||||||
@@ -0,0 +1,121 @@
|
|
||||||
+#
|
|
||||||
+# Copyright (C) 2020 Red Hat, Inc.
|
|
||||||
+#
|
|
||||||
+# This program is free software; you can redistribute it and/or modify
|
|
||||||
+# it under the terms of the GNU General Public License as published by
|
|
||||||
+# the Free Software Foundation; either version 2 of the License, or
|
|
||||||
+# (at your option) any later version.
|
|
||||||
+#
|
|
||||||
+# 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, see <http://www.gnu.org/licenses/>.
|
|
||||||
+#
|
|
||||||
+from contextlib import contextmanager
|
|
||||||
+import os
|
|
||||||
+from rpmfluff import SimpleRpmBuild, SourceFile, expectedArch
|
|
||||||
+import shutil
|
|
||||||
+import tempfile
|
|
||||||
+import unittest
|
|
||||||
+
|
|
||||||
+from pylorax import ArchData, DataHolder
|
|
||||||
+from pylorax.dnfbase import get_dnf_base_object
|
|
||||||
+from pylorax.treebuilder import RuntimeBuilder
|
|
||||||
+
|
|
||||||
+# TODO Put these into a common test library location
|
|
||||||
+@contextmanager
|
|
||||||
+def in_tempdir(prefix='tmp'):
|
|
||||||
+ """Execute a block of code with chdir in a temporary location"""
|
|
||||||
+ oldcwd = os.getcwd()
|
|
||||||
+ tmpdir = tempfile.mkdtemp(prefix=prefix)
|
|
||||||
+ os.chdir(tmpdir)
|
|
||||||
+ try:
|
|
||||||
+ yield
|
|
||||||
+ finally:
|
|
||||||
+ os.chdir(oldcwd)
|
|
||||||
+ shutil.rmtree(tmpdir)
|
|
||||||
+
|
|
||||||
+def makeFakeRPM(repo_dir, name, epoch, version, release, files=None, provides=None):
|
|
||||||
+ """Make a fake rpm file in repo_dir"""
|
|
||||||
+ if provides is None:
|
|
||||||
+ provides = []
|
|
||||||
+ p = SimpleRpmBuild(name, version, release)
|
|
||||||
+ if epoch:
|
|
||||||
+ p.epoch = epoch
|
|
||||||
+ if not files:
|
|
||||||
+ p.add_simple_payload_file_random()
|
|
||||||
+ else:
|
|
||||||
+ # Make a number of fake file entries in the rpm
|
|
||||||
+ for f in files:
|
|
||||||
+ p.add_installed_file(
|
|
||||||
+ installPath = f,
|
|
||||||
+ sourceFile = SourceFile(os.path.basename(f), "THIS IS A FAKE FILE"))
|
|
||||||
+ for c in provides:
|
|
||||||
+ p.add_provides(c)
|
|
||||||
+ with in_tempdir("lorax-test-rpms."):
|
|
||||||
+ p.make()
|
|
||||||
+ rpmfile = p.get_built_rpm(expectedArch)
|
|
||||||
+ shutil.move(rpmfile, repo_dir)
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+class InstallBrandingTestCase(unittest.TestCase):
|
|
||||||
+ def install_branding(self, repo_dir, variant=None, skip_branding=False):
|
|
||||||
+ """Run the _install_branding and return the names of the installed packages"""
|
|
||||||
+ with tempfile.TemporaryDirectory(prefix="lorax.test.") as root_dir:
|
|
||||||
+ dbo = get_dnf_base_object(root_dir, ["file://"+repo_dir], enablerepos=[], disablerepos=[])
|
|
||||||
+ self.assertTrue(dbo is not None)
|
|
||||||
+
|
|
||||||
+ product = DataHolder(name="Fedora", version="33", release="33",
|
|
||||||
+ variant=variant, bugurl="http://none", isfinal=True)
|
|
||||||
+ arch = ArchData(os.uname().machine)
|
|
||||||
+ rb = RuntimeBuilder(product, arch, dbo, skip_branding=skip_branding)
|
|
||||||
+ rb._install_branding()
|
|
||||||
+ dbo.resolve()
|
|
||||||
+ self.assertTrue(dbo.transaction is not None)
|
|
||||||
+
|
|
||||||
+ return sorted(p.name for p in dbo.transaction.install_set)
|
|
||||||
+
|
|
||||||
+ def test_no_pkgs(self):
|
|
||||||
+ """Test with a repo with no system-release packages"""
|
|
||||||
+ # No system-release packages
|
|
||||||
+ with tempfile.TemporaryDirectory(prefix="lorax.test.repo.") as repo_dir:
|
|
||||||
+ makeFakeRPM(repo_dir, "fake-milhouse", 0, "1.0.0", "1")
|
|
||||||
+ os.system("createrepo_c " + repo_dir)
|
|
||||||
+
|
|
||||||
+ pkgs = self.install_branding(repo_dir)
|
|
||||||
+ self.assertEqual(pkgs, [])
|
|
||||||
+
|
|
||||||
+ def test_generic_pkg(self):
|
|
||||||
+ """Test with a repo with only a generic-release package"""
|
|
||||||
+ # Only generic-release
|
|
||||||
+ with tempfile.TemporaryDirectory(prefix="lorax.test.repo.") as repo_dir:
|
|
||||||
+ makeFakeRPM(repo_dir, "generic-release", 0, "33", "1", ["/etc/system-release"], ["system-release"])
|
|
||||||
+ os.system("createrepo_c " + repo_dir)
|
|
||||||
+
|
|
||||||
+ pkgs = self.install_branding(repo_dir)
|
|
||||||
+ self.assertEqual(pkgs, [])
|
|
||||||
+
|
|
||||||
+ def test_two_pkgs(self):
|
|
||||||
+ """Test with a repo with generic-release, and a redhat-release package"""
|
|
||||||
+ # Two system-release packages
|
|
||||||
+ with tempfile.TemporaryDirectory(prefix="lorax.test.repo.") as repo_dir:
|
|
||||||
+ makeFakeRPM(repo_dir, "generic-release", 0, "33", "1", ["/etc/system-release"], ["system-release"])
|
|
||||||
+ makeFakeRPM(repo_dir, "redhat-release", 0, "33", "1", ["/etc/system-release"], ["system-release"])
|
|
||||||
+ makeFakeRPM(repo_dir, "redhat-logos", 0, "33", "1")
|
|
||||||
+ os.system("createrepo_c " + repo_dir)
|
|
||||||
+
|
|
||||||
+ pkgs = self.install_branding(repo_dir)
|
|
||||||
+ self.assertEqual(pkgs, ["redhat-logos", "redhat-release"])
|
|
||||||
+
|
|
||||||
+ def test_skip_branding(self):
|
|
||||||
+ """Test disabled branding"""
|
|
||||||
+ with tempfile.TemporaryDirectory(prefix="lorax.test.repo.") as repo_dir:
|
|
||||||
+ makeFakeRPM(repo_dir, "redhat-release", 0, "33", "1", ["/etc/system-release"], ["system-release"])
|
|
||||||
+ makeFakeRPM(repo_dir, "redhat-logos", 0, "33", "1")
|
|
||||||
+ os.system("createrepo_c " + repo_dir)
|
|
||||||
+
|
|
||||||
+ pkgs = self.install_branding(repo_dir, skip_branding=True)
|
|
||||||
+ self.assertEqual(pkgs, [])
|
|
||||||
--
|
|
||||||
2.25.4
|
|
||||||
|
|
139
SPECS/lorax.spec
139
SPECS/lorax.spec
@ -3,7 +3,7 @@
|
|||||||
%define debug_package %{nil}
|
%define debug_package %{nil}
|
||||||
|
|
||||||
Name: lorax
|
Name: lorax
|
||||||
Version: 28.14.42
|
Version: 28.14.55
|
||||||
Release: 2%{?dist}
|
Release: 2%{?dist}
|
||||||
Summary: Tool for creating the anaconda install images
|
Summary: Tool for creating the anaconda install images
|
||||||
|
|
||||||
@ -16,12 +16,6 @@ URL: https://github.com/weldr/lorax
|
|||||||
# tito build --tgz
|
# tito build --tgz
|
||||||
Source0: %{name}-%{version}.tar.gz
|
Source0: %{name}-%{version}.tar.gz
|
||||||
|
|
||||||
# Patches for rhbz#1826479
|
|
||||||
Patch0001: 0001-lorax-Add-skip-branding-cmdline-argument.patch
|
|
||||||
Patch0002: 0002-Move-get_dnf_base_object-into-a-module.patch
|
|
||||||
Patch0003: 0003-tests-Add-tests-for-branding.patch
|
|
||||||
|
|
||||||
|
|
||||||
BuildRequires: python3-devel
|
BuildRequires: python3-devel
|
||||||
|
|
||||||
Requires: lorax-templates
|
Requires: lorax-templates
|
||||||
@ -120,6 +114,7 @@ Summary: livemedia-creator no-virt dependencies
|
|||||||
Requires: lorax = %{version}-%{release}
|
Requires: lorax = %{version}-%{release}
|
||||||
Requires: anaconda-core
|
Requires: anaconda-core
|
||||||
Requires: anaconda-tui
|
Requires: anaconda-tui
|
||||||
|
Requires: anaconda-install-env-deps
|
||||||
Requires: system-logos
|
Requires: system-logos
|
||||||
|
|
||||||
%description lmc-novirt
|
%description lmc-novirt
|
||||||
@ -258,12 +253,132 @@ getent passwd weldr >/dev/null 2>&1 || useradd -r -g weldr -d / -s /sbin/nologin
|
|||||||
%{_sysconfdir}/bash_completion.d/composer-cli
|
%{_sysconfdir}/bash_completion.d/composer-cli
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
* Mon Apr 27 2020 Brian C. Lane <bcl@redhat.com> - 28.14.42-2
|
* Thu Sep 10 2020 Brian C. Lane <bcl@redhat.com> - 28.14.55-2
|
||||||
- tests: Add tests for branding (bcl)
|
- tests: Add rhel8 beta repository for osbuild-composer setup
|
||||||
- Move get_dnf_base_object into a module (bcl)
|
Related: rhbz#1876563
|
||||||
- lorax: Add --skip-branding cmdline argument (bcl)
|
|
||||||
|
* Thu Sep 10 2020 Brian C. Lane <bcl@redhat.com> 28.14.55-1
|
||||||
|
- docs: Remove anaconda from rhel-livemedia.ks example (bcl)
|
||||||
|
Resolves: rhbz#1876563
|
||||||
|
|
||||||
|
* Mon Aug 10 2020 Brian C. Lane <bcl@redhat.com> 28.14.54-1
|
||||||
|
- tests: Fix diff test BACKEND check (bcl)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
|
||||||
|
* Fri Aug 07 2020 Brian C. Lane <bcl@redhat.com> 28.14.53-1
|
||||||
|
- tests: Push example blueprints b/c they may not exist (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- rhel-livemedia.ks: Add anaconda-live package to the example kickstart (bcl)
|
||||||
|
Related: rhbz#1691319
|
||||||
|
- Switch VMware testing env to improve stability results (chrobert)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
|
||||||
|
* Mon Aug 03 2020 Brian C. Lane <bcl@redhat.com> 28.14.52-1
|
||||||
|
- Skip test_z_diff() if runninga against for osbuild-composer (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
|
||||||
|
* Tue Jul 28 2020 Brian C. Lane <bcl@redhat.com> 28.14.51-1
|
||||||
|
- tests: Overwrite rhel-8.json when testing with osbuild-composer (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: Allow skipping image build in compose sanity test (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- Set BACKEND=osbuild-composer if running that test scenario (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: Don't check info after compose cancel with osbuild-composer (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: Compare blueprints as TOML objects, not strings (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: Remove lorax-composer specific checks (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: Remove compose after we're done (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: don't use beakerlib in blueprint (lars)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: don't depend on internal state of composer (lars)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: Do not rely on example blueprints (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: Special case compose types for osbuild-composer (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: Don't check example blueprints if we don't have to (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: Use BACKEND env variable instead of hard-coded values (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
- tests: Disable non-cli test scenarios b/c osbuild-composer (atodorov)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
|
||||||
|
* Thu Jul 23 2020 Brian C. Lane <bcl@redhat.com> 28.14.50-1
|
||||||
|
- composer-cli: Make start-ostree parent and ref optional (bcl)
|
||||||
|
Resolves: rhbz#1859680
|
||||||
|
- composer-cli: Add a get_arg function (bcl)
|
||||||
|
Related: rhbz#1859680
|
||||||
|
|
||||||
|
* Thu Jun 25 2020 Brian C. Lane <bcl@redhat.com> 28.14.49-1
|
||||||
|
- include generic.ins for s390 boot iso (dan)
|
||||||
|
Related: rhbz#1844517
|
||||||
|
- Fix the bug referenced in the last build (bcl)
|
||||||
|
Resolves: rhbz#1848727
|
||||||
|
|
||||||
|
* Thu Jun 18 2020 Brian C. Lane <bcl@redhat.com> 28.14.48-1
|
||||||
|
- composer-cli: Disable retry counter on connection timeout (bcl)
|
||||||
|
Related: rhbz#1848727
|
||||||
|
- composer-cli: Change timeout to 5 minutes (bcl)
|
||||||
|
Resolves: rhbz#1848727
|
||||||
|
|
||||||
|
* Thu Jun 11 2020 Brian C. Lane <bcl@redhat.com> 28.14.47-1
|
||||||
|
- lorax-composer: Add deprecation notice to documentation (bcl)
|
||||||
|
Related: rhbz#1844649
|
||||||
|
- composer-cli: Add documentation for uploads and osbuild-composer (bcl)
|
||||||
|
Related: rhbz#1844649
|
||||||
|
- composer-cli: Remove the upload and providers commands (bcl)
|
||||||
|
Related: rhbz#1844649
|
||||||
|
- composer-cli: Return a better error with no value (bcl)
|
||||||
|
Related: rhbz#1844649
|
||||||
|
- tests: rhel8-branch uses nose not pytest (bcl)
|
||||||
|
Related: rhbz#1844649
|
||||||
|
- tests: Add tests for composer-cli compose start JSON POST (bcl)
|
||||||
|
Related: rhbz#1844649
|
||||||
|
- composer-cli: Update bash completion for start-ostree (bcl)
|
||||||
|
Related: rhbz#1844649
|
||||||
|
- composer-cli: Add new start-ostree command (bcl)
|
||||||
|
Resolves: rhbz#1844649
|
||||||
|
- composer-cli: Add support for --size to compose start (bcl)
|
||||||
|
Related: rhbz#1844649
|
||||||
|
- composer-cli: pytoml load() needs an open file not a string (bcl)
|
||||||
|
Resolves: rhbz#1843704
|
||||||
|
- test: Put VM image overlay into /var/tmp (martin) (martin)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
* Mon Jun 01 2020 Brian C. Lane <bcl@redhat.com> 28.14.46-1
|
||||||
|
- Keep /etc/default/useradd in install.img (honza.stodola)
|
||||||
|
Resolves: rhbz#1838677
|
||||||
|
|
||||||
|
* Mon May 18 2020 Brian C. Lane <bcl@redhat.com> 28.14.45-1
|
||||||
|
- lmc-no-virt: Add requirement on anaconda-install-env-deps (bcl)
|
||||||
|
Resolves: rhbz#1827911
|
||||||
|
- Fix cherry-pick of extra multipath.conf line (bcl)
|
||||||
|
Related: rhbz#1676777
|
||||||
|
- Use smarter multipath detection logic. (dlehman)
|
||||||
|
Resolves: rhbz#1676777
|
||||||
|
|
||||||
|
* Tue May 05 2020 Brian C. Lane <bcl@redhat.com> 28.14.44-1
|
||||||
|
- rsyslog: Disable journal ratelimits during install (bcl)
|
||||||
|
Resolves: rhbz#1752754
|
||||||
|
- Update datastore for VMware testing (chrobert)
|
||||||
|
Related: rhbz#1825190
|
||||||
|
|
||||||
|
* Mon Apr 27 2020 Brian C. Lane <bcl@redhat.com> 28.14.43-1
|
||||||
|
- New lorax documentation - 28.14.43 (bcl)
|
||||||
Related: rhbz#1826479
|
Related: rhbz#1826479
|
||||||
Resolves: rhbz#1828616
|
- tests: Add tests for variant branding detection (bcl)
|
||||||
|
Related: rhbz#1826479
|
||||||
|
- lorax: Update how the release package is chosen (bcl)
|
||||||
|
Resolves: rhbz#1826479
|
||||||
|
- tests: Add tests for branding (bcl)
|
||||||
|
Related: rhbz#1826479
|
||||||
|
- Move get_dnf_base_object into a module (bcl)
|
||||||
|
Related: rhbz#1826479
|
||||||
|
- lorax: Add --skip-branding cmdline argument (bcl)
|
||||||
|
Resolves: rhbz#1826479
|
||||||
|
|
||||||
* Thu Feb 27 2020 Brian C. Lane <bcl@redhat.com> 28.14.42-1
|
* Thu Feb 27 2020 Brian C. Lane <bcl@redhat.com> 28.14.42-1
|
||||||
- lorax: Restore the 98dracut-systemd service files to the install.img (bcl)
|
- lorax: Restore the 98dracut-systemd service files to the install.img (bcl)
|
||||||
|
Loading…
Reference in New Issue
Block a user