Merge branch 'roth_install_hook' of github.com:carlroth/OpenNetworkLinux

This commit is contained in:
Jeffrey Townsend
2016-11-28 22:50:10 +00:00
7 changed files with 110 additions and 48 deletions

View File

@@ -18,8 +18,8 @@ MKINSTALLER_OPTS = \
--swi onl-swi:$(ARCH) \
--preinstall-script $(ONL)/builds/any/installer/sample-preinstall.sh \
--postinstall-script $(ONL)/builds/any/installer/sample-postinstall.sh \
--preinstall-plugin $(ONL)/builds/any/installer/sample-preinstall.py \
--postinstall-plugin $(ONL)/builds/any/installer/sample-postinstall.py \
--plugin $(ONL)/builds/any/installer/sample-preinstall.py \
--plugin $(ONL)/builds/any/installer/sample-postinstall.py \
# THIS LINE INTENTIONALLY LEFT BLANK
__installer:

View File

@@ -5,7 +5,7 @@ Example Python script for post-install hooks.
Add this as a postinstall hook to your installer via
the 'mkinstaller.py' command line:
$ mkinstaller.py ... --postinstall-plugin sample-postinstall.py ...
$ mkinstaller.py ... --plugin sample-postinstall.py ...
At install time, this script will
@@ -26,6 +26,8 @@ of the installer Python script) will
2. instantiate an instance of each class, with the installer
object initialized as the 'installer' attribute
3. invoke the 'run' method (which must be overridden by implementors)
For a post-install plugin, the 'mode' argument is set to
PLUGIN_POSTINSTALL.
4. invoke the 'shutdown' method (by default, a no-op)
The 'run' method should return zero on success. In any other case, the
@@ -45,6 +47,12 @@ e.g. MountContext. The OnlMountContextReadWrite object and their
siblings won't work here because the mtab.yml file is not populated
within the loader environment.
A post-install plugin should execute any post-install actions when
'mode' is set to PLUGIN_POSTINSTALL. If 'mode' is set to any other
value, the plugin should ignore it and return zero. The plugin run()
method is invoked multiple times during the installer with different
values of 'mode'. The 'shutdown()' method is called only once.
When using MountContxt, the system state in the installer object can help
(self.installer.blkidParts in particular).
@@ -54,6 +62,10 @@ import onl.install.Plugin
class Plugin(onl.install.Plugin.Plugin):
def run(self):
self.log.info("hello from postinstall plugin")
def run(self, mode):
if mode == self.PLUGIN_POSTINSTALL:
self.log.info("hello from postinstall plugin")
return 0
return 0

View File

@@ -5,7 +5,7 @@ Example Python script for pre-install hooks.
Add this as a preinstall hook to your installer via
the 'mkinstaller.py' command line:
$ mkinstaller.py ... --preinstall-plugin sample-preinstall.py ...
$ mkinstaller.py ... --plugin sample-preinstall.py ...
At install time, this script will
@@ -26,6 +26,8 @@ of the installer Python script) will
2. instantiate an instance of each class, with the installer
object initialized as the 'installer' attribute
3. invoke the 'run' method (which must be overridden by implementors)
For a pre-install plugin, the 'mode' argument is set to
PLUGIN_PREINSTALL.
4. invoke the 'shutdown' method (by default, a no-op)
The 'run' method should return zero on success. In any other case, the
@@ -38,12 +40,22 @@ prepped/initialized/scanned yet. As per the ONL installer API, the
installer starts with *no* filesystems mounted, not even the ones from
a prior install.
A pre-install plugin should execute any pre-install actions when
'mode' is set to PLUGIN_PREINSTALL. If 'mode' is set to any other
value, the plugin should ignore it and return zero. The plugin run()
method is invoked multiple times during the installer with different
values of 'mode'. The 'shutdown()' method is called only once.
"""
import onl.install.Plugin
class Plugin(onl.install.Plugin.Plugin):
def run(self):
self.log.info("hello from preinstall plugin")
def run(self, mode):
if mode == self.PLUGIN_PREINSTALL:
self.log.info("hello from preinstall plugin")
return 0
return 0

View File

@@ -18,8 +18,8 @@ MKINSTALLER_OPTS = \
--swi onl-swi:$(ARCH) \
--preinstall-script $(ONL)/builds/any/installer/sample-preinstall.sh \
--postinstall-script $(ONL)/builds/any/installer/sample-postinstall.sh \
--preinstall-plugin $(ONL)/builds/any/installer/sample-preinstall.py \
--postinstall-plugin $(ONL)/builds/any/installer/sample-postinstall.py \
--plugin $(ONL)/builds/any/installer/sample-preinstall.py \
--plugin $(ONL)/builds/any/installer/sample-postinstall.py \
# THIS LINE INTENTIONALLY LEFT BLANK
__installer:

View File

@@ -14,6 +14,7 @@ import yaml
import zipfile
import shutil
import imp
import fnmatch, glob
from InstallUtils import SubprocessMixin
from InstallUtils import MountContext, BlkidParser, PartedParser
@@ -90,6 +91,9 @@ class Base:
self.zf = None
# zipfile handle to installer archive
self.plugins = []
# dynamically-detected plugins
def run(self):
self.log.error("not implemented")
return 1
@@ -99,6 +103,11 @@ class Base:
return 1
def shutdown(self):
plugins, self.plugins = self.plugins, []
for plugin in plugins:
plugin.shutdown()
zf, self.zf = self.zf, None
if zf: zf.close()
@@ -415,38 +424,27 @@ class Base:
return 0
def preinstall(self):
return self.runPlugin("preinstall.py")
def postinstall(self):
return self.runPlugin("postinstall.py")
def runPluginFile(self, pyPath):
def loadPluginsFromFile(self, pyPath):
self.log.info("loading plugins from %s", pyPath)
with open(pyPath) as fd:
sfx = ('.py', 'U', imp.PY_SOURCE,)
mod = imp.load_module("plugin", fd, pyPath, sfx)
for attr in dir(mod):
klass = getattr(mod, attr)
if isinstance(klass, type) and issubclass(klass, Plugin):
self.log.info("%s: running plugin %s", pyPath, attr)
self.log.info("%s: found plugin %s", pyPath, attr)
plugin = klass(self)
try:
code = plugin.run()
except:
self.log.exception("plugin failed")
code = 1
plugin.shutdown()
if code: return code
self.plugins.append(plugin)
return 0
def loadPlugins(self):
def runPlugin(self, basename):
pat = os.path.join(self.im.installerConf.installer_dir, "plugins", "*.py")
for src in glob.glob(pat):
self.loadPluginsFromFile(src)
src = os.path.join(self.im.installerConf.installer_dir, basename)
if os.path.exists(src):
return self.runPluginFile(src)
if basename in self.zf.namelist():
pat = "plugins/*.py"
for basename in self.zf.namelist():
if not fnmatch.fnmatch(basename, pat): continue
try:
src = None
with self.zf.open(basename, "r") as rfd:
@@ -454,11 +452,24 @@ class Base:
suffix=".py")
with os.fdopen(wfno, "w") as wfd:
shutil.copyfileobj(rfd, wfd)
return self.runPluginFile(src)
self.loadPluginsFromFile(src)
finally:
if src and os.path.exists(src):
os.unlink(src)
return 0
def runPlugins(self, mode):
self.log.info("running plugins: %s", mode)
for plugin in self.plugins:
try:
code = plugin.run(mode)
except:
self.log.exception("plugin failed")
code = 1
if code: return code
return 0
GRUB_TPL = """\
serial %(serial)s
terminal_input serial
@@ -654,7 +665,10 @@ class GrubInstaller(SubprocessMixin, Base):
self.im.installerConf.installer_zip)
self.zf = zipfile.ZipFile(p)
code = self.preinstall()
code = self.loadPlugins()
if code: return code
code = self.runPlugins(Plugin.PLUGIN_PREINSTALL)
if code: return code
code = self.findGpt()
@@ -712,7 +726,7 @@ class GrubInstaller(SubprocessMixin, Base):
code = self.installGrub()
if code: return code
code = self.postinstall()
code = self.runPlugins(Plugin.PLUGIN_POSTINSTALL)
if code: return code
self.log.info("ONL loader install successful.")
@@ -896,7 +910,10 @@ class UbootInstaller(SubprocessMixin, Base):
self.im.installerConf.installer_zip)
self.zf = zipfile.ZipFile(p)
code = self.preinstall()
code = self.loadPlugins()
if code: return code
code = self.runPlugins(Plugin.PLUGIN_PREINSTALL)
if code: return code
code = self.assertUnmounted()
@@ -968,7 +985,7 @@ class UbootInstaller(SubprocessMixin, Base):
code = self.installUbootEnv()
if code: return code
code = self.postinstall()
code = self.runPlugins(Plugin.PLUGIN_POSTINSTALL)
if code: return code
return 0

View File

@@ -5,13 +5,25 @@ Base class for installer plugins.
class Plugin(object):
PLUGIN_PREINSTALL = "preinstall"
PLUGIN_POSTINSTALL = "postinstall"
def __init__(self, installer):
self.installer = installer
self.log = self.installer.log.getChild("plugin")
def run(self):
self.log.warn("not implemented")
return 0
def run(self, mode):
if mode == self.PLUGIN_PREINSTALL:
self.log.warn("pre-install plugin not implemented")
return 0
if mode == self.PLUGIN_POSTINSTALL:
self.log.warn("post-install plugin not implemented")
return 0
self.log.warn("invalid plugin mode %s", repr(mode))
return 1
def shutdown(self):
pass

View File

@@ -191,10 +191,8 @@ if __name__ == '__main__':
ap.add_argument("--postinstall-script",
help="Specify a preinstall script (runs after installer)")
ap.add_argument("--preinstall-plugin",
help="Specify a preinstall plugin (runs from within the installer chroot)")
ap.add_argument("--postinstall-plugin",
help="Specify a postinstall plugin (runs from within the installer chroot)")
ap.add_argument("--plugin", action='append',
help="Specify a Python plugin (runs from within the installer chroot)")
ops = ap.parse_args()
installer = InstallerShar(ops.arch, ops.work_dir)
@@ -235,15 +233,26 @@ if __name__ == '__main__':
if not os.path.exists(hookdir):
os.makedirs(hookdir)
plugindir = os.path.join(installer.work_dir, "tmp/plugins")
if not os.path.exists(plugindir):
os.makedirs(plugindir)
if ops.preinstall_script:
installer.add_file_as(ops.preinstall_script, "preinstall.sh")
if ops.postinstall_script:
installer.add_file_as(ops.postinstall_script, "postinstall.sh")
if ops.preinstall_plugin:
installer.add_file_as(ops.preinstall_plugin, "preinstall.py")
if ops.postinstall_plugin:
installer.add_file_as(ops.postinstall_plugin, "postinstall.py")
for plugin in ops.plugin:
basename = os.path.split(plugin)[1]
basename = os.path.splitext(basename)[0]
dst = tempfile.mktemp(dir=plugindir,
prefix=basename+'-',
suffix='.py')
shutil.copy(plugin, dst)
l = os.listdir(plugindir)
if l:
installer.add_dir(plugindir)
iname = os.path.abspath(ops.out)
installer.build(iname)