mirror of
https://github.com/Telecominfraproject/OpenNetworkLinux.git
synced 2025-12-25 17:27:01 +00:00
Merge branch 'roth_install_hook' of github.com:carlroth/OpenNetworkLinux
This commit is contained in:
@@ -10,8 +10,20 @@ endif
|
||||
include $(ONL)/make/versions/version-onl.mk
|
||||
INSTALLER_NAME=$(FNAME_PRODUCT_VERSION)_ONL-OS_$(FNAME_BUILD_ID)_$(UARCH)_$(BOOTMODE)_INSTALLER
|
||||
|
||||
MKINSTALLER_OPTS = \
|
||||
--arch $(ARCH) \
|
||||
--boot-config boot-config \
|
||||
--add-dir config \
|
||||
--initrd onl-loader-initrd:$(ARCH) onl-loader-initrd-$(ARCH).cpio.gz \
|
||||
--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 \
|
||||
# THIS LINE INTENTIONALLY LEFT BLANK
|
||||
|
||||
__installer:
|
||||
$(ONL)/tools/mkinstaller.py --arch $(ARCH) --boot-config boot-config --add-dir config --initrd onl-loader-initrd:$(ARCH) onl-loader-initrd-$(ARCH).cpio.gz --swi onl-swi:$(ARCH) --out $(INSTALLER_NAME)
|
||||
$(ONL)/tools/mkinstaller.py $(MKINSTALLER_OPTS) --out $(INSTALLER_NAME)
|
||||
md5sum "$(INSTALLER_NAME)" | awk '{ print $$1 }' > "$(INSTALLER_NAME).md5sum"
|
||||
|
||||
|
||||
|
||||
@@ -361,16 +361,44 @@ else
|
||||
installer_list=$initrd_archive
|
||||
fi
|
||||
|
||||
installer_unzip() {
|
||||
local zip tmp dummy
|
||||
zip=$1; shift
|
||||
|
||||
installer_say "Extracting from $zip: $@ ..."
|
||||
|
||||
tmp=$(mktemp -d -t "unzip-XXXXXX")
|
||||
if test "$SFX_PAD"; then
|
||||
# ha ha, busybox cannot exclude multiple files
|
||||
unzip -o $zip "$@" -x $SFX_PAD -d $tmp
|
||||
elif test "$SFX_UNZIP"; then
|
||||
unzip -o $zip "$@" -x $installer_script -d $tmp
|
||||
else
|
||||
dd if=$zip bs=$SFX_BLOCKSIZE skip=$SFX_BLOCKS \
|
||||
| unzip -o - "$@" -x $installer_script -d $tmp
|
||||
fi
|
||||
|
||||
rm -f $tmp/$installer_script
|
||||
if test "$SFX_PAD"; then
|
||||
rm -f $tmp/$SFX_PAD
|
||||
fi
|
||||
|
||||
set dummy $tmp/*
|
||||
if test -e "$2"; then
|
||||
shift
|
||||
while test $# -gt 0; do
|
||||
mv "$1" .
|
||||
shift
|
||||
done
|
||||
else
|
||||
installer_say "Extracting from $zip: no files extracted"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
installer_say "Unpacking ONL installer files..."
|
||||
if test "$SFX_PAD"; then
|
||||
# ha ha, busybox cannot exclude multiple files
|
||||
unzip -o $installer_zip $installer_list -x $SFX_PAD
|
||||
elif test "$SFX_UNZIP"; then
|
||||
unzip -o $installer_zip $installer_list -x $installer_script
|
||||
else
|
||||
dd if=$installer_zip bs=$SFX_BLOCKSIZE skip=$SFX_BLOCKS \
|
||||
| unzip -o - $installer_list -x $installer_script
|
||||
fi
|
||||
installer_unzip $installer_zip $installer_list
|
||||
|
||||
# Developer debugging
|
||||
if has_boot_env onl_installer_unpack_only; then installer_unpack_only=1; fi
|
||||
@@ -513,6 +541,13 @@ else
|
||||
installer_say "*** watch out for lingering mount-points"
|
||||
fi
|
||||
|
||||
installer_unzip $installer_zip preinstall.sh || :
|
||||
if test -f preinstall.sh; then
|
||||
installer_say "Invoking pre-install actions"
|
||||
chmod +x preinstall.sh
|
||||
./preinstall.sh $rootdir
|
||||
fi
|
||||
|
||||
chroot "${rootdir}" $installer_shell
|
||||
|
||||
if test -f "$postinst"; then
|
||||
@@ -522,6 +557,12 @@ if test -f "$postinst"; then
|
||||
set +x
|
||||
fi
|
||||
|
||||
installer_unzip $installer_zip postinstall.sh || :
|
||||
if test -f preinstall.sh; then
|
||||
chmod +x postinstall.sh
|
||||
./postinstall.sh $rootdir
|
||||
fi
|
||||
|
||||
trap - 0 1
|
||||
installer_umount
|
||||
|
||||
|
||||
59
builds/any/installer/sample-postinstall.py
Normal file
59
builds/any/installer/sample-postinstall.py
Normal file
@@ -0,0 +1,59 @@
|
||||
"""sample-postinstall.py
|
||||
|
||||
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 ...
|
||||
|
||||
At install time, this script will
|
||||
|
||||
1. be extracted into a temporary working directory
|
||||
2. be imported as a module, in the same process as the installer
|
||||
script
|
||||
|
||||
Importing the module should not trigger any side-effects.
|
||||
|
||||
At the appropriate time during the install (a chrooted invocation
|
||||
of the installer Python script) will
|
||||
|
||||
1. scrape the top-level plugin's namespace for subclasses of
|
||||
onl.install.Plugin.Plugin.
|
||||
Implementors should declare classes here
|
||||
(inheriting from onl.install.Plugin.Plugin) to embed the plugin
|
||||
functionality.
|
||||
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)
|
||||
4. invoke the 'shutdown' method (by default, a no-op)
|
||||
|
||||
The 'run' method should return zero on success. In any other case, the
|
||||
installer terminates.
|
||||
|
||||
The post-install plugins are invoked after the installer is complete
|
||||
and after the boot loader is updated.
|
||||
|
||||
An exception to this is for proxy GRUB configurations. In that case, the
|
||||
post-install plugins are invoked after the install is finished, but before
|
||||
the boot loader has been updated.
|
||||
|
||||
At the time the post-install plugin is invoked, none of the
|
||||
filesystems are mounted. If the implementor needs to manipulate the
|
||||
disk, the filesystems should be re-mounted temporarily with
|
||||
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.
|
||||
|
||||
When using MountContxt, the system state in the installer object can help
|
||||
(self.installer.blkidParts in particular).
|
||||
|
||||
"""
|
||||
|
||||
import onl.install.Plugin
|
||||
|
||||
class Plugin(onl.install.Plugin.Plugin):
|
||||
|
||||
def run(self):
|
||||
self.log.info("hello from postinstall plugin")
|
||||
return 0
|
||||
42
builds/any/installer/sample-postinstall.sh
Normal file
42
builds/any/installer/sample-postinstall.sh
Normal file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
######################################################################
|
||||
#
|
||||
# sample-postinstall.sh
|
||||
#
|
||||
# Example script for post-install hooks.
|
||||
#
|
||||
# Add this as a postinstall hook to your installer via
|
||||
# the 'mkinstaller.py' command line:
|
||||
#
|
||||
# $ mkinstaller.py ... --postinstall-script sample-postinstall.sh ...
|
||||
#
|
||||
# At install time, this script will
|
||||
#
|
||||
# 1. be extracted into the working directory with the other installer
|
||||
# collateral
|
||||
# 2. have the execute bit set
|
||||
# 3. run in-place with the installer chroot directory passed
|
||||
# as the first command line parameter
|
||||
#
|
||||
# If the script fails (returns a non-zero exit code) then
|
||||
# the install is aborted.
|
||||
#
|
||||
# This script is executed using the ONIE runtime (outside the chroot),
|
||||
# after the actual installer (chrooted Python script) has finished.
|
||||
#
|
||||
# This script is run after the postinstall actions (e.g. proxy GRUB
|
||||
# commands)
|
||||
#
|
||||
# At the time the script is run, the installer environment (chroot)
|
||||
# is fully prepared, including filesystem mount-points.
|
||||
# That is, the chroot mount points have not been unmounted yet.
|
||||
#
|
||||
######################################################################
|
||||
|
||||
rootdir=$1; shift
|
||||
|
||||
echo "Hello from postinstall"
|
||||
echo "Chroot is $rootdir"
|
||||
|
||||
exit 0
|
||||
49
builds/any/installer/sample-preinstall.py
Normal file
49
builds/any/installer/sample-preinstall.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""sample-preinstall.py
|
||||
|
||||
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 ...
|
||||
|
||||
At install time, this script will
|
||||
|
||||
1. be extracted into a temporary working directory
|
||||
2. be imported as a module, in the same process as the installer
|
||||
script
|
||||
|
||||
Importing the module should not trigger any side-effects.
|
||||
|
||||
At the appropriate time during the install (a chrooted invocation
|
||||
of the installer Python script) will
|
||||
|
||||
1. scrape the top-level plugin's namespace for subclasses of
|
||||
onl.install.Plugin.Plugin.
|
||||
Implementors should declare classes here
|
||||
(inheriting from onl.install.Plugin.Plugin) to embed the plugin
|
||||
functionality.
|
||||
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)
|
||||
4. invoke the 'shutdown' method (by default, a no-op)
|
||||
|
||||
The 'run' method should return zero on success. In any other case, the
|
||||
installer terminates.
|
||||
|
||||
The 'installer' object has a handle onto the installer ZIP archive
|
||||
(self.installer.zf) but otherwise the install has not been
|
||||
started. That is, the install disk has not been
|
||||
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.
|
||||
|
||||
"""
|
||||
|
||||
import onl.install.Plugin
|
||||
|
||||
class Plugin(onl.install.Plugin.Plugin):
|
||||
|
||||
def run(self):
|
||||
self.log.info("hello from preinstall plugin")
|
||||
return 0
|
||||
38
builds/any/installer/sample-preinstall.sh
Normal file
38
builds/any/installer/sample-preinstall.sh
Normal file
@@ -0,0 +1,38 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
######################################################################
|
||||
#
|
||||
# sample-preinstall.sh
|
||||
#
|
||||
# Example script for pre-install hooks.
|
||||
#
|
||||
# Add this as a preinstall hook to your installer via
|
||||
# the 'mkinstaller.py' command line:
|
||||
#
|
||||
# $ mkinstaller.py ... --preinstall-script sample-preinstall.sh ...
|
||||
#
|
||||
# At install time, this script will
|
||||
#
|
||||
# 1. be extracted into the working directory with the other installer
|
||||
# collateral
|
||||
# 2. have the execute bit set
|
||||
# 3. run in-place with the installer chroot directory passed
|
||||
# as the first command line parameter
|
||||
#
|
||||
# If the script fails (returns a non-zero exit code) then
|
||||
# the install is aborted.
|
||||
#
|
||||
# This script is executed using the ONIE runtime (outside the chroot),
|
||||
# before the actual installer (chrooted Python script)
|
||||
#
|
||||
# At the time the script is run, the installer environment (chroot)
|
||||
# has been fully prepared, including filesystem mount-points.
|
||||
#
|
||||
######################################################################
|
||||
|
||||
rootdir=$1; shift
|
||||
|
||||
echo "Hello from preinstall"
|
||||
echo "Chroot is $rootdir"
|
||||
|
||||
exit 0
|
||||
@@ -10,8 +10,20 @@ endif
|
||||
include $(ONL)/make/versions/version-onl.mk
|
||||
INSTALLER_NAME=$(FNAME_PRODUCT_VERSION)_ONL-OS_$(FNAME_BUILD_ID)_$(UARCH)_$(BOOTMODE)_INSTALLER
|
||||
|
||||
MKINSTALLER_OPTS = \
|
||||
--arch $(ARCH) \
|
||||
--boot-config boot-config \
|
||||
--add-dir config \
|
||||
--fit onl-loader-fit:$(ARCH) onl-loader-fit.itb \
|
||||
--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 \
|
||||
# THIS LINE INTENTIONALLY LEFT BLANK
|
||||
|
||||
__installer:
|
||||
$(ONL)/tools/mkinstaller.py --arch $(ARCH) --boot-config boot-config --add-dir config --fit onl-loader-fit:$(ARCH) onl-loader-fit.itb --swi onl-swi:$(ARCH) --out $(INSTALLER_NAME)
|
||||
$(ONL)/tools/mkinstaller.py $(MKINSTALLER_OPTS) --out $(INSTALLER_NAME)
|
||||
md5sum "$(INSTALLER_NAME)" | awk '{ print $$1 }' > "$(INSTALLER_NAME).md5sum"
|
||||
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Base classes for installers.
|
||||
"""
|
||||
|
||||
import os, stat
|
||||
import os, sys, stat
|
||||
import subprocess
|
||||
import re
|
||||
import tempfile
|
||||
@@ -13,10 +13,12 @@ import parted
|
||||
import yaml
|
||||
import zipfile
|
||||
import shutil
|
||||
import imp
|
||||
|
||||
from InstallUtils import SubprocessMixin
|
||||
from InstallUtils import MountContext, BlkidParser, PartedParser
|
||||
from InstallUtils import ProcMountsParser
|
||||
from Plugin import Plugin
|
||||
|
||||
import onl.YamlUtils
|
||||
from onl.sysconfig import sysconfig
|
||||
@@ -413,6 +415,50 @@ class Base:
|
||||
|
||||
return 0
|
||||
|
||||
def preinstall(self):
|
||||
return self.runPlugin("preinstall.py")
|
||||
|
||||
def postinstall(self):
|
||||
return self.runPlugin("postinstall.py")
|
||||
|
||||
def runPluginFile(self, 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)
|
||||
plugin = klass(self)
|
||||
try:
|
||||
code = plugin.run()
|
||||
except:
|
||||
self.log.exception("plugin failed")
|
||||
code = 1
|
||||
plugin.shutdown()
|
||||
if code: return code
|
||||
|
||||
return 0
|
||||
|
||||
def runPlugin(self, basename):
|
||||
|
||||
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():
|
||||
try:
|
||||
src = None
|
||||
with self.zf.open(basename, "r") as rfd:
|
||||
wfno, src = tempfile.mkstemp(prefix="plugin-",
|
||||
suffix=".py")
|
||||
with os.fdopen(wfno, "w") as wfd:
|
||||
shutil.copyfileobj(rfd, wfd)
|
||||
return self.runPluginFile(src)
|
||||
finally:
|
||||
if src and os.path.exists(src):
|
||||
os.unlink(src)
|
||||
|
||||
GRUB_TPL = """\
|
||||
serial %(serial)s
|
||||
terminal_input serial
|
||||
@@ -603,6 +649,14 @@ class GrubInstaller(SubprocessMixin, Base):
|
||||
|
||||
def installGpt(self):
|
||||
|
||||
# get a handle to the installer zip
|
||||
p = os.path.join(self.im.installerConf.installer_dir,
|
||||
self.im.installerConf.installer_zip)
|
||||
self.zf = zipfile.ZipFile(p)
|
||||
|
||||
code = self.preinstall()
|
||||
if code: return code
|
||||
|
||||
code = self.findGpt()
|
||||
if code: return code
|
||||
|
||||
@@ -640,11 +694,6 @@ class GrubInstaller(SubprocessMixin, Base):
|
||||
self.im.grubEnv.__dict__['bootPart'] = dev.device
|
||||
self.im.grubEnv.__dict__['bootDir'] = None
|
||||
|
||||
# get a handle to the installer zip
|
||||
p = os.path.join(self.im.installerConf.installer_dir,
|
||||
self.im.installerConf.installer_zip)
|
||||
self.zf = zipfile.ZipFile(p)
|
||||
|
||||
code = self.installSwi()
|
||||
if code: return code
|
||||
|
||||
@@ -663,6 +712,9 @@ class GrubInstaller(SubprocessMixin, Base):
|
||||
code = self.installGrub()
|
||||
if code: return code
|
||||
|
||||
code = self.postinstall()
|
||||
if code: return code
|
||||
|
||||
self.log.info("ONL loader install successful.")
|
||||
self.log.info("GRUB installation is required next.")
|
||||
|
||||
@@ -839,6 +891,14 @@ class UbootInstaller(SubprocessMixin, Base):
|
||||
self.log.error("not a block device: %s", self.device)
|
||||
return 1
|
||||
|
||||
# get a handle to the installer zip
|
||||
p = os.path.join(self.im.installerConf.installer_dir,
|
||||
self.im.installerConf.installer_zip)
|
||||
self.zf = zipfile.ZipFile(p)
|
||||
|
||||
code = self.preinstall()
|
||||
if code: return code
|
||||
|
||||
code = self.assertUnmounted()
|
||||
if code: return code
|
||||
|
||||
@@ -884,11 +944,6 @@ class UbootInstaller(SubprocessMixin, Base):
|
||||
self.rawLoaderDevice = self.device + str(partIdx+1)
|
||||
break
|
||||
|
||||
# get a handle to the installer zip
|
||||
p = os.path.join(self.im.installerConf.installer_dir,
|
||||
self.im.installerConf.installer_zip)
|
||||
self.zf = zipfile.ZipFile(p)
|
||||
|
||||
code = self.installSwi()
|
||||
if code: return code
|
||||
|
||||
@@ -913,6 +968,9 @@ class UbootInstaller(SubprocessMixin, Base):
|
||||
code = self.installUbootEnv()
|
||||
if code: return code
|
||||
|
||||
code = self.postinstall()
|
||||
if code: return code
|
||||
|
||||
return 0
|
||||
|
||||
def run(self):
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
"""Plugin.py
|
||||
|
||||
Base class for installer plugins.
|
||||
"""
|
||||
|
||||
class Plugin(object):
|
||||
|
||||
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 shutdown(self):
|
||||
pass
|
||||
@@ -106,6 +106,18 @@ class InstallerShar(object):
|
||||
self.files.append(filename)
|
||||
self.files = list(set(self.files))
|
||||
|
||||
def add_file_as(self, source, basename):
|
||||
if not os.path.exists(source):
|
||||
self.abort("File %s does not exist." % source)
|
||||
|
||||
tmpdir = os.path.join(self.work_dir, "tmp")
|
||||
if not os.path.exists(tmpdir):
|
||||
os.mkdir(tmpdir)
|
||||
|
||||
dst = os.path.join(tmpdir, basename)
|
||||
shutil.copy(source, dst)
|
||||
self.add_file(dst)
|
||||
|
||||
def add_dir(self, dir_):
|
||||
if not os.path.isdir(dir_):
|
||||
self.abort("Directory %s does not exist." % dir_)
|
||||
@@ -174,6 +186,16 @@ if __name__ == '__main__':
|
||||
ap.add_argument("--verbose", '-v', help="Verbose output.", action='store_true')
|
||||
ap.add_argument("--out", help="Destination Filename")
|
||||
|
||||
ap.add_argument("--preinstall-script",
|
||||
help="Specify a preinstall script (runs before installer)")
|
||||
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)")
|
||||
|
||||
ops = ap.parse_args()
|
||||
installer = InstallerShar(ops.arch, ops.work_dir)
|
||||
|
||||
@@ -209,6 +231,20 @@ if __name__ == '__main__':
|
||||
if ops.swi:
|
||||
installer.add_swi(ops.swi)
|
||||
|
||||
hookdir = os.path.join(installer.work_dir, "tmp")
|
||||
if not os.path.exists(hookdir):
|
||||
os.makedirs(hookdir)
|
||||
|
||||
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")
|
||||
|
||||
iname = os.path.abspath(ops.out)
|
||||
installer.build(iname)
|
||||
logger.info("installer: %s" % iname)
|
||||
|
||||
Reference in New Issue
Block a user