Move the run part of LoraxTemplateRunner into new TemplateRunner class

This will make it easier to add a new subclass that only handles
installpkg for use with livemedia-creator and lorax-composer iso
creation.

(cherry picked from commit 8eaad3bc5e)
This commit is contained in:
Brian C. Lane 2019-02-15 16:31:08 -08:00
parent 883bc07fc8
commit 54fe00d16e

View File

@ -103,19 +103,13 @@ def rexists(pathname, root=""):
return True return True
return False return False
# TODO: operate inside an actual chroot for safety? Not that RPM bothers.. class TemplateRunner(object):
class LoraxTemplateRunner(object):
''' '''
This class parses and executes Lorax templates. Sample usage: This class parses and executes Lorax templates. Sample usage:
# install a bunch of packages # install a bunch of packages
runner = LoraxTemplateRunner(inroot=rundir, outroot=rundir, dbo=dnf_obj) runner = LoraxTemplateRunner(inroot=rundir, outroot=rundir, dbo=dnf_obj)
runner.run("install-packages.ltmpl") runner.run("install-packages.ltmpl")
# modify a runtime dir
runner = LoraxTemplateRunner(inroot=rundir, outroot=newrun)
runner.run("runtime-transmogrify.ltmpl")
NOTES: NOTES:
* Parsing procedure is roughly: * Parsing procedure is roughly:
@ -129,6 +123,75 @@ class LoraxTemplateRunner(object):
* Parsing and execution are *separate* passes - so you can't use the result * Parsing and execution are *separate* passes - so you can't use the result
of a command in an %if statement (or any other control statements)! of a command in an %if statement (or any other control statements)!
'''
def __init__(self, fatalerrors=True, templatedir=None, defaults=None, builtins=None):
self.fatalerrors = fatalerrors
self.templatedir = templatedir or "/usr/share/lorax"
self.templatefile = None
self.builtins = builtins or {}
self.defaults = defaults or {}
def run(self, templatefile, **variables):
for k,v in list(self.defaults.items()) + list(self.builtins.items()):
variables.setdefault(k,v)
logger.debug("executing %s with variables=%s", templatefile, variables)
self.templatefile = templatefile
t = LoraxTemplate(directories=[self.templatedir])
commands = t.parse(templatefile, variables)
self._run(commands)
def _run(self, parsed_template):
logger.info("running %s", self.templatefile)
for (num, line) in enumerate(parsed_template,1):
logger.debug("template line %i: %s", num, " ".join(line))
skiperror = False
(cmd, args) = (line[0], line[1:])
# Following Makefile convention, if the command is prefixed with
# a dash ('-'), we'll ignore any errors on that line.
if cmd.startswith('-'):
cmd = cmd[1:]
skiperror = True
try:
# grab the method named in cmd and pass it the given arguments
f = getattr(self, cmd, None)
if cmd[0] == '_' or cmd == 'run' or not isinstance(f, collections.Callable):
raise ValueError("unknown command %s" % cmd)
f(*args)
except Exception: # pylint: disable=broad-except
if skiperror:
logger.debug("ignoring error")
continue
logger.error("template command error in %s:", self.templatefile)
logger.error(" %s", " ".join(line))
# format the exception traceback
exclines = traceback.format_exception(*sys.exc_info())
# skip the bit about "ltmpl.py, in _run()" - we know that
exclines.pop(1)
# log the "ErrorType: this is what happened" line
logger.error(" %s", exclines[-1].strip())
# and log the entire traceback to the debug log
for _line in ''.join(exclines).splitlines():
logger.debug(" %s", _line)
if self.fatalerrors:
raise
# TODO: operate inside an actual chroot for safety? Not that RPM bothers..
class LoraxTemplateRunner(TemplateRunner):
'''
This class parses and executes Lorax templates. Sample usage:
# install a bunch of packages
runner = LoraxTemplateRunner(inroot=rundir, outroot=rundir, dbo=dnf_obj)
runner.run("install-packages.ltmpl")
# modify a runtime dir
runner = LoraxTemplateRunner(inroot=rundir, outroot=newrun)
runner.run("runtime-transmogrify.ltmpl")
NOTES:
* Commands that run external programs (e.g. systemctl) currently use * Commands that run external programs (e.g. systemctl) currently use
the *host*'s copy of that program, which may cause problems if there's a the *host*'s copy of that program, which may cause problems if there's a
@ -152,14 +215,11 @@ class LoraxTemplateRunner(object):
self.inroot = inroot self.inroot = inroot
self.outroot = outroot self.outroot = outroot
self.dbo = dbo self.dbo = dbo
self.fatalerrors = fatalerrors builtins = DataHolder(exists=lambda p: rexists(p, root=inroot),
self.templatedir = templatedir or "/usr/share/lorax" glob=lambda g: list(rglob(g, root=inroot)))
self.templatefile = None
# some builtin methods
self.builtins = DataHolder(exists=lambda p: rexists(p, root=inroot),
glob=lambda g: list(rglob(g, root=inroot)))
self.defaults = defaults or {}
self.results = DataHolder(treeinfo=dict()) # just treeinfo for now self.results = DataHolder(treeinfo=dict()) # just treeinfo for now
super(LoraxTemplateRunner, self).__init__(fatalerrors, templatedir, defaults, builtins)
# TODO: set up custom logger with a filter to add line info # TODO: set up custom logger with a filter to add line info
def _out(self, path): def _out(self, path):
@ -210,51 +270,6 @@ class LoraxTemplateRunner(object):
for pkg in debug_pkgs: for pkg in debug_pkgs:
f.write("%s\n" % pkg) f.write("%s\n" % pkg)
def run(self, templatefile, **variables):
for k,v in list(self.defaults.items()) + list(self.builtins.items()):
variables.setdefault(k,v)
logger.debug("executing %s with variables=%s", templatefile, variables)
self.templatefile = templatefile
t = LoraxTemplate(directories=[self.templatedir])
commands = t.parse(templatefile, variables)
self._run(commands)
def _run(self, parsed_template):
logger.info("running %s", self.templatefile)
for (num, line) in enumerate(parsed_template,1):
logger.debug("template line %i: %s", num, " ".join(line))
skiperror = False
(cmd, args) = (line[0], line[1:])
# Following Makefile convention, if the command is prefixed with
# a dash ('-'), we'll ignore any errors on that line.
if cmd.startswith('-'):
cmd = cmd[1:]
skiperror = True
try:
# grab the method named in cmd and pass it the given arguments
f = getattr(self, cmd, None)
if cmd[0] == '_' or cmd == 'run' or not isinstance(f, collections.Callable):
raise ValueError("unknown command %s" % cmd)
f(*args)
except Exception: # pylint: disable=broad-except
if skiperror:
logger.debug("ignoring error")
continue
logger.error("template command error in %s:", self.templatefile)
logger.error(" %s", " ".join(line))
# format the exception traceback
exclines = traceback.format_exception(*sys.exc_info())
# skip the bit about "ltmpl.py, in _run()" - we know that
exclines.pop(1)
# log the "ErrorType: this is what happened" line
logger.error(" %s", exclines[-1].strip())
# and log the entire traceback to the debug log
for _line in ''.join(exclines).splitlines():
logger.debug(" %s", _line)
if self.fatalerrors:
raise
def install(self, srcglob, dest): def install(self, srcglob, dest):
''' '''
install SRC DEST install SRC DEST