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

[WIP] Handle the %pre and %pre-install sections in the runtime module #5504

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion anaconda.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ def _earlyExceptionHandler(ty, value, traceback):
log.info("Found a kickstart file: %s", kspath)

# Run %pre scripts.
startup_utils.run_pre_scripts(kspath)
startup_utils.run_pre_scripts()

# Parse the kickstart file.
ksdata = startup_utils.parse_kickstart(kspath, strict_mode=opts.ksstrict)
Expand Down
174 changes: 174 additions & 0 deletions pyanaconda/core/kickstart/script.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#
# Support for %pre, %pre-install sections.
#
# Copyright (C) 2024 Red Hat, Inc. All rights reserved.
#
# 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 pykickstart.errors import KickstartParseError
from pykickstart.sections import Section

from pyanaconda.core.i18n import _

__all__ = ["PreScriptSection", "PreInstallScriptSection"]


class PreScriptSection(Section):
"""Parser of the %pre sections.

Parses the name of the current %pre section and propagates
all arguments and lines of this section to the pre with the
specified name.
"""
sectionOpen = "%pre"

def __init__(self, handler, **kwargs):
super().__init__(handler, **kwargs)
self.data = None
self.interp = None
self.error_on_fail = None
self.log = None

def handleHeader(self, lineno, args):
"""Handle a header of the current %pre section.

This method is called when the opening tag for a section is
seen. Not all sections will need this method, though all
provided with kickstart include one.

:param lineno: a number of the current line
:param args: a list of strings passed as arguments
"""
super().handleHeader(lineno, args)

if not args:
raise KickstartParseError(
_("Missing name of the %pre section."),
lineno=lineno
)

name = args[1]
arguments = args[2:]
data = getattr(self.handler.pre, name, None)

if not data:
raise KickstartParseError(
_("Unknown name of the %pre section."),
lineno=lineno
)

self.data = data
self.line_number = lineno
self.data.handle_header(arguments, self.line_number)

def handleLine(self, line):
"""Handle one line of the current %pre section.

This method is called for every line of a section. Take
whatever action is appropriate. While this method is not
required to be provided, not providing it does not make
a whole lot of sense.

:param line: a complete line, with any trailing newline
"""
if not self.handler:
return

self.line_number += 1
self.data.handle_line(line, self.line_number)

def finalize(self):
"""Handle the end of the current %pre section.

This method is called when the %end tag for a section is
seen.
"""
super().finalize()
self.data.handle_end()
self.data = None


class PreInstallScriptSection(Section):
"""Parser of the %pre-install sections.

Parses the name of the current %pre-install section and propagates
all arguments and lines of this section to the pre-install with the
specified name.
"""
sectionOpen = "%pre-install"

def __init__(self, handler, **kwargs):
super().__init__(handler, **kwargs)
self.data = None
self.interp = None
self.error_on_fail = None
self.log = None

def handleHeader(self, lineno, args):
"""Handle a header of the current %pre-install section.

This method is called when the opening tag for a section is
seen. Not all sections will need this method, though all
provided with kickstart include one.

:param lineno: a number of the current line
:param args: a list of strings passed as arguments
"""
super().handleHeader(lineno, args)

if not args:
raise KickstartParseError(
_("Missing name of the %pre-install section."),
lineno=lineno
)

name = args[1]
arguments = args[2:]
data = getattr(self.handler.pre_install, name, None)

if not data:
raise KickstartParseError(
_("Unknown name of the %pre-install section."),
lineno=lineno
)

self.data = data
self.line_number = lineno
self.data.handle_header(arguments, self.line_number)

def handleLine(self, line):
"""Handle one line of the current %pre-install section.

This method is called for every line of a section. Take
whatever action is appropriate. While this method is not
required to be provided, not providing it does not make
a whole lot of sense.

:param line: a complete line, with any trailing newline
"""
if not self.handler:
return

self.line_number += 1
self.data.handle_line(line, self.line_number)

def finalize(self):
"""Handle the end of the current %pre-install section.

This method is called when the %end tag for a section is
seen.
"""
super().finalize()
self.data.handle_end()
self.data = None
28 changes: 23 additions & 5 deletions pyanaconda/core/kickstart/specification.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

from pyanaconda.core.kickstart.version import VERSION
from pyanaconda.core.kickstart.addon import AddonSection, AddonRegistry
from pyanaconda.core.kickstart.script import PreScriptSection, PreInstallScriptSection

__all__ = ["KickstartSpecification", "NoKickstartSpecification",
"KickstartSpecificationHandler", "KickstartSpecificationParser"]
Expand Down Expand Up @@ -59,8 +60,8 @@ class KickstartSpecification(object):
version = VERSION
commands = {}
commands_data = {}
sections = {}
sections_data = {}
sections = { "scripts": [] }
sections_data = { "scripts": [] }
addons = {}


Expand All @@ -83,6 +84,9 @@ def __init__(self, specification):
self.registerData(name, data)

for name, data in specification.sections_data.items():
if name is "scripts":
name, data = specification.sections_data.scripts.values()

self.registerSectionData(name, data)

if specification.addons:
Expand All @@ -93,7 +97,12 @@ def __init__(self, specification):

def registerSectionData(self, name, data):
"""Register data used by a section."""
obj = data()
if type(data) is tuple:
class_name, data = data
obj = class_name(data)
else:
obj = data()

setattr(self, name, obj)
self._registerWriteOrder(obj)

Expand All @@ -119,8 +128,17 @@ class KickstartSpecificationParser(KickstartParser):
def __init__(self, handler, specification):
super().__init__(handler)

for section in specification.sections.values():
self.registerSection(section(handler))
for sectionObj in specification.sections.values():
if "scripts" in specification.sections.keys():
sectionObj = specification.sections.scripts.values()

if type(sectionObj) is tuple:
section, dataObj = sectionObj
else:
section = sectionObj
dataObj = None

self.registerSection(section(handler, dataObj=dataObj))

if specification.addons:
self.registerSection(AddonSection(handler))
Expand Down
2 changes: 1 addition & 1 deletion pyanaconda/installation.py
Original file line number Diff line number Diff line change
Expand Up @@ -378,7 +378,7 @@ def _prepare_installation(self, payload, ksdata):
)
pre_install_scripts.append(Task(
"Run %pre-install scripts",
runPreInstallScripts, (ksdata.scripts,)
runPreInstallScripts, ()
))
installation_queue.append(pre_install_scripts)

Expand Down
52 changes: 18 additions & 34 deletions pyanaconda/kickstart.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,17 @@
from pyanaconda.errors import ScriptError, errorHandler
from pyanaconda.flags import flags
from pyanaconda.core.i18n import _
from pyanaconda.modules.common.constants.services import BOSS
from pyanaconda.modules.common.constants.objects import SCRIPTS
from pyanaconda.modules.common.constants.services import BOSS, RUNTIME
from pyanaconda.modules.common.structures.kickstart import KickstartReport

from pykickstart.base import KickstartCommand, RemovedCommand
from pykickstart.constants import KS_SCRIPT_POST, KS_SCRIPT_PRE, KS_SCRIPT_TRACEBACK, KS_SCRIPT_PREINSTALL
from pykickstart.constants import KS_SCRIPT_POST, KS_SCRIPT_TRACEBACK
from pykickstart.errors import KickstartError, KickstartParseWarning
from pykickstart.ko import KickstartObject
from pykickstart.parser import KickstartParser
from pykickstart.parser import Script as KSScript
from pykickstart.sections import NullSection, PostScriptSection, PreScriptSection, \
PreInstallScriptSection, OnErrorScriptSection, TracebackScriptSection, Section
from pykickstart.sections import NullSection, PreScriptSection, PostScriptSection, OnErrorScriptSection, TracebackScriptSection, Section
from pykickstart.version import returnClassForVersion

log = get_module_logger(__name__)
Expand Down Expand Up @@ -259,6 +259,9 @@ def __init__(self, commandUpdates=None, dataUpdates=None):
# The %packages section is handled by the DBus module.
self.packages = UselessObject()

# The %pre, %pre-install sections are handled by the DBus module.
self.scripts = UselessObject()

def __str__(self):
proxy = BOSS.get_proxy()
modules = proxy.GenerateKickstart().strip()
Expand Down Expand Up @@ -296,11 +299,11 @@ def handleCommand(self, lineno, args):
return KickstartParser.handleCommand(self, lineno, args)

def setupSections(self):
self.registerSection(PreScriptSection(self.handler, dataObj=self.scriptClass))
self.registerSection(PreInstallScriptSection(self.handler, dataObj=self.scriptClass))
self.registerSection(PostScriptSection(self.handler, dataObj=self.scriptClass))
self.registerSection(TracebackScriptSection(self.handler, dataObj=self.scriptClass))
self.registerSection(OnErrorScriptSection(self.handler, dataObj=self.scriptClass))
self.registerSection(UselessSection(self.handler, sectionOpen="%pre"))
self.registerSection(UselessSection(self.handler, sectionOpen="%pre-install"))
self.registerSection(UselessSection(self.handler, sectionOpen="%packages"))
self.registerSection(UselessSection(self.handler, sectionOpen="%addon"))

Expand All @@ -309,13 +312,9 @@ def preScriptPass(f):
# The first pass through kickstart file processing - look for %pre scripts
# and run them. This must come in a separate pass in case a script
# generates an included file that has commands for later.
ksparser = AnacondaPreParser(AnacondaKSHandler())

with check_kickstart_error():
ksparser.readKickstart(f)

# run %pre scripts
runPreScripts(ksparser.handler.scripts)
runPreScripts()


def parseKickstart(handler, f, strict_mode=False):
Expand Down Expand Up @@ -406,33 +405,18 @@ def runPostScripts(scripts):
script_log.info("All kickstart %%post script(s) have been run")


def runPreScripts(scripts):
preScripts = [s for s in scripts if s.type == KS_SCRIPT_PRE]

if len(preScripts) == 0:
return

script_log.info("Running kickstart %%pre script(s)")
stdoutLog.info(_("Running pre-installation scripts"))

for script in preScripts:
script.run("/")

script_log.info("All kickstart %%pre script(s) have been run")

def runPreScripts():
runtime_proxy = RUNTIME.get_proxy(SCRIPTS)
task = runtime_proxy.RunPreScriptsWithTask()

def runPreInstallScripts(scripts):
preInstallScripts = [s for s in scripts if s.type == KS_SCRIPT_PREINSTALL]
return task

if len(preInstallScripts) == 0:
return

script_log.info("Running kickstart %%pre-install script(s)")

for script in preInstallScripts:
script.run("/")
def runPreInstallScripts():
runtime_proxy = RUNTIME.get_proxy(SCRIPTS)
task = runtime_proxy.RunPreInstallScriptsWithTask()

script_log.info("All kickstart %%pre-install script(s) have been run")
return task


def runTracebackScripts(scripts):
Expand Down
2 changes: 1 addition & 1 deletion pyanaconda/modules/boss/kickstart_manager/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
TrackedKickstartElements

VALID_SECTIONS_ANACONDA = [
"%pre", "%pre-install", "%post", "%onerror", "%traceback", "%packages", "%addon"
"%pre", "%pre-install", "%post", "%onerror", "%traceback", "%packages", "%scripts", "%addon"
]


Expand Down
5 changes: 5 additions & 0 deletions pyanaconda/modules/common/constants/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
basename="UserInterface"
)

SCRIPTS = DBusObjectIdentifier(
namespace=RUNTIME_NAMESPACE,
basename="ScriptsInterface"
)

# Storage objects.

BOOTLOADER = DBusObjectIdentifier(
Expand Down
Loading
Loading