Merge pull request #121 from carlroth/master

Implement system upgrade for in-place re-partitioning
This commit is contained in:
Jeffrey Townsend
2016-11-09 09:16:48 -08:00
committed by GitHub
18 changed files with 615 additions and 66 deletions

View File

@@ -284,7 +284,7 @@ class Runner(onl.install.InstallUtils.SubprocessMixin):
l = [x for x in os.listdir(d) if x.endswith('.swi')]
l = [os.path.join(d, x) for x in l]
l.sort(key=swiSortKey)
return l[-1]
return l[-1] if l else None
pm = ProcMountsParser()
parts = [x for x in pm.mounts if x.device == dev]
@@ -294,6 +294,9 @@ class Runner(onl.install.InstallUtils.SubprocessMixin):
self.log.info("found 'latest' swi %s", dst)
else:
dst = os.path.join(parts[0].dir, src)
if dst is None:
self.log.error("missing SWI")
return None
if not os.path.exists(dst):
self.log.error("missing SWI: %s", dst)
return None

View File

@@ -1,6 +1,6 @@
#!/usr/bin/python
"""Run native ONIE tools
"""Run native (on-disk) loader tools
"""
import onl.install.ShellApp

View File

@@ -0,0 +1,7 @@
#!/usr/bin/python
"""Run the upgrade image
"""
import onl.install.ShellApp
onl.install.ShellApp.Upgrader.main()

View File

@@ -0,0 +1 @@
/sbin/onl-upgrade-system

View File

@@ -22,6 +22,9 @@ upgrade:
package:
dir: /lib/platform-config/current/onl/upgrade/onie
system:
auto: advisory
firmware:
auto: advisory
package:

View File

@@ -62,17 +62,26 @@ installer_mkchroot() {
local rootdir
rootdir=$1
local hasDevTmpfs
if grep -q devtmpfs /proc/filesystems; then
hasDevTmpfs=1
fi
# special handling for /dev, which usually already has nested mounts
installer_say "Setting up /dev"
rm -fr "${rootdir}/dev"/*
for dev in /dev/*; do
if test -d "$dev"; then
mkdir "${rootdir}${dev}"
else
cp -a "$dev" "${rootdir}${dev}"
fi
done
mkdir -p "${rootdir}/dev/pts"
if test "$hasDevTmpfs"; then
:
else
for dev in /dev/*; do
if test -d "$dev"; then
mkdir "${rootdir}${dev}"
else
cp -a "$dev" "${rootdir}${dev}"
fi
done
mkdir -p "${rootdir}/dev/pts"
fi
installer_say "Setting up /run"
rm -fr "${rootdir}/run"/*
@@ -99,6 +108,10 @@ installer_mkchroot() {
installer_say "Setting up mounts"
mount -t proc proc "${rootdir}/proc"
mount -t sysfs sysfs "${rootdir}/sys"
if test "$hasDevTmpfs"; then
mount -t devtmpfs devtmpfs "${rootdir}/dev"
mkdir -p ${rootdir}/dev/pts
fi
mount -t devpts devpts "${rootdir}/dev/pts"
if test ${TMPDIR+set}; then

View File

@@ -17,9 +17,32 @@ import time
from InstallUtils import InitrdContext
from InstallUtils import SubprocessMixin
from InstallUtils import ProcMountsParser
from ShellApp import Onie
import ConfUtils, BaseInstall
class App(SubprocessMixin):
class OnieHelper(Onie):
"""Unpack the initrd, but keep it around."""
UMOUNT = False
# leave self.onieDir mounted
ictx = None
def _runInitrdShell(self, initrd):
with InitrdContext(initrd, log=self.log) as self.ictx:
self.initrdDir = self.ictx.dir
self.ictx.detach()
def shutdown(self):
self.ictx.attach()
self.ictx.shutdown()
if self.dctx is not None:
self.dctx.attach()
self.dctx.shutdown()
class App(SubprocessMixin, object):
def __init__(self, url=None,
debug=False, force=False,
@@ -43,6 +66,8 @@ class App(SubprocessMixin):
self.nextUpdate = None
self.onieHelper = None
def run(self):
if self.url is not None:
@@ -123,27 +148,27 @@ class App(SubprocessMixin):
self.log.info("please reboot this system now.")
return 0
def runLocal(self):
def runLocalOrChroot(self):
self.log.info("getting installer configuration")
if os.path.exists(ConfUtils.MachineConf.PATH):
self.machineConf = ConfUtils.MachineConf()
else:
self.log.warn("missing /etc/machine.conf from ONIE runtime")
self.machineConf = ConfUtils.MachineConf(path='/dev/null')
self.installerConf = ConfUtils.InstallerConf()
if self.machineConf is None:
self.log.error("missing machine.conf")
return 1
if self.installerConf is None:
self.log.error("missing installer.conf")
return 1
##self.log.info("using native GRUB")
##self.grubEnv = ConfUtils.GrubEnv(log=self.log.getChild("grub"))
pat = "/mnt/onie-boot/onie/initrd.img*"
l = glob.glob(pat)
if l:
initrd = l[0]
self.log.info("using native ONIE initrd+chroot GRUB (%s)", initrd)
initrdDir = InitrdContext.mkChroot(initrd, log=self.log)
self.grubEnv = ConfUtils.ChrootGrubEnv(initrdDir,
bootDir="/mnt/onie-boot",
self.onieHelper = OnieHelper(log=self.log)
code = self.onieHelper.run()
if code:
self.log.warn("cannot find ONIE initrd")
if self.onieHelper.onieDir is not None:
self.log.info("using native ONIE initrd+chroot GRUB (%s)", self.onieHelper.onieDir)
self.grubEnv = ConfUtils.ChrootGrubEnv(self.onieHelper.initrdDir,
bootDir=self.onieHelper.onieDir,
path="/grub/grubenv",
log=self.log.getChild("grub"))
# direct access using ONIE initrd as a chroot
@@ -216,6 +241,19 @@ class App(SubprocessMixin):
self.log.info("Install finished.")
return 0
def runLocal(self):
self.log.info("getting installer configuration")
if os.path.exists(ConfUtils.MachineConf.PATH):
self.machineConf = ConfUtils.MachineConf()
else:
self.log.warn("missing /etc/machine.conf from ONIE runtime")
self.machineConf = ConfUtils.MachineConf(path='/dev/null')
self.installerConf = ConfUtils.InstallerConf()
return self.runLocalOrChroot()
def findPlatform(self):
plat = arch = None
@@ -302,6 +340,10 @@ class App(SubprocessMixin):
if installer is not None:
installer.shutdown()
h, self.onieHelper = self.onieHelper, None
if h is not None:
h.shutdown()
def post_mortem(self):
self.log.info("re-attaching to tty")
fdno = os.open("/dev/console", os.O_RDWR)

View File

@@ -390,7 +390,7 @@ class Base:
for m in pm.mounts:
if m.device.startswith(self.device):
if not self.force:
self.log.error("mount %s on %s will be erased by install",
self.log.error("mount %s on %s will be erased by install (try --force)",
m.dir, m.device)
return 1
else:
@@ -676,9 +676,6 @@ class UbootInstaller(SubprocessMixin, Base):
self.device = self.im.getDevice()
code = self.assertUnmounted()
if code: return code
self.rawLoaderDevice = None
# set to a partition device for raw loader install,
# default to None for FS-based install
@@ -797,6 +794,9 @@ class UbootInstaller(SubprocessMixin, Base):
self.log.error("not a block device: %s", self.device)
return 1
code = self.assertUnmounted()
if code: return code
code = self.maybeCreateLabel()
if code: return code

View File

@@ -7,6 +7,7 @@ import os
import logging
import subprocess
from InstallUtils import SubprocessMixin, ChrootSubprocessMixin, MountContext
from cStringIO import StringIO
class ConfBase:
@@ -45,6 +46,14 @@ class ConfBase:
def __setattr__(self, attr, val):
self.__dict__['_data'][attr] = val
def dumps(self):
"""Generate a serialized representation."""
buf = StringIO()
data = self.__dict__.get('_data', {})
for key, val in data.iteritems():
buf.write("%s=\"%s\"\n" % (key, val,))
return buf.getvalue()
class ConfFileBase(ConfBase):
PATH = None

View File

@@ -177,6 +177,25 @@ class SubprocessMixin:
# don't believe it
self.check_call(cmd, vmode=self.V1)
def cpR(self, srcRoot, dstRoot):
srcRoot = os.path.abspath(srcRoot)
dstRoot = os.path.abspath(dstRoot)
dstRoot = os.path.join(dstRoot, os.path.split(srcRoot)[1])
for r, dl, fl in os.walk(srcRoot):
for de in dl:
src = os.path.join(r, de)
subdir = src[len(srcRoot)+1:]
dst = os.path.join(dstRoot, subdir)
if not os.path.exists(dst):
self.makedirs(dst)
for fe in fl:
src = os.path.join(r, fe)
subdir = src[len(srcRoot)+1:]
dst = os.path.join(dstRoot, subdir)
self.copy2(src, dst)
class TempdirContext(SubprocessMixin):
def __init__(self, prefix=None, suffix=None, chroot=None, log=None):
@@ -219,6 +238,9 @@ class MountContext(SubprocessMixin):
if not self.device and not self.label:
raise ValueError("no device or label specified")
self._detachMounted = False
self._detachHostDir = None
def __enter__(self):
dev = self.device
if dev is None:
@@ -245,7 +267,7 @@ class MountContext(SubprocessMixin):
self.mounted = True
return self
def __exit__(self, type, value, tb):
def shutdown(self):
mounted = False
if self.mounted:
@@ -263,8 +285,18 @@ class MountContext(SubprocessMixin):
if self.hostDir is not None:
self.rmdir(self.hostDir)
def __exit__(self, type, value, tb):
self.shutdown()
return False
def detach(self):
self.mounted, self._detachMounted = False, self.mounted
self.hostDir, self._detachHostdir = None, self.hostDir
def attach(self):
self.mounted = self._detachMounted
self.hostDir = self._detachHostdir
class BlkidEntry:
def __init__(self, device, **kwargs):
@@ -665,6 +697,9 @@ class InitrdContext(SubprocessMixin):
self.ilog.setLevel(logging.INFO)
self.log = self.hlog
self._hasDevTmpfs = False
self._detachInitrd = None
def _unpack(self):
self.dir = self.mkdtemp(prefix="chroot-",
suffix=".d")
@@ -724,27 +759,28 @@ class InitrdContext(SubprocessMixin):
else:
self.unlink(dst)
for e in os.listdir("/dev"):
src = os.path.join("/dev", e)
dst = os.path.join(dev2, e)
if os.path.islink(src):
self.symlink(os.readlink(src), dst)
elif os.path.isdir(src):
self.mkdir(dst)
elif os.path.isfile(src):
self.copy2(src, dst)
else:
st = os.stat(src)
if stat.S_ISBLK(st.st_mode):
maj, min = os.major(st.st_rdev), os.minor(st.st_rdev)
self.log.debug("+ mknod %s b %d %d", dst, maj, min)
os.mknod(dst, st.st_mode, st.st_rdev)
elif stat.S_ISCHR(st.st_mode):
maj, min = os.major(st.st_rdev), os.minor(st.st_rdev)
self.log.debug("+ mknod %s c %d %d", dst, maj, min)
os.mknod(dst, st.st_mode, st.st_rdev)
if not self._hasDevTmpfs:
for e in os.listdir("/dev"):
src = os.path.join("/dev", e)
dst = os.path.join(dev2, e)
if os.path.islink(src):
self.symlink(os.readlink(src), dst)
elif os.path.isdir(src):
self.mkdir(dst)
elif os.path.isfile(src):
self.copy2(src, dst)
else:
self.log.debug("skipping device %s", src)
st = os.stat(src)
if stat.S_ISBLK(st.st_mode):
maj, min = os.major(st.st_rdev), os.minor(st.st_rdev)
self.log.debug("+ mknod %s b %d %d", dst, maj, min)
os.mknod(dst, st.st_mode, st.st_rdev)
elif stat.S_ISCHR(st.st_mode):
maj, min = os.major(st.st_rdev), os.minor(st.st_rdev)
self.log.debug("+ mknod %s c %d %d", dst, maj, min)
os.mknod(dst, st.st_mode, st.st_rdev)
else:
self.log.debug("skipping device %s", src)
dst = os.path.join(self.dir, "dev/pts")
if not os.path.exists(dst):
@@ -757,6 +793,11 @@ class InitrdContext(SubprocessMixin):
def __enter__(self):
with open("/proc/filesystems") as fd:
buf = fd.read()
if "devtmpfs" in buf:
self._hasDevTmpfs = True
if self.initrd is not None:
self.log.debug("extracting initrd %s", self.initrd)
@@ -777,13 +818,23 @@ class InitrdContext(SubprocessMixin):
cmd = ('mount', '-t', 'sysfs', 'sysfs', dst,)
self.check_call(cmd, vmode=self.V1)
# maybe mount devtmpfs
if self._hasDevTmpfs:
dst = os.path.join(self.dir, "dev")
cmd = ('mount', '-t', 'devtmpfs', 'devtmpfs', dst,)
self.check_call(cmd, vmode=self.V1)
dst = os.path.join(self.dir, "dev/pts")
if not os.path.exists(dst):
self.mkdir(dst)
dst = os.path.join(self.dir, "dev/pts")
cmd = ('mount', '-t', 'devpts', 'devpts', dst,)
self.check_call(cmd, vmode=self.V1)
return self
def __exit__(self, type, value, tb):
def shutdown(self):
p = ProcMountsParser()
dirs = [e.dir for e in p.mounts if e.dir.startswith(self.dir)]
@@ -803,13 +854,21 @@ class InitrdContext(SubprocessMixin):
else:
self.log.debug("saving chroot in %s", self.dir)
def __exit__(self, type, value, tb):
self.shutdown()
return False
def detach(self):
self.initrd, self._detachInitrd = None, self.initrd
def attach(self):
self.initrd = self._detachInitrd
@classmethod
def mkChroot(self, initrd, log=None):
with InitrdContext(initrd=initrd, log=log) as ctx:
def mkChroot(cls, initrd, log=None):
with cls(initrd=initrd, log=log) as ctx:
initrdDir = ctx.dir
ctx.initrd = None
ctx.detach()
# save the unpacked directory, do not clean it up
# (it's inside this chroot anyway)
return initrdDir

View File

@@ -16,8 +16,9 @@ from InstallUtils import BlkidParser
import Fit
import onl.platform.current
from onl.sysconfig import sysconfig
class AppBase(SubprocessMixin):
class AppBase(SubprocessMixin, object):
@property
def PROG(self):
@@ -109,14 +110,20 @@ class AppBase(SubprocessMixin):
sys.exit(code)
class Onie(AppBase):
"""Application shell that uses the ONIE runtime."""
PROG = "onie-shell"
UNMOUNT = True
# default, unmount directories once the initrd is extracted
def run(self):
self.pm = ProcMountsParser()
self.blkid = BlkidParser(log=self.log.getChild("blkid"))
self.mtd = ProcMtdParser(log=self.log.getChild("mtd"))
self.dctx = None
self.onieDir = None
def _g(d):
pat = os.path.join(d, "onie/initrd.img*")
@@ -134,22 +141,28 @@ class Onie(AppBase):
parts = [p for p in self.pm.mounts if p.device == dev]
if parts:
onieDir = parts[0]
self.log.debug("found ONIE boot mounted at %s", onieDir)
initrd = _g(onieDir)
self.log.debug("found ONIE boot mounted at %s", parts[0].dir)
initrd = _g(parts[0].dir)
if initrd is None:
self.log.warn("cannot find ONIE initrd on %s", onieDir)
self.log.warn("cannot find ONIE initrd on %s", parts[0].dir)
else:
self.onieDir = parts[0].dir
self.log.debug("found ONIE initrd at %s", initrd)
return _runInitrdShell(initrd)
return self._runInitrdShell(initrd)
with MountContext(dev, log=self.log) as ctx:
initrd = _g(ctx.dir)
# else, try to mount the directory containing the initrd
with MountContext(dev, log=self.log) as self.dctx:
initrd = _g(self.dctx.dir)
if initrd is None:
self.log.warn("cannot find ONIE initrd on %s", dev)
else:
self.log.debug("found ONIE initrd at %s", initrd)
return self._runInitrdShell(initrd)
try:
return self._runInitrdShell(initrd)
finally:
if not self.UMOUNT:
self.onieDir = self.dctx.hostDir
self.dctx.detach()
self.log.warn("cannot find an ONIE initrd")
return 1
@@ -163,6 +176,7 @@ class Onie(AppBase):
self.log.debug("cannot find ONIE initrd on %s (%s)",
part.device, part.dir)
else:
self.onieDir = part.dir
self.log.debug("found ONIE initrd at %s", initrd)
return self._runInitrdShell(initrd)
@@ -181,6 +195,7 @@ class Onie(AppBase):
return 1
class Loader(AppBase):
"""Application shell that uses the (installed) loader runtime."""
PROG = "loader-shell"
@@ -297,6 +312,49 @@ class Loader(AppBase):
self.log.error("invalid platform-config")
return 1
class Upgrader(AppBase):
"""Application shell that uses on-disk upgrade loader runtime."""
PROG = "upgrade-shell"
def runGrub(self):
d = sysconfig.upgrade.loader.package.dir
for b in sysconfig.upgrade.loader.package.grub:
p = os.path.join(d, b)
if os.path.exists(p):
self.log.debug("found upgrade initrd at %s", p)
return self._runInitrdShell(p)
self.log.error("cannot find upgrade initrd")
return 1
def runUboot(self):
d = sysconfig.upgrade.loader.package.dir
for b in sysconfig.upgrade.loader.package.fit:
p = os.path.join(d, b)
if os.path.exists(p):
self.log.debug("found upgrade FIT image %s", p)
return self._runFitShell(p)
self.log.error("cannot find FIT image")
return 1
def run(self):
self.platform = onl.platform.current.OnlPlatform()
self.pc = self.platform.platform_config
if 'grub' in self.pc:
return self.runGrub()
if 'flat_image_tree' in self.pc:
return self.runUboot()
self.log.error("invalid platform-config")
return 1
main = Onie.main
if __name__ == "__main__":

View File

@@ -0,0 +1,267 @@
"""App.py
Application code for onl-install.
"""
import logging
import os, sys
import json
import tempfile
import zipfile
import shutil
import argparse
import fnmatch
import subprocess
from onl.install.InstallUtils import InitrdContext
from onl.install.InstallUtils import ProcMountsParser
from onl.install.ConfUtils import MachineConf, InstallerConf
from onl.install.ShellApp import Onie, Upgrader
from onl.install.InstallUtils import SubprocessMixin
import onl.install.App
from onl.sysconfig import sysconfig
from onl.mounts import OnlMountContextReadWrite
class UpgradeHelper(Upgrader):
def __init__(self, callback=None, log=None):
super(UpgradeHelper, self).__init__(log=log)
self.callback = callback
def _runInitrdShell(self, p):
if self.callback is not None:
self.callback(self, p)
class App(SubprocessMixin):
def __init__(self, force=False, log=None):
if log is not None:
self.log = log
else:
self.log = logging.getLogger(self.__class__.__name__)
self.force = force
self.onieHelper = None
def _runInitrd(self, helper, path):
with InitrdContext(initrd=path, log=self.log) as ctx:
tdir = os.path.join(ctx.dir, "tmp")
abs_idir = tempfile.mkdtemp(dir=tdir,
prefix="installer-", suffix=".d")
chroot_idir = abs_idir[len(ctx.dir):]
self.onieHelper = onl.install.App.OnieHelper(log=self.log)
code = self.onieHelper.run()
if code:
self.log.error("cannot find/unpack ONIE initrd")
return code
self.log.info("onie directory is %s", self.onieHelper.onieDir)
self.log.info("initrd directory is %s", self.onieHelper.initrdDir)
src = os.path.join(self.onieHelper.initrdDir, "etc/machine.conf")
dst = os.path.join(ctx.dir, "etc/machine.conf")
self.log.debug("+ /bin/cp %s %s", src, dst)
shutil.copy2(src, dst)
h, self.onieHelper = self.onieHelper, None
if h is not None:
h.shutdown()
src = "/etc/fw_env.config"
if os.path.exists(src):
dst = os.path.join(ctx.dir, "etc/fw_env.config")
self.log.debug("+ /bin/cp %s %s", src, dst)
shutil.copy2(src, dst)
srcRoot = "/etc/onl"
dstRoot = os.path.join(ctx.dir, "etc")
self.cpR(srcRoot, dstRoot)
# constitute an /etc/onl/installer.conf in place
installerConf = InstallerConf(path="/dev/null")
with open("/etc/onl/loader/versions.json") as fd:
data = json.load(fd)
installerConf.onl_version = data['VERSION_ID']
installerConf.installer_dir = chroot_idir
abs_postinst = tempfile.mktemp(dir=abs_idir,
prefix="postinst-", suffix=".sh")
chroot_postinst = abs_postinst[len(ctx.dir):]
installerConf.installer_postinst = chroot_postinst
# make an empty(ish) zip file (local path in installer_dir) for collateral
zipPath = tempfile.mktemp(dir=abs_idir,
prefix="install-", suffix=".zip")
with zipfile.ZipFile(zipPath, "w") as zf:
pass
installerConf.installer_zip = os.path.split(zipPath)[1]
# finalize the local installer.conf
dst = os.path.join(ctx.dir, "etc/onl/installer.conf")
with open(dst, "w") as fd:
fd.write(installerConf.dumps())
# populate installer_dir with the contents of the loader upgrade
# See also Loader_Upgrade_x86_64.do_upgrade
# Here the initrd filename is as per the installer.zip;
# it is renamed on install to the grub directory
sdir = sysconfig.upgrade.loader.package.dir
# get kernels for grub installs:
pats = ["kernel-*",]
for f in os.listdir(sdir):
for pat in pats:
if fnmatch.fnmatch(f, pat):
src = os.path.join(sdir, f)
dst = os.path.join(abs_idir, f)
self.log.debug("+ /bin/cp %s %s", src, dst)
shutil.copy2(src, dst)
try:
l = sysconfig.upgrade.loader.package.grub
except AttributeError:
l = []
for f in l:
src = os.path.join(sdir, f)
if os.path.exists(src):
dst = os.path.join(abs_idir, f)
self.log.debug("+ /bin/cp %s %s", src, dst)
shutil.copy2(src, dst)
# get FIT files from powerpc installs:
try:
l = sysconfig.upgrade.loader.package.fit
except AttributeError:
l = []
for f in l:
src = os.path.join(sdir, f)
if os.path.exists(src):
dst = os.path.join(abs_idir, f)
self.log.debug("+ /bin/cp %s %s", src, dst)
shutil.copy2(src, dst)
with OnlMountContextReadWrite('ONL-BOOT', logger=self.log) as octx:
src = os.path.join(octx.directory, "boot-config")
dst = os.path.join(abs_idir, "boot-config")
self.log.debug("+ /bin/cp %s %s", src, dst)
shutil.copy2(src, dst)
# chroot to the onl-install script
##cmd = ('chroot', ctx.dir,
## '/bin/sh', '-i')
if self.log.level < logging.INFO:
cmd = ('chroot', ctx.dir, "/usr/bin/onl-install", "--verbose", "--force",)
else:
cmd = ('chroot', ctx.dir, "/usr/bin/onl-install", "--force",)
try:
self.check_call(cmd)
except subprocess.CalledProcessError, what:
pass
def run(self):
"""XXX roth -- migrate this to onl.install.App.App
XXX roth -- assume TMPDIR=/tmp.
"""
pm = ProcMountsParser()
# resize /tmp to be large enough for the initrd, see tmpfs
# nonsense in installer.sh.in
tflags = None
tdev = os.stat('/tmp').st_dev
pdir = None
for m in pm.mounts:
if m.fsType in ('ramfs', 'tmpfs',):
dev = os.stat(m.dir).st_dev
if dev == tdev:
self.log.info("found tmpfs/ramfs %s (%s)", dev, m.flags)
pdir = m.dir
tflags = m.flags
# XXX glean this from install.sh.in (installer_tmpfs_kmin)
if pdir is None:
self.check_call(('mount',
'-o', 'size=1048576k',
'-t', 'tmpfs',
'tmpfs', '/tmp',))
else:
self.check_call(('mount',
'-o', 'remount,size=1048576k',
pdir,))
for m in pm.mounts:
if m.dir.startswith('/mnt/onl'):
if not self.force:
self.log.error("directory %s is still mounted (try --force)", m.dir)
return 1
self.log.warn("unmounting %s (--force)", m.dir)
self.check_call(('umount', m.dir,))
upgrader = UpgradeHelper(callback=self._runInitrd, log=self.log)
try:
code = upgrader.run()
except:
self.log.exception("upgrader failed")
code = 1
upgrader.shutdown()
return code
def shutdown(self):
h, self.onieHelper = self.onieHelper, None
if h is not None:
h.shutdown()
@classmethod
def main(cls):
logging.basicConfig()
logger = logging.getLogger("onl-install")
logger.setLevel(logging.DEBUG)
# send to ONIE log
hnd = logging.FileHandler("/dev/console")
logger.addHandler(hnd)
logger.propagate = False
onie_verbose = 'onie_verbose' in os.environ
installer_debug = 'installer_debug' in os.environ
ap = argparse.ArgumentParser()
ap.add_argument('-v', '--verbose', action='store_true',
default=onie_verbose,
help="Enable verbose logging")
ap.add_argument('-D', '--debug', action='store_true',
default=installer_debug,
help="Enable python debugging")
ap.add_argument('-F', '--force', action='store_true',
help="Unmount filesystems before install")
ops = ap.parse_args()
if ops.verbose:
logger.setLevel(logging.DEBUG)
app = cls(force=ops.force,
log=logger)
try:
code = app.run()
except:
logger.exception("runner failed")
code = 1
if ops.debug:
app.post_mortem()
app.shutdown()
sys.exit(code)
main = App.main
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,78 @@
#!/usr/bin/python
############################################################
#
# ONL System Upgrade
#
############################################################
import os
import sys
import fnmatch
from onl.upgrade import ubase
from onl.sysconfig import sysconfig
from onl.mounts import OnlMountManager, OnlMountContextReadOnly, OnlMountContextReadWrite
from onl.install.SystemInstall import App
class SystemUpgrade(ubase.BaseUpgrade):
name="system"
Name="System"
title="System Compatibility Version Check"
atype="A Compatible System"
current_version_key="Current System Compatibility Version"
next_version_key="Next System Compatibility Version"
def auto_upgrade_default(self):
return sysconfig.upgrade.system.auto
def init_versions(self):
#
# Current loader version file.
# If this file doesn't exist then in-place upgrade is not supported.
#
ETC_LOADER_VERSIONS_JSON = sysconfig.upgrade.loader.versions
# Upgrade Loader Version file.
NEXT_LOADER_VERSIONS_JSON = os.path.join(sysconfig.upgrade.loader.package.dir, "manifest.json")
VKEY = "SYSTEM_COMPATIBILITY_VERSION"
self.current_version = self.load_json(ETC_LOADER_VERSIONS_JSON, VKEY, None)
self.next_version = self.load_json(NEXT_LOADER_VERSIONS_JSON,
"version", {}).get(VKEY, None)
def prepare_upgrade(self):
pass
def summarize(self):
self.logger.info("Current System Compatibility Version: %s",
self.current_version)
self.logger.info(" Next System Compatibility Version: %s",
self.next_version)
self.logger.info("")
def upgrade_notes(self):
return """
* One or more reboots will be required to complete this upgrade.
"""
def do_upgrade(self, forced=False):
app = App(force=True, log=self.logger)
try:
code = app.run()
except:
self.logger.exception("upgrade failed")
code = 1
app.shutdown()
if code:
self.abort("System upgrade failed.")
else:
self.logger.info("Upgrade succeeded, rebooting")
self.reboot()
if __name__ == '__main__':
klass = SystemUpgrade
klass().main()

View File

@@ -0,0 +1,7 @@
#!/usr/bin/python
"""Re-install ONL using the ONL installer infrastructure.
"""
import onl.install.SystemInstall
onl.install.SystemInstall.main()

View File

@@ -0,0 +1,3 @@
#!/usr/bin/python
from onl.upgrade.system import SystemUpgrade
SystemUpgrade().main()

View File

@@ -1098,7 +1098,6 @@ if __name__ == '__main__':
ap.add_argument("--repo-package-dir", default=os.environ.get('ONLPM_OPTION_REPO_PACKAGE_DIR', 'packages'))
ap.add_argument("--packagedirs", nargs='+', metavar='PACKAGEDIR')
ap.add_argument("--subdir", default=os.getcwd())
ap.add_argument("--extract", metavar='PACKAGE')
ap.add_argument("--extract-dir", nargs=2, metavar=('PACKAGE', 'DIR'), action='append')
ap.add_argument("--force", action='store_true')
ap.add_argument("--list", action='store_true');