Add support for DNF 3.2 module_platform_id config value
This borrows simpleconfig.py from Anaconda to make parsing os-release easier. It defaults to platform:el8
This commit is contained in:
parent
a2375e870f
commit
3a9d39f397
@ -64,6 +64,9 @@ DRACUT_DEFAULT = ["--xz", "--install", "/.buildstamp", "--no-early-microcode", "
|
||||
REMOVE_PPC64_DRIVERS = "floppy scsi_debug nouveau radeon cirrus mgag200"
|
||||
REMOVE_PPC64_MODULES = "drm plymouth"
|
||||
|
||||
# Used for DNF conf.module_platform_id
|
||||
DEFAULT_PLATFORM_ID = "platform:el8"
|
||||
|
||||
class ArchData(DataHolder):
|
||||
lib64_arches = ("x86_64", "ppc64", "ppc64le", "s390x", "ia64", "aarch64")
|
||||
bcj_arch = dict(i386="x86", x86_64="x86",
|
||||
|
@ -25,6 +25,8 @@ from glob import glob
|
||||
import os
|
||||
import shutil
|
||||
|
||||
from pylorax import DEFAULT_PLATFORM_ID
|
||||
from pylorax.simpleconfig import SimpleConfigFile
|
||||
|
||||
def get_base_object(conf):
|
||||
"""Get the DNF object with settings from the config file
|
||||
@ -69,6 +71,17 @@ def get_base_object(conf):
|
||||
log.info("releasever = %s", _releasever)
|
||||
dbc.releasever = _releasever
|
||||
|
||||
# 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 = SimpleConfigFile("/etc/os-release")
|
||||
os_release.read()
|
||||
platform_id = os_release.get("PLATFORM_ID") or DEFAULT_PLATFORM_ID
|
||||
log.info("Using %s for module_platform_id", platform_id)
|
||||
dbc.module_platform_id = platform_id
|
||||
|
||||
# write the dnf configuration file
|
||||
with open(dnfconf, "w") as f:
|
||||
f.write(dbc.dump())
|
||||
|
203
src/pylorax/simpleconfig.py
Normal file
203
src/pylorax/simpleconfig.py
Normal file
@ -0,0 +1,203 @@
|
||||
#
|
||||
# simpleconifg.py - representation of a simple configuration file (sh-like)
|
||||
#
|
||||
# Copyright (C) 1999-2015 Red Hat, Inc.
|
||||
#
|
||||
# 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 v.2, or (at your option) any later version.
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
# ANY WARRANTY expressed or implied, including the implied warranties 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. Any Red Hat trademarks that are incorporated in the
|
||||
# source code or documentation are not subject to the GNU General Public
|
||||
# License and may only be used or replicated with the express permission of
|
||||
# Red Hat, Inc.
|
||||
#
|
||||
import os
|
||||
import shlex
|
||||
import string # pylint: disable=deprecated-module
|
||||
import tempfile
|
||||
from pyanaconda.core.util import upperASCII
|
||||
|
||||
_SAFECHARS = frozenset(string.ascii_letters + string.digits + '@%_-+=:,./')
|
||||
|
||||
def unquote(s):
|
||||
return ' '.join(shlex.split(s))
|
||||
|
||||
def quote(s, always=False):
|
||||
""" If always is set it returns a quoted value
|
||||
"""
|
||||
if not always:
|
||||
for c in s:
|
||||
if c not in _SAFECHARS:
|
||||
break
|
||||
else:
|
||||
return s
|
||||
return '"' + s.replace('"', '\\"') + '"'
|
||||
|
||||
def find_comment(s):
|
||||
""" Look for a # comment outside of a quoted string.
|
||||
If there are no quotes, find the last # in the string.
|
||||
|
||||
:param str s: string to check for comment and quotes
|
||||
:returns: index of comment or None
|
||||
:rtype: int or None
|
||||
|
||||
Handles comments inside quotes and quotes inside quotes.
|
||||
"""
|
||||
q = None
|
||||
for i in range(len(s)):
|
||||
if not q and s[i] == '#':
|
||||
return i
|
||||
|
||||
# Ignore quotes inside other quotes
|
||||
if s[i] in "'\"":
|
||||
if s[i] == q:
|
||||
q = None
|
||||
elif q is None:
|
||||
q = s[i]
|
||||
return None
|
||||
|
||||
|
||||
def write_tmpfile(filename, data):
|
||||
# Create a temporary in the same directory as the target file to ensure
|
||||
# the new file is on the same filesystem
|
||||
tmpf = tempfile.NamedTemporaryFile(mode="w", delete=False,
|
||||
dir=os.path.dirname(filename) or '.',
|
||||
prefix="." + os.path.basename(filename))
|
||||
tmpf.write(data)
|
||||
tmpf.close()
|
||||
|
||||
# Change the permissions (currently 0600) to match the original file
|
||||
if os.path.exists(filename):
|
||||
m = os.stat(filename).st_mode
|
||||
else:
|
||||
m = 0o0644
|
||||
os.chmod(tmpf.name, m)
|
||||
|
||||
# Move the temporary file over the top of the original
|
||||
os.rename(tmpf.name, filename)
|
||||
|
||||
class SimpleConfigFile(object):
|
||||
""" Edit values in a configuration file without changing comments.
|
||||
Supports KEY=VALUE lines and ignores everything else.
|
||||
Supports adding new keys.
|
||||
Supports deleting keys.
|
||||
Preserves comment, blank lines and comments on KEY lines
|
||||
Does not support duplicate key entries.
|
||||
"""
|
||||
def __init__(self, filename=None, read_unquote=True, write_quote=True,
|
||||
always_quote=False):
|
||||
self.filename = filename
|
||||
self.read_unquote = read_unquote
|
||||
self.write_quote = write_quote
|
||||
self.always_quote = always_quote
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
self._lines = []
|
||||
self.info = {}
|
||||
|
||||
def read(self, filename=None):
|
||||
""" passing filename will override the filename passed to init.
|
||||
|
||||
save the lines into self._lines and the key/value pairs into
|
||||
self.info
|
||||
"""
|
||||
filename = filename or self.filename
|
||||
with open(filename) as f:
|
||||
for line in f:
|
||||
self._lines.append(line)
|
||||
key, value, _comment = self._parseline(line)
|
||||
if key:
|
||||
self.info[key] = value
|
||||
|
||||
def write(self, filename=None, use_tmp=True):
|
||||
""" passing filename will override the filename passed to init.
|
||||
"""
|
||||
filename = filename or self.filename
|
||||
if not filename:
|
||||
return None
|
||||
|
||||
if use_tmp:
|
||||
write_tmpfile(filename, str(self))
|
||||
else:
|
||||
# write directly to the file
|
||||
with open(filename, "w") as fobj:
|
||||
fobj.write(str(self))
|
||||
|
||||
def set(self, *args):
|
||||
for key, value in args:
|
||||
self.info[upperASCII(key)] = value
|
||||
|
||||
def unset(self, *keys):
|
||||
for key in (upperASCII(k) for k in keys):
|
||||
if key in self.info:
|
||||
del self.info[key]
|
||||
|
||||
def get(self, key):
|
||||
return self.info.get(upperASCII(key), "")
|
||||
|
||||
def _parseline(self, line):
|
||||
""" parse a line into a key, value and comment
|
||||
|
||||
:param str line: Line to be parsed
|
||||
:returns: Tuple of key, value, comment
|
||||
:rtype: tuple
|
||||
|
||||
Handle comments and optionally unquote quoted strings
|
||||
Returns (key, value, comment) or (None, None, comment)
|
||||
key is always UPPERCASE and comment may by "" if none was found.
|
||||
"""
|
||||
s = line.strip()
|
||||
# Look for a # outside any quotes
|
||||
comment = ""
|
||||
comment_index = find_comment(s)
|
||||
if comment_index is not None:
|
||||
comment = s[comment_index:]
|
||||
s = s[:comment_index] # remove from comment to EOL
|
||||
|
||||
key, eq, val = s.partition('=')
|
||||
key = key.strip()
|
||||
val = val.strip()
|
||||
if self.read_unquote:
|
||||
val = unquote(val)
|
||||
if key != '' and eq == '=':
|
||||
return (upperASCII(key), val, comment)
|
||||
else:
|
||||
return (None, None, comment)
|
||||
|
||||
def _kvpair(self, key, comment=""):
|
||||
value = self.info[key]
|
||||
if self.write_quote or self.always_quote:
|
||||
value = quote(value, self.always_quote)
|
||||
if comment:
|
||||
comment = " " + comment
|
||||
return key + '=' + value + comment + "\n"
|
||||
|
||||
def __str__(self):
|
||||
""" Return the file that was read, replacing existing keys with new values
|
||||
removing keys that have been deleted and adding new keys.
|
||||
"""
|
||||
oldkeys = []
|
||||
s = ""
|
||||
for line in self._lines:
|
||||
key, _value, comment = self._parseline(line)
|
||||
if key is None:
|
||||
s += line
|
||||
else:
|
||||
if key not in self.info:
|
||||
continue
|
||||
oldkeys.append(key)
|
||||
s += self._kvpair(key, comment)
|
||||
|
||||
# Add new keys
|
||||
for key in self.info:
|
||||
if key not in oldkeys:
|
||||
s += self._kvpair(key)
|
||||
|
||||
return s
|
@ -33,8 +33,9 @@ import dnf
|
||||
import dnf.logging
|
||||
import librepo
|
||||
import pylorax
|
||||
from pylorax import DRACUT_DEFAULT
|
||||
from pylorax import DRACUT_DEFAULT, DEFAULT_PLATFORM_ID
|
||||
from pylorax.cmdline import lorax_parser
|
||||
from pylorax.simpleconfig import SimpleConfigFile
|
||||
import selinux
|
||||
|
||||
def setup_logging(opts):
|
||||
@ -209,6 +210,17 @@ def get_dnf_base_object(installroot, sources, mirrorlists=None, repos=None,
|
||||
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 = SimpleConfigFile("/etc/os-release")
|
||||
os_release.read()
|
||||
platform_id = os_release.get("PLATFORM_ID") or 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")
|
||||
|
Loading…
Reference in New Issue
Block a user